From ad452086d956ded273ede6dfb75f92c640e71e26 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. Closes GH-15047. --- NEWS | 2 ++ 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, 91 insertions(+), 6 deletions(-) create mode 100644 ext/tidy/tests/sibling_nodes.phpt diff --git a/NEWS b/NEWS index fb1fa2b793f..a90c0055ed8 100644 --- a/NEWS +++ b/NEWS @@ -53,6 +53,8 @@ PHP NEWS - Tidy: . Failures in the constructor now throw exceptions rather than emitting warnings and having a broken object. (nielsdos) + . Add tidyNode::getNextSibling() and tidyNode::getPreviousSibling(). + (nielsdos) - XSL: . Fix trampoline leak in xpath callables. (nielsdos) diff --git a/UPGRADING b/UPGRADING index 153abf0ed74..4974182f4d4 100644 --- a/UPGRADING +++ b/UPGRADING @@ -667,6 +667,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 00000000000..3bcad13e620 --- /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 8682c9c298e..c5efe7a5b24 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -1599,18 +1599,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 e00773f2e1d..add98c505b1 100644 --- a/ext/tidy/tidy.stub.php +++ b/ext/tidy/tidy.stub.php @@ -1004,4 +1004,8 @@ final class tidyNode 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 61aa0687df4..99cc28b6809 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 };