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.
This commit is contained in:
Niels Dossche 2024-07-21 01:21:35 +02:00
parent a0a8624346
commit ad452086d9
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
6 changed files with 91 additions and 6 deletions

2
NEWS
View file

@ -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)

View file

@ -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

View file

@ -0,0 +1,52 @@
--TEST--
getPreviousSibling() and getNextSibling()
--EXTENSIONS--
tidy
--FILE--
<?php
$tidy = tidy_parse_string(<<<HTML
<!DOCTYPE html>
<html>
<body>
<div>first</div>
<!-- second -->
<div>third</div>
</body>
</html>
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) "<!-- second -->"
=== From the perspective of child 1 ===
Previous: string(16) "<div>first</div>"
Next: string(16) "<div>third</div>"
=== From the perspective of child 2 ===
Previous: string(15) "<!-- second -->"
Next: NULL
=== html element has only the doctype as sibling ===
Previous: string(15) "<!DOCTYPE html>"
Next: NULL

View file

@ -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)

View file

@ -1004,4 +1004,8 @@ final class tidyNode
public function isPhp(): bool {}
public function getParent(): ?tidyNode {}
public function getPreviousSibling(): ?tidyNode {}
public function getNextSibling(): ?tidyNode {}
}

View file

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