[php-src] PHP-8.6: Merge branch 'PHP-8.5' into PHP-8.6
| From: | Ilia Alshanetsky | Date: | Mon, 05 Oct 2026 11:28:25 +0000 |
| Subject: | [php-src] PHP-8.6: Merge branch 'PHP-8.5' into PHP-8.6 | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-141956@lists.php.net to get a copy of this message | ||
Author: Ilia Alshanetsky (iliaal)
Date: 2026-10-05T07:14:22-04:00
Commit: https://github.com/php/php-src/commit/2290177565d8ded70f8b1c1677f24e1b2d6b5f24
Raw diff: https://github.com/php/php-src/commit/2290177565d8ded70f8b1c1677f24e1b2d6b5f24.diff
Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5:
ext/dom: Restore the XPath context after a reentrant evaluation
Changed paths:
A ext/dom/tests/xpath_reentrant_context.phpt
M NEWS
M ext/dom/xpath.c
Diff:
diff --git a/NEWS b/NEWS
index bc63b84709c2..30869309af87 100644
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,11 @@ PHP NEWS
. Fixed System V shared memory emulation for Windows attaching segments past
the end of their mapping. (Ilia Alshanetsky)
+- DOM:
+ . Fixed DOMXPath and Dom\XPath losing the context node and in-scope
+ namespaces of an evaluation after a php:function() callback evaluated
+ another expression on the same object. (Ilia Alshanetsky)
+
- FFI:
. Fixed crashes with FFI callbacks created from __call() trampolines
and array callables whose object is released. (Ilia Alshanetsky)
diff --git a/ext/dom/tests/xpath_reentrant_context.phpt b/ext/dom/tests/xpath_reentrant_context.phpt
new file mode 100644
index 000000000000..4a307ae1dfc5
--- /dev/null
+++ b/ext/dom/tests/xpath_reentrant_context.phpt
@@ -0,0 +1,54 @@
+--TEST--
+DOMXPath: Reentrant evaluation must restore the context node and namespaces
+--EXTENSIONS--
+dom
+--FILE--
+<?php
+$doc = new DOMDocument();
+$doc->loadXML('<r
xmlns:p="urn:p"><a><x>A</x><p:y/></a><a><x>A2</x></a><b><x>B</x><x>B2</x><x>B3</x></b></r>');
+$xp = new DOMXPath($doc);
+$xp->registerNamespace('php', 'http://php.net/xpath');
+$xp->registerPhpFunctions();
+$b = $doc->documentElement->lastElementChild;
+$GLOBALS['xp'] = $xp;
+$GLOBALS['b'] = $b;
+function callback($node) {
+ $GLOBALS['xp']->query('x[2]', $GLOBALS['b']);
+ return true;
+}
+
+echo "context node:\n";
+var_dump($xp->query('a[php:function("callback", .) and x]',
$doc->documentElement)->length);
+var_dump($xp->evaluate('count(a[php:function("callback", .) and x])',
$doc->documentElement));
+
+echo "in-scope namespace:\n";
+var_dump($xp->query('a[php:function("callback", .) and p:y]',
$doc->documentElement)->length);
+
+echo "position and size:\n";
+var_dump($xp->query('a[php:function("callback", .) and position() = 2]',
$doc->documentElement)->length);
+var_dump($xp->query('a[php:function("callback", .) and last() = 2]',
$doc->documentElement)->length);
+
+echo "after reentry:\n";
+var_dump($xp->query('a[x]', $doc->documentElement)->length);
+
+echo "Dom\\XPath:\n";
+$modern =
Dom\XMLDocument::createFromString('<r><a><x>A</x></a><a><x>A2</x></a><b><x>B</x><x>B2</x><x>B3</x></b></r>');
+$GLOBALS['xp'] = new Dom\XPath($modern);
+$GLOBALS['xp']->registerNamespace('php', 'http://php.net/xpath');
+$GLOBALS['xp']->registerPhpFunctions();
+$GLOBALS['b'] = $modern->documentElement->lastElementChild;
+var_dump($GLOBALS['xp']->query('a[php:function("callback", .) and
x]', $modern->documentElement)->length);
+?>
+--EXPECT--
+context node:
+int(2)
+float(2)
+in-scope namespace:
+int(1)
+position and size:
+int(1)
+int(2)
+after reentry:
+int(2)
+Dom\XPath:
+int(2)
diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c
index cff89553aa81..9f00363f25da 100644
--- a/ext/dom/xpath.c
+++ b/ext/dom/xpath.c
@@ -318,6 +318,10 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern)
RETURN_THROWS();
}
+ xmlNodePtr old_node = ctxp->node;
+ xmlNsPtr *old_namespaces = ctxp->namespaces;
+ int old_nsNr = ctxp->nsNr;
+
ctxp->node = nodep;
php_dom_in_scope_ns in_scope_ns;
@@ -335,12 +339,12 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool
modern)
intern->evaluation_depth++;
xmlXPathObjectPtr xpathobjp = xmlXPathEvalExpression(BAD_CAST expr, ctxp);
intern->evaluation_depth--;
- ctxp->node = NULL;
+ ctxp->node = old_node;
+ ctxp->namespaces = old_namespaces;
+ ctxp->nsNr = old_nsNr;
if (register_node_ns && nodep != NULL) {
php_dom_in_scope_ns_destroy(&in_scope_ns);
- ctxp->namespaces = NULL;
- ctxp->nsNr = 0;
}
if (! xpathobjp) {