From 7974c6287353281aea5803b1fd107d4f9ac435ef Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 22 Feb 2025 13:34:45 +0100 Subject: [PATCH] Fix using Dom\Node with Dom\XPath callbacks This code was introduced when the Dom\Node and DOMNode classes were still aliases, so the type check was never updated. We fix this by checking if the doc pointer follows the spec and pick the right node CE based on that. Closes GH-17888. --- NEWS | 1 + .../modern/xml/return_dom_node_from_xpath.phpt | 17 +++++++++++++++++ ext/dom/xpath_callbacks.c | 6 ++++-- ext/xsl/tests/xslt_non_dom_node.phpt | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 ext/dom/tests/modern/xml/return_dom_node_from_xpath.phpt diff --git a/NEWS b/NEWS index d08748de8aa..052f33c44fa 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,7 @@ PHP NEWS . Fixed bug GH-17802 (\Dom\HTMLDocument querySelector attribute name is case sensitive in HTML). (nielsdos) . Fixed bug GH-17847 (xinclude destroys live node). (nielsdos) + . Fix using Dom\Node with Dom\XPath callbacks. (nielsdos) - GD: . Fixed bug GH-17703 (imagescale with both width and height negative values diff --git a/ext/dom/tests/modern/xml/return_dom_node_from_xpath.phpt b/ext/dom/tests/modern/xml/return_dom_node_from_xpath.phpt new file mode 100644 index 00000000000..4388139c43c --- /dev/null +++ b/ext/dom/tests/modern/xml/return_dom_node_from_xpath.phpt @@ -0,0 +1,17 @@ +--TEST-- +Returning a Dom\Node from Dom\XPath callback +--EXTENSIONS-- +dom +--FILE-- +'); +$xpath = new Dom\XPath($dom); +$xpath->registerPhpFunctionNs('urn:x', 'test', fn() => $dom->createElement('foo')); +$xpath->registerNamespace('x', 'urn:x'); +$test = $xpath->query('x:test()'); +var_dump($test[0]->nodeName); + +?> +--EXPECT-- +string(3) "foo" diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c index 00b5ccb5911..283ccd8f7cc 100644 --- a/ext/dom/xpath_callbacks.c +++ b/ext/dom/xpath_callbacks.c @@ -24,6 +24,7 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "internal_helpers.h" #include static void xpath_callbacks_entry_dtor(zval *zv) @@ -425,7 +426,8 @@ static zend_result php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks *xpat } if (Z_TYPE(callback_retval) != IS_UNDEF) { - if (Z_TYPE(callback_retval) == IS_OBJECT && instanceof_function(Z_OBJCE(callback_retval), dom_node_class_entry)) { + if (Z_TYPE(callback_retval) == IS_OBJECT + && (instanceof_function(Z_OBJCE(callback_retval), dom_get_node_ce(php_dom_follow_spec_node((const xmlNode *) ctxt->context->doc))))) { xmlNode *nodep; dom_object *obj; if (xpath_callbacks->node_list == NULL) { @@ -439,7 +441,7 @@ static zend_result php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks *xpat } else if (Z_TYPE(callback_retval) == IS_FALSE || Z_TYPE(callback_retval) == IS_TRUE) { valuePush(ctxt, xmlXPathNewBoolean(Z_TYPE(callback_retval) == IS_TRUE)); } else if (Z_TYPE(callback_retval) == IS_OBJECT) { - zend_type_error("Only objects that are instances of DOMNode can be converted to an XPath expression"); + zend_type_error("Only objects that are instances of DOM nodes can be converted to an XPath expression"); zval_ptr_dtor(&callback_retval); return FAILURE; } else { diff --git a/ext/xsl/tests/xslt_non_dom_node.phpt b/ext/xsl/tests/xslt_non_dom_node.phpt index 9ead8a67fb5..0fbebc12287 100644 --- a/ext/xsl/tests/xslt_non_dom_node.phpt +++ b/ext/xsl/tests/xslt_non_dom_node.phpt @@ -28,4 +28,4 @@ try { } ?> --EXPECT-- -Only objects that are instances of DOMNode can be converted to an XPath expression +Only objects that are instances of DOM nodes can be converted to an XPath expression