From 1527f651e7caa9bf36da40a2e29dd6a745b8021d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 21 Jul 2024 01:21:35 +0200 Subject: [PATCH] Add tidyNode::getNextSibling() and tidyNode::getPreviousSibling() These get the next and previous sibling nodes, respectively. We can already kind of do this by using the $child array, but that's inconvenient when actually walking the tree by only using node instances. Since the class is final, there is no BC break here. --- NEWS | 4 +++ UPGRADING | 3 ++ ext/tidy/tests/sibling_nodes.phpt | 52 +++++++++++++++++++++++++++++++ ext/tidy/tidy.c | 26 +++++++++++++--- ext/tidy/tidy.stub.php | 4 +++ ext/tidy/tidy_arginfo.h | 10 +++++- 6 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 ext/tidy/tests/sibling_nodes.phpt diff --git a/NEWS b/NEWS index 9eb17f3eff915..d4e869636a80d 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,10 @@ PHP NEWS . Fix references in request_parse_body() options array. (nielsdos) . Add RoundingMode enum. (timwolla, saki) +- Tidy: + . Add tidyNode::getNextSibling() and tidyNode::getPreviousSibling(). + (nielsdos) + - XSL: . Fix trampoline leak in xpath callables. (nielsdos) diff --git a/UPGRADING b/UPGRADING index 6e748906059da..be5d621d0455b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -654,6 +654,9 @@ PHP 8.4 UPGRADE NOTES array_any(). RFC: https://wiki.php.net/rfc/array_find +- Tidy: + . Added tidyNode::getNextSibling() and tidyNode::getPreviousSibling(). + - XMLReader: . Added XMLReader::fromStream(), XMLReader::fromUri(), XMLReader::fromString(). RFC: https://wiki.php.net/rfc/xmlreader_writer_streams diff --git a/ext/tidy/tests/sibling_nodes.phpt b/ext/tidy/tests/sibling_nodes.phpt new file mode 100644 index 0000000000000..3bcad13e62085 --- /dev/null +++ b/ext/tidy/tests/sibling_nodes.phpt @@ -0,0 +1,52 @@ +--TEST-- +getPreviousSibling() and getNextSibling() +--EXTENSIONS-- +tidy +--FILE-- + + + +
first
+ +
third
+ + +HTML); +$body = $tidy->body(); + +function format($str) { + if (is_null($str)) return $str; + return trim($str); +} + +foreach ($body->child as $i => $child) { + echo "=== From the perspective of child $i ===\n"; + echo "Previous: "; + var_dump(format($child->getPreviousSibling()?->value)); + echo "Next: "; + var_dump(format($child->getNextSibling()?->value)); +} + +echo "=== html element has only the doctype as sibling ===\n"; +echo "Previous: "; +var_dump(format($tidy->html()->getPreviousSibling()?->value)); +echo "Next: "; +var_dump(format($tidy->html()->getNextSibling()?->value)); + +?> +--EXPECT-- +=== From the perspective of child 0 === +Previous: NULL +Next: string(15) "" +=== From the perspective of child 1 === +Previous: string(16) "
first
" +Next: string(16) "
third
" +=== From the perspective of child 2 === +Previous: string(15) "" +Next: NULL +=== html element has only the doctype as sibling === +Previous: string(15) "" +Next: NULL diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 3a743df396674..df1965a4ed645 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -1603,18 +1603,34 @@ PHP_METHOD(tidyNode, isPhp) /* {{{ Returns the parent node if available or NULL */ PHP_METHOD(tidyNode, getParent) { - TidyNode parent_node; TIDY_FETCH_ONLY_OBJECT; - parent_node = tidyGetParent(obj->node); - if(parent_node) { + TidyNode parent_node = tidyGetParent(obj->node); + if (parent_node) { tidy_create_node_object(return_value, obj->ptdoc, parent_node); - } else { - ZVAL_NULL(return_value); } } /* }}} */ +PHP_METHOD(tidyNode, getPreviousSibling) +{ + TIDY_FETCH_ONLY_OBJECT; + + TidyNode previous_node = tidyGetPrev(obj->node); + if (previous_node) { + tidy_create_node_object(return_value, obj->ptdoc, previous_node); + } +} + +PHP_METHOD(tidyNode, getNextSibling) +{ + TIDY_FETCH_ONLY_OBJECT; + + TidyNode next_node = tidyGetNext(obj->node); + if (next_node) { + tidy_create_node_object(return_value, obj->ptdoc, next_node); + } +} /* {{{ __constructor for tidyNode. */ PHP_METHOD(tidyNode, __construct) diff --git a/ext/tidy/tidy.stub.php b/ext/tidy/tidy.stub.php index e00773f2e1d89..add98c505b114 100644 --- a/ext/tidy/tidy.stub.php +++ b/ext/tidy/tidy.stub.php @@ -1004,4 +1004,8 @@ public function isAsp(): bool {} public function isPhp(): bool {} public function getParent(): ?tidyNode {} + + public function getPreviousSibling(): ?tidyNode {} + + public function getNextSibling(): ?tidyNode {} } diff --git a/ext/tidy/tidy_arginfo.h b/ext/tidy/tidy_arginfo.h index 61aa0687df4f3..99cc28b68099d 100644 --- a/ext/tidy/tidy_arginfo.h +++ b/ext/tidy/tidy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 5efa4f23774fac9610f05d895d8f8c6f481cc5a6 */ + * Stub hash: 0e6561410a63658f76011c1ddcecdd1e68757f0a */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_tidy_parse_string, 0, 1, tidy, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) @@ -183,6 +183,10 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_tidyNode_getParent, 0, 0, tidyNode, 1) ZEND_END_ARG_INFO() +#define arginfo_class_tidyNode_getPreviousSibling arginfo_class_tidyNode_getParent + +#define arginfo_class_tidyNode_getNextSibling arginfo_class_tidyNode_getParent + ZEND_FUNCTION(tidy_parse_string); ZEND_FUNCTION(tidy_get_error_buffer); ZEND_FUNCTION(tidy_get_output); @@ -222,6 +226,8 @@ ZEND_METHOD(tidyNode, isJste); ZEND_METHOD(tidyNode, isAsp); ZEND_METHOD(tidyNode, isPhp); ZEND_METHOD(tidyNode, getParent); +ZEND_METHOD(tidyNode, getPreviousSibling); +ZEND_METHOD(tidyNode, getNextSibling); static const zend_function_entry ext_functions[] = { ZEND_FE(tidy_parse_string, arginfo_tidy_parse_string) @@ -289,6 +295,8 @@ static const zend_function_entry class_tidyNode_methods[] = { ZEND_ME(tidyNode, isAsp, arginfo_class_tidyNode_isAsp, ZEND_ACC_PUBLIC) ZEND_ME(tidyNode, isPhp, arginfo_class_tidyNode_isPhp, ZEND_ACC_PUBLIC) ZEND_ME(tidyNode, getParent, arginfo_class_tidyNode_getParent, ZEND_ACC_PUBLIC) + ZEND_ME(tidyNode, getPreviousSibling, arginfo_class_tidyNode_getPreviousSibling, ZEND_ACC_PUBLIC) + ZEND_ME(tidyNode, getNextSibling, arginfo_class_tidyNode_getNextSibling, ZEND_ACC_PUBLIC) ZEND_FE_END };