1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Christian Stocker <chregu@php.net> |
14 | Rob Richards <rrichards@php.net> |
15 | Niels Dossche <nielsdos@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "php.h"
24 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
25
26 #include "php_dom.h"
27 #include <libxml/parserInternals.h>
28
xpath_callbacks_entry_dtor(zval * zv)29 static void xpath_callbacks_entry_dtor(zval *zv)
30 {
31 zend_fcall_info_cache *fcc = Z_PTR_P(zv);
32 zend_fcc_dtor(fcc);
33 efree(fcc);
34 }
35
php_dom_xpath_callback_ns_ctor(php_dom_xpath_callback_ns * ns)36 PHP_DOM_EXPORT void php_dom_xpath_callback_ns_ctor(php_dom_xpath_callback_ns *ns)
37 {
38 zend_hash_init(&ns->functions, 0, NULL, xpath_callbacks_entry_dtor, false);
39 ns->mode = PHP_DOM_REG_FUNC_MODE_NONE;
40 }
41
php_dom_xpath_callback_ns_dtor(php_dom_xpath_callback_ns * ns)42 PHP_DOM_EXPORT void php_dom_xpath_callback_ns_dtor(php_dom_xpath_callback_ns *ns)
43 {
44 zend_hash_destroy(&ns->functions);
45 }
46
php_dom_xpath_callbacks_ctor(php_dom_xpath_callbacks * registry)47 PHP_DOM_EXPORT void php_dom_xpath_callbacks_ctor(php_dom_xpath_callbacks *registry)
48 {
49 registry->php_ns = NULL;
50 registry->namespaces = NULL;
51 registry->node_list = NULL;
52 }
53
php_dom_xpath_callbacks_clean_node_list(php_dom_xpath_callbacks * registry)54 PHP_DOM_EXPORT void php_dom_xpath_callbacks_clean_node_list(php_dom_xpath_callbacks *registry)
55 {
56 if (registry->node_list) {
57 zend_hash_destroy(registry->node_list);
58 FREE_HASHTABLE(registry->node_list);
59 registry->node_list = NULL;
60 }
61 }
62
php_dom_xpath_callbacks_clean_argument_stack(xmlXPathParserContextPtr ctxt,uint32_t num_args)63 PHP_DOM_EXPORT void php_dom_xpath_callbacks_clean_argument_stack(xmlXPathParserContextPtr ctxt, uint32_t num_args)
64 {
65 for (uint32_t i = 0; i < num_args; i++) {
66 xmlXPathObjectPtr obj = valuePop(ctxt);
67 xmlXPathFreeObject(obj);
68 }
69
70 /* Don't push a sentinel value here. If this is called from an error situation, then by *not* pushing a sentinel
71 * the execution will halt. If this is called from a regular situation, then it is the caller's responsibility
72 * to ensure the stack remains balanced. */
73 }
74
php_dom_xpath_callbacks_dtor(php_dom_xpath_callbacks * registry)75 PHP_DOM_EXPORT void php_dom_xpath_callbacks_dtor(php_dom_xpath_callbacks *registry)
76 {
77 if (registry->php_ns) {
78 php_dom_xpath_callback_ns_dtor(registry->php_ns);
79 efree(registry->php_ns);
80 }
81 if (registry->namespaces) {
82 php_dom_xpath_callback_ns *ns;
83 ZEND_HASH_MAP_FOREACH_PTR(registry->namespaces, ns) {
84 php_dom_xpath_callback_ns_dtor(ns);
85 efree(ns);
86 } ZEND_HASH_FOREACH_END();
87
88 zend_hash_destroy(registry->namespaces);
89 FREE_HASHTABLE(registry->namespaces);
90 }
91 php_dom_xpath_callbacks_clean_node_list(registry);
92 }
93
php_dom_xpath_callback_ns_get_gc(php_dom_xpath_callback_ns * ns,zend_get_gc_buffer * gc_buffer)94 static void php_dom_xpath_callback_ns_get_gc(php_dom_xpath_callback_ns *ns, zend_get_gc_buffer *gc_buffer)
95 {
96 zend_fcall_info_cache *entry;
97 ZEND_HASH_MAP_FOREACH_PTR(&ns->functions, entry) {
98 zend_get_gc_buffer_add_fcc(gc_buffer, entry);
99 } ZEND_HASH_FOREACH_END();
100 }
101
php_dom_xpath_callbacks_get_gc(php_dom_xpath_callbacks * registry,zend_get_gc_buffer * gc_buffer)102 PHP_DOM_EXPORT void php_dom_xpath_callbacks_get_gc(php_dom_xpath_callbacks *registry, zend_get_gc_buffer *gc_buffer)
103 {
104 if (registry->php_ns) {
105 php_dom_xpath_callback_ns_get_gc(registry->php_ns, gc_buffer);
106 }
107 if (registry->namespaces) {
108 php_dom_xpath_callback_ns *ns;
109 ZEND_HASH_MAP_FOREACH_PTR(registry->namespaces, ns) {
110 php_dom_xpath_callback_ns_get_gc(ns, gc_buffer);
111 } ZEND_HASH_FOREACH_END();
112 }
113 }
114
php_dom_xpath_callbacks_get_gc_for_whole_object(php_dom_xpath_callbacks * registry,zend_object * object,zval ** table,int * n)115 PHP_DOM_EXPORT HashTable *php_dom_xpath_callbacks_get_gc_for_whole_object(php_dom_xpath_callbacks *registry, zend_object *object, zval **table, int *n)
116 {
117 if (registry->php_ns || registry->namespaces) {
118 zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
119 php_dom_xpath_callbacks_get_gc(registry, gc_buffer);
120 zend_get_gc_buffer_use(gc_buffer, table, n);
121
122 if (object->properties == NULL && object->ce->default_properties_count == 0) {
123 return NULL;
124 } else {
125 return zend_std_get_properties(object);
126 }
127 } else {
128 return zend_std_get_gc(object, table, n);
129 }
130 }
131
php_dom_xpath_is_callback_name_valid(const zend_string * name,php_dom_xpath_callback_name_validation name_validation)132 static bool php_dom_xpath_is_callback_name_valid(const zend_string *name, php_dom_xpath_callback_name_validation name_validation)
133 {
134 if (ZSTR_LEN(name) == 0) {
135 return false;
136 }
137
138 if (name_validation == PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NULLS || name_validation == PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME) {
139 if (zend_str_has_nul_byte(name)) {
140 return false;
141 }
142 }
143
144 if (name_validation == PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME) {
145 if (xmlValidateNCName(BAD_CAST ZSTR_VAL(name), /* pass 0 to disallow spaces */ 0) != 0) {
146 return false;
147 }
148 }
149
150 return true;
151 }
152
php_dom_xpath_is_callback_name_valid_and_throw(const zend_string * name,php_dom_xpath_callback_name_validation name_validation,bool is_array)153 static bool php_dom_xpath_is_callback_name_valid_and_throw(const zend_string *name, php_dom_xpath_callback_name_validation name_validation, bool is_array)
154 {
155 if (!php_dom_xpath_is_callback_name_valid(name, name_validation)) {
156 if (is_array) {
157 zend_argument_value_error(1, "must be an array containing valid callback names");
158 } else {
159 zend_argument_value_error(2, "must be a valid callback name");
160 }
161 return false;
162 }
163 return true;
164 }
165
php_dom_xpath_callbacks_delayed_lib_registration(const php_dom_xpath_callbacks * registry,void * ctxt,php_dom_xpath_callbacks_register_func_ctx register_func)166 PHP_DOM_EXPORT void php_dom_xpath_callbacks_delayed_lib_registration(const php_dom_xpath_callbacks* registry, void *ctxt, php_dom_xpath_callbacks_register_func_ctx register_func)
167 {
168 if (registry->namespaces) {
169 zend_string *namespace;
170 php_dom_xpath_callback_ns *ns;
171 ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(registry->namespaces, namespace, ns) {
172 zend_string *name;
173 ZEND_HASH_MAP_FOREACH_STR_KEY(&ns->functions, name) {
174 register_func(ctxt, namespace, name);
175 } ZEND_HASH_FOREACH_END();
176 } ZEND_HASH_FOREACH_END();
177 }
178 }
179
php_dom_xpath_callback_ns_update_method_handler(php_dom_xpath_callback_ns * ns,xmlXPathContextPtr ctxt,const zend_string * namespace,zend_string * name,const HashTable * callable_ht,php_dom_xpath_callback_name_validation name_validation,php_dom_xpath_callbacks_register_func_ctx register_func)180 static zend_result php_dom_xpath_callback_ns_update_method_handler(
181 php_dom_xpath_callback_ns* ns,
182 xmlXPathContextPtr ctxt,
183 const zend_string *namespace,
184 zend_string *name,
185 const HashTable *callable_ht,
186 php_dom_xpath_callback_name_validation name_validation,
187 php_dom_xpath_callbacks_register_func_ctx register_func
188 )
189 {
190 zval *entry, registered_value;
191
192 if (callable_ht) {
193 zend_string *key;
194 ZEND_HASH_FOREACH_STR_KEY_VAL(callable_ht, key, entry) {
195 zend_fcall_info_cache* fcc = emalloc(sizeof(zend_fcall_info));
196 char *error;
197 if (!zend_is_callable_ex(entry, NULL, 0, NULL, fcc, &error)) {
198 zend_argument_type_error(1, "must be an array with valid callbacks as values, %s", error);
199 efree(fcc);
200 efree(error);
201 return FAILURE;
202 }
203
204 zend_fcc_addref(fcc);
205 ZVAL_PTR(®istered_value, fcc);
206
207 if (!key) {
208 zend_string *str = zval_try_get_string(entry);
209 if (str && php_dom_xpath_is_callback_name_valid_and_throw(str, name_validation, true)) {
210 zend_hash_update(&ns->functions, str, ®istered_value);
211 if (register_func) {
212 register_func(ctxt, namespace, str);
213 }
214 zend_string_release_ex(str, false);
215 } else {
216 zend_fcc_dtor(fcc);
217 efree(fcc);
218 return FAILURE;
219 }
220 } else {
221 if (!php_dom_xpath_is_callback_name_valid_and_throw(key, name_validation, true)) {
222 zend_fcc_dtor(fcc);
223 efree(fcc);
224 return FAILURE;
225 }
226 zend_hash_update(&ns->functions, key, ®istered_value);
227 if (register_func) {
228 register_func(ctxt, namespace, key);
229 }
230 }
231 } ZEND_HASH_FOREACH_END();
232 ns->mode = PHP_DOM_REG_FUNC_MODE_SET;
233 } else if (name) {
234 if (!php_dom_xpath_is_callback_name_valid(name, name_validation)) {
235 zend_argument_value_error(1, "must be a valid callback name");
236 return FAILURE;
237 }
238 zend_fcall_info_cache* fcc = emalloc(sizeof(zend_fcall_info));
239 char *error;
240 zval tmp;
241 ZVAL_STR(&tmp, name);
242 if (!zend_is_callable_ex(&tmp, NULL, 0, NULL, fcc, &error)) {
243 zend_argument_type_error(1, "must be a callable, %s", error);
244 efree(fcc);
245 efree(error);
246 return FAILURE;
247 }
248 zend_fcc_addref(fcc);
249 ZVAL_PTR(®istered_value, fcc);
250 zend_hash_update(&ns->functions, name, ®istered_value);
251 if (register_func) {
252 register_func(ctxt, namespace, name);
253 }
254 ns->mode = PHP_DOM_REG_FUNC_MODE_SET;
255 } else {
256 ns->mode = PHP_DOM_REG_FUNC_MODE_ALL;
257 }
258
259 return SUCCESS;
260 }
261
php_dom_xpath_callbacks_ensure_ns(php_dom_xpath_callbacks * registry,zend_string * ns)262 static php_dom_xpath_callback_ns *php_dom_xpath_callbacks_ensure_ns(php_dom_xpath_callbacks *registry, zend_string *ns)
263 {
264 if (ns == NULL) {
265 if (!registry->php_ns) {
266 registry->php_ns = emalloc(sizeof(php_dom_xpath_callback_ns));
267 php_dom_xpath_callback_ns_ctor(registry->php_ns);
268 }
269 return registry->php_ns;
270 } else {
271 if (!registry->namespaces) {
272 /* In most cases probably only a single namespace is registered. */
273 registry->namespaces = zend_new_array(1);
274 }
275 php_dom_xpath_callback_ns *namespace = zend_hash_find_ptr(registry->namespaces, ns);
276 if (namespace == NULL) {
277 namespace = emalloc(sizeof(php_dom_xpath_callback_ns));
278 php_dom_xpath_callback_ns_ctor(namespace);
279 zend_hash_add_new_ptr(registry->namespaces, ns, namespace);
280 }
281 return namespace;
282 }
283 }
284
php_dom_xpath_callbacks_update_method_handler(php_dom_xpath_callbacks * registry,xmlXPathContextPtr ctxt,zend_string * ns,zend_string * name,const HashTable * callable_ht,php_dom_xpath_callback_name_validation name_validation,php_dom_xpath_callbacks_register_func_ctx register_func)285 PHP_DOM_EXPORT zend_result php_dom_xpath_callbacks_update_method_handler(php_dom_xpath_callbacks* registry, xmlXPathContextPtr ctxt, zend_string *ns, zend_string *name, const HashTable *callable_ht, php_dom_xpath_callback_name_validation name_validation, php_dom_xpath_callbacks_register_func_ctx register_func)
286 {
287 php_dom_xpath_callback_ns *namespace = php_dom_xpath_callbacks_ensure_ns(registry, ns);
288 return php_dom_xpath_callback_ns_update_method_handler(namespace, ctxt, ns, name, callable_ht, name_validation, register_func);
289 }
290
php_dom_xpath_callbacks_update_single_method_handler(php_dom_xpath_callbacks * registry,xmlXPathContextPtr ctxt,zend_string * ns,zend_string * name,const zend_fcall_info_cache * fcc,php_dom_xpath_callback_name_validation name_validation,php_dom_xpath_callbacks_register_func_ctx register_func)291 PHP_DOM_EXPORT zend_result php_dom_xpath_callbacks_update_single_method_handler(php_dom_xpath_callbacks* registry, xmlXPathContextPtr ctxt, zend_string *ns, zend_string *name, const zend_fcall_info_cache *fcc, php_dom_xpath_callback_name_validation name_validation, php_dom_xpath_callbacks_register_func_ctx register_func)
292 {
293 if (!php_dom_xpath_is_callback_name_valid_and_throw(name, name_validation, false)) {
294 return FAILURE;
295 }
296
297 php_dom_xpath_callback_ns *namespace = php_dom_xpath_callbacks_ensure_ns(registry, ns);
298 zend_fcall_info_cache* allocated_fcc = emalloc(sizeof(zend_fcall_info));
299 zend_fcc_dup(allocated_fcc, fcc);
300
301 zval registered_value;
302 ZVAL_PTR(®istered_value, allocated_fcc);
303
304 zend_hash_update(&namespace->functions, name, ®istered_value);
305 if (register_func) {
306 register_func(ctxt, ns, name);
307 }
308
309 namespace->mode = PHP_DOM_REG_FUNC_MODE_SET;
310
311 return SUCCESS;
312 }
313
php_dom_xpath_callback_fetch_args(xmlXPathParserContextPtr ctxt,uint32_t param_count,php_dom_xpath_nodeset_evaluation_mode evaluation_mode,dom_object * intern,php_dom_xpath_callbacks_proxy_factory proxy_factory)314 static zval *php_dom_xpath_callback_fetch_args(xmlXPathParserContextPtr ctxt, uint32_t param_count, php_dom_xpath_nodeset_evaluation_mode evaluation_mode, dom_object *intern, php_dom_xpath_callbacks_proxy_factory proxy_factory)
315 {
316 if (param_count == 0) {
317 return NULL;
318 }
319
320 zval *params = safe_emalloc(param_count, sizeof(zval), 0);
321
322 /* Reverse order to pop values off ctxt stack */
323 for (zval *param = params + param_count - 1; param >= params; param--) {
324 xmlXPathObjectPtr obj = valuePop(ctxt);
325 ZEND_ASSERT(obj != NULL);
326 switch (obj->type) {
327 case XPATH_STRING:
328 ZVAL_STRING(param, (char *)obj->stringval);
329 break;
330 case XPATH_BOOLEAN:
331 ZVAL_BOOL(param, obj->boolval);
332 break;
333 case XPATH_NUMBER:
334 ZVAL_DOUBLE(param, obj->floatval);
335 break;
336 case XPATH_NODESET:
337 if (evaluation_mode == PHP_DOM_XPATH_EVALUATE_NODESET_TO_STRING) {
338 char *str = (char *)xmlXPathCastToString(obj);
339 ZVAL_STRING(param, str);
340 xmlFree(str);
341 } else if (evaluation_mode == PHP_DOM_XPATH_EVALUATE_NODESET_TO_NODESET) {
342 if (obj->nodesetval && obj->nodesetval->nodeNr > 0) {
343 array_init_size(param, obj->nodesetval->nodeNr);
344 zend_hash_real_init_packed(Z_ARRVAL_P(param));
345 for (int j = 0; j < obj->nodesetval->nodeNr; j++) {
346 xmlNodePtr node = obj->nodesetval->nodeTab[j];
347 zval child;
348 if (UNEXPECTED(node->type == XML_NAMESPACE_DECL)) {
349 xmlNodePtr nsparent = node->_private;
350 xmlNsPtr original = (xmlNsPtr) node;
351
352 /* Make sure parent dom object exists, so we can take an extra reference. */
353 zval parent_zval; /* don't destroy me, my lifetime is transferred to the fake namespace decl */
354 php_dom_create_object(nsparent, &parent_zval, intern);
355 dom_object *parent_intern = Z_DOMOBJ_P(&parent_zval);
356
357 php_dom_create_fake_namespace_decl(nsparent, original, &child, parent_intern);
358 } else {
359 proxy_factory(node, &child, intern, ctxt);
360 }
361 zend_hash_next_index_insert_new(Z_ARRVAL_P(param), &child);
362 }
363 } else {
364 ZVAL_EMPTY_ARRAY(param);
365 }
366 }
367 break;
368 default: {
369 char *str = (char *)xmlXPathCastToString(obj);
370 ZVAL_STRING(param, str);
371 xmlFree(str);
372 break;
373 }
374 }
375 xmlXPathFreeObject(obj);
376 }
377
378 return params;
379 }
380
php_dom_xpath_callback_cleanup_args(zval * params,uint32_t param_count)381 static void php_dom_xpath_callback_cleanup_args(zval *params, uint32_t param_count)
382 {
383 if (params) {
384 for (uint32_t i = 0; i < param_count; i++) {
385 zval_ptr_dtor(¶ms[i]);
386 }
387 efree(params);
388 }
389 }
390
php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks * xpath_callbacks,php_dom_xpath_callback_ns * ns,xmlXPathParserContextPtr ctxt,zval * params,uint32_t param_count,const char * function_name,size_t function_name_length)391 static zend_result php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks *xpath_callbacks, php_dom_xpath_callback_ns *ns, xmlXPathParserContextPtr ctxt, zval *params, uint32_t param_count, const char *function_name, size_t function_name_length)
392 {
393 zval callback_retval;
394
395 if (UNEXPECTED(ns == NULL)) {
396 zend_throw_error(NULL, "No callbacks were registered");
397 return FAILURE;
398 }
399
400 if (ns->mode == PHP_DOM_REG_FUNC_MODE_ALL) {
401 zend_fcall_info fci;
402 fci.size = sizeof(fci);
403 fci.object = NULL;
404 fci.retval = &callback_retval;
405 fci.param_count = param_count;
406 fci.params = params;
407 fci.named_params = NULL;
408 ZVAL_STRINGL(&fci.function_name, function_name, function_name_length);
409
410 zend_call_function(&fci, NULL);
411 zend_string_release_ex(Z_STR(fci.function_name), false);
412 if (UNEXPECTED(EG(exception))) {
413 return FAILURE;
414 }
415 } else {
416 ZEND_ASSERT(ns->mode == PHP_DOM_REG_FUNC_MODE_SET);
417
418 zval *fcc_wrapper = zend_hash_str_find(&ns->functions, function_name, function_name_length);
419 if (fcc_wrapper) {
420 zend_call_known_fcc(Z_PTR_P(fcc_wrapper), &callback_retval, param_count, params, NULL);
421 } else {
422 zend_throw_error(NULL, "No callback handler \"%s\" registered", function_name);
423 return FAILURE;
424 }
425 }
426
427 if (Z_TYPE(callback_retval) != IS_UNDEF) {
428 if (Z_TYPE(callback_retval) == IS_OBJECT && instanceof_function(Z_OBJCE(callback_retval), dom_node_class_entry)) {
429 xmlNode *nodep;
430 dom_object *obj;
431 if (xpath_callbacks->node_list == NULL) {
432 xpath_callbacks->node_list = zend_new_array(0);
433 }
434 Z_ADDREF_P(&callback_retval);
435 zend_hash_next_index_insert_new(xpath_callbacks->node_list, &callback_retval);
436 obj = Z_DOMOBJ_P(&callback_retval);
437 nodep = dom_object_get_node(obj);
438 valuePush(ctxt, xmlXPathNewNodeSet(nodep));
439 } else if (Z_TYPE(callback_retval) == IS_FALSE || Z_TYPE(callback_retval) == IS_TRUE) {
440 valuePush(ctxt, xmlXPathNewBoolean(Z_TYPE(callback_retval) == IS_TRUE));
441 } else if (Z_TYPE(callback_retval) == IS_OBJECT) {
442 zend_type_error("Only objects that are instances of DOMNode can be converted to an XPath expression");
443 zval_ptr_dtor(&callback_retval);
444 return FAILURE;
445 } else {
446 zend_string *str = zval_get_string(&callback_retval);
447 valuePush(ctxt, xmlXPathNewString(BAD_CAST ZSTR_VAL(str)));
448 zend_string_release_ex(str, 0);
449 }
450 zval_ptr_dtor(&callback_retval);
451 }
452
453 return SUCCESS;
454 }
455
php_dom_xpath_callbacks_call_php_ns(php_dom_xpath_callbacks * xpath_callbacks,xmlXPathParserContextPtr ctxt,int num_args,php_dom_xpath_nodeset_evaluation_mode evaluation_mode,dom_object * intern,php_dom_xpath_callbacks_proxy_factory proxy_factory)456 PHP_DOM_EXPORT zend_result php_dom_xpath_callbacks_call_php_ns(php_dom_xpath_callbacks *xpath_callbacks, xmlXPathParserContextPtr ctxt, int num_args, php_dom_xpath_nodeset_evaluation_mode evaluation_mode, dom_object *intern, php_dom_xpath_callbacks_proxy_factory proxy_factory)
457 {
458 zend_result result = FAILURE;
459
460 if (UNEXPECTED(num_args == 0)) {
461 zend_throw_error(NULL, "Function name must be passed as the first argument");
462 goto cleanup_no_obj;
463 }
464
465 uint32_t param_count = num_args - 1;
466 zval *params = php_dom_xpath_callback_fetch_args(ctxt, param_count, evaluation_mode, intern, proxy_factory);
467
468 /* Last element of the stack is the function name */
469 xmlXPathObjectPtr obj = valuePop(ctxt);
470 if (UNEXPECTED(obj->stringval == NULL)) {
471 zend_type_error("Handler name must be a string");
472 goto cleanup;
473 }
474
475 const char *function_name = (const char *) obj->stringval;
476 size_t function_name_length = strlen(function_name);
477
478 result = php_dom_xpath_callback_dispatch(xpath_callbacks, xpath_callbacks->php_ns, ctxt, params, param_count, function_name, function_name_length);
479
480 cleanup:
481 xmlXPathFreeObject(obj);
482 php_dom_xpath_callback_cleanup_args(params, param_count);
483 cleanup_no_obj:
484 if (UNEXPECTED(result != SUCCESS)) {
485 /* Push sentinel value */
486 valuePush(ctxt, xmlXPathNewString((const xmlChar *) ""));
487 }
488
489 return result;
490 }
491
php_dom_xpath_callbacks_call_custom_ns(php_dom_xpath_callbacks * xpath_callbacks,xmlXPathParserContextPtr ctxt,int num_args,php_dom_xpath_nodeset_evaluation_mode evaluation_mode,dom_object * intern,php_dom_xpath_callbacks_proxy_factory proxy_factory)492 PHP_DOM_EXPORT zend_result php_dom_xpath_callbacks_call_custom_ns(php_dom_xpath_callbacks *xpath_callbacks, xmlXPathParserContextPtr ctxt, int num_args, php_dom_xpath_nodeset_evaluation_mode evaluation_mode, dom_object *intern, php_dom_xpath_callbacks_proxy_factory proxy_factory)
493 {
494 uint32_t param_count = num_args;
495 zval *params = php_dom_xpath_callback_fetch_args(ctxt, param_count, evaluation_mode, intern, proxy_factory);
496
497 const char *namespace = (const char *) ctxt->context->functionURI;
498 /* Impossible because it wouldn't have been registered inside the context. */
499 ZEND_ASSERT(xpath_callbacks->namespaces != NULL);
500
501 php_dom_xpath_callback_ns *ns = zend_hash_str_find_ptr(xpath_callbacks->namespaces, namespace, strlen(namespace));
502 /* Impossible because it wouldn't have been registered inside the context. */
503 ZEND_ASSERT(ns != NULL);
504
505 const char *function_name = (const char *) ctxt->context->function;
506 size_t function_name_length = strlen(function_name);
507
508 zend_result result = php_dom_xpath_callback_dispatch(xpath_callbacks, ns, ctxt, params, param_count, function_name, function_name_length);
509
510 php_dom_xpath_callback_cleanup_args(params, param_count);
511 if (UNEXPECTED(result != SUCCESS)) {
512 /* Push sentinel value */
513 valuePush(ctxt, xmlXPathNewString((const xmlChar *) ""));
514 }
515
516 return result;
517 }
518
519 #endif
520