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