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
};