diff --git a/ext/dom/tests/xpath_php_function_removes_argument_node.phpt b/ext/dom/tests/xpath_php_function_removes_argument_node.phpt new file mode 100644 index 000000000000..e6696617be25 --- /dev/null +++ b/ext/dom/tests/xpath_php_function_removes_argument_node.phpt @@ -0,0 +1,33 @@ +--TEST-- +DOMXPath: a php:function callback that removes its argument node must not free it mid-evaluation +--EXTENSIONS-- +dom +--FILE-- +loadXML('1234'); +$xp = new DOMXPath($doc); +$xp->registerNamespace('php', 'http://php.net/xpath'); +$xp->registerPhpFunctions(); + +function cb($nodes) { + foreach ($nodes as $n) { + if ($n->parentNode) { + $n->parentNode->removeChild($n); + } + } + return true; +} + +$res = $xp->query('//a[php:function("cb", .)]'); +foreach ($res as $r) { + var_dump($r->nodeName); +} +echo "done\n"; +?> +--EXPECT-- +string(1) "a" +string(1) "a" +string(1) "a" +string(1) "a" +done diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c index 28a5272a302a..53e8f3443149 100644 --- a/ext/dom/xpath_callbacks.c +++ b/ext/dom/xpath_callbacks.c @@ -381,11 +381,19 @@ static zval *php_dom_xpath_callback_fetch_args(xmlXPathParserContextPtr ctxt, ui return params; } -static void php_dom_xpath_callback_cleanup_args(zval *params, uint32_t param_count) +static void php_dom_xpath_callback_cleanup_args(php_dom_xpath_callbacks *xpath_callbacks, zval *params, uint32_t param_count) { if (params) { for (uint32_t i = 0; i < param_count; i++) { - zval_ptr_dtor(¶ms[i]); + zval *param = ¶ms[i]; + if (Z_TYPE_P(param) == IS_OBJECT || Z_TYPE_P(param) == IS_ARRAY) { + if (xpath_callbacks->node_list == NULL) { + xpath_callbacks->node_list = zend_new_array(0); + } + zend_hash_next_index_insert_new(xpath_callbacks->node_list, param); + } else { + zval_ptr_dtor(param); + } } efree(params); } @@ -483,7 +491,7 @@ PHP_DOM_EXPORT zend_result php_dom_xpath_callbacks_call_php_ns(php_dom_xpath_cal cleanup: xmlXPathFreeObject(obj); - php_dom_xpath_callback_cleanup_args(params, param_count); + php_dom_xpath_callback_cleanup_args(xpath_callbacks, params, param_count); cleanup_no_obj: if (UNEXPECTED(result != SUCCESS)) { /* Push sentinel value */ @@ -511,7 +519,7 @@ PHP_DOM_EXPORT zend_result php_dom_xpath_callbacks_call_custom_ns(php_dom_xpath_ zend_result result = php_dom_xpath_callback_dispatch(xpath_callbacks, ns, ctxt, params, param_count, function_name, function_name_length); - php_dom_xpath_callback_cleanup_args(params, param_count); + php_dom_xpath_callback_cleanup_args(xpath_callbacks, params, param_count); if (UNEXPECTED(result != SUCCESS)) { /* Push sentinel value */ valuePush(ctxt, xmlXPathNewString((const xmlChar *) ""));