Skip to content

Commit c03d1aa

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: ext/dom: Restore the XPath context after a reentrant evaluation
2 parents 49a29e1 + 523127f commit c03d1aa

3 files changed

Lines changed: 64 additions & 3 deletions

File tree

‎NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ PHP NEWS
4242
(Ilia Alshanetsky)
4343
. Fixed bug GH-23887 (Dom\HTMLCollection::namedItem() assertion failure,
4444
hang, or missed first element). (Lazizbek Ergashev)
45+
. Fixed DOMXPath and Dom\XPath losing the context node and in-scope
46+
namespaces of an evaluation after a php:function() callback evaluated
47+
another expression on the same object. (Ilia Alshanetsky)
4548

4649
- FFI:
4750
. Fixed crashes with FFI callbacks created from __call() trampolines
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
DOMXPath: Reentrant evaluation must restore the context node and namespaces
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
$doc = new DOMDocument();
8+
$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>');
9+
$xp = new DOMXPath($doc);
10+
$xp->registerNamespace('php', 'http://php.net/xpath');
11+
$xp->registerPhpFunctions();
12+
$b = $doc->documentElement->lastElementChild;
13+
$GLOBALS['xp'] = $xp;
14+
$GLOBALS['b'] = $b;
15+
function callback($node) {
16+
$GLOBALS['xp']->query('x[2]', $GLOBALS['b']);
17+
return true;
18+
}
19+
20+
echo "context node:\n";
21+
var_dump($xp->query('a[php:function("callback", .) and x]', $doc->documentElement)->length);
22+
var_dump($xp->evaluate('count(a[php:function("callback", .) and x])', $doc->documentElement));
23+
24+
echo "in-scope namespace:\n";
25+
var_dump($xp->query('a[php:function("callback", .) and p:y]', $doc->documentElement)->length);
26+
27+
echo "position and size:\n";
28+
var_dump($xp->query('a[php:function("callback", .) and position() = 2]', $doc->documentElement)->length);
29+
var_dump($xp->query('a[php:function("callback", .) and last() = 2]', $doc->documentElement)->length);
30+
31+
echo "after reentry:\n";
32+
var_dump($xp->query('a[x]', $doc->documentElement)->length);
33+
34+
echo "Dom\\XPath:\n";
35+
$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>');
36+
$GLOBALS['xp'] = new Dom\XPath($modern);
37+
$GLOBALS['xp']->registerNamespace('php', 'http://php.net/xpath');
38+
$GLOBALS['xp']->registerPhpFunctions();
39+
$GLOBALS['b'] = $modern->documentElement->lastElementChild;
40+
var_dump($GLOBALS['xp']->query('a[php:function("callback", .) and x]', $modern->documentElement)->length);
41+
?>
42+
--EXPECT--
43+
context node:
44+
int(2)
45+
float(2)
46+
in-scope namespace:
47+
int(1)
48+
position and size:
49+
int(1)
50+
int(2)
51+
after reentry:
52+
int(2)
53+
Dom\XPath:
54+
int(2)

‎ext/dom/xpath.c‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,10 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern)
322322
RETURN_THROWS();
323323
}
324324

325+
xmlNodePtr old_node = ctxp->node;
326+
xmlNsPtr *old_namespaces = ctxp->namespaces;
327+
int old_nsNr = ctxp->nsNr;
328+
325329
ctxp->node = nodep;
326330

327331
php_dom_in_scope_ns in_scope_ns;
@@ -339,12 +343,12 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern)
339343
intern->evaluation_depth++;
340344
xmlXPathObjectPtr xpathobjp = xmlXPathEvalExpression(BAD_CAST expr, ctxp);
341345
intern->evaluation_depth--;
342-
ctxp->node = NULL;
346+
ctxp->node = old_node;
347+
ctxp->namespaces = old_namespaces;
348+
ctxp->nsNr = old_nsNr;
343349

344350
if (register_node_ns && nodep != NULL) {
345351
php_dom_in_scope_ns_destroy(&in_scope_ns);
346-
ctxp->namespaces = NULL;
347-
ctxp->nsNr = 0;
348352
}
349353

350354
if (! xpathobjp) {

0 commit comments

Comments
 (0)