mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
523 lines
18 KiB
C
523 lines
18 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| https://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Authors: Christian Stocker <chregu@php.net> |
|
|
| Rob Richards <rrichards@php.net> |
|
|
| Niels Dossche <nielsdos@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "php.h"
|
|
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
|
|
|
|
#include "php_dom.h"
|
|
#include "internal_helpers.h"
|
|
#include <libxml/parserInternals.h>
|
|
|
|
static void xpath_callbacks_entry_dtor(zval *zv)
|
|
{
|
|
zend_fcall_info_cache *fcc = Z_PTR_P(zv);
|
|
zend_fcc_dtor(fcc);
|
|
efree(fcc);
|
|
}
|
|
|
|
PHP_DOM_EXPORT void php_dom_xpath_callback_ns_ctor(php_dom_xpath_callback_ns *ns)
|
|
{
|
|
zend_hash_init(&ns->functions, 0, NULL, xpath_callbacks_entry_dtor, false);
|
|
ns->mode = PHP_DOM_REG_FUNC_MODE_NONE;
|
|
}
|
|
|
|
PHP_DOM_EXPORT void php_dom_xpath_callback_ns_dtor(php_dom_xpath_callback_ns *ns)
|
|
{
|
|
zend_hash_destroy(&ns->functions);
|
|
}
|
|
|
|
PHP_DOM_EXPORT void php_dom_xpath_callbacks_ctor(php_dom_xpath_callbacks *registry)
|
|
{
|
|
registry->php_ns = NULL;
|
|
registry->namespaces = NULL;
|
|
registry->node_list = NULL;
|
|
}
|
|
|
|
PHP_DOM_EXPORT void php_dom_xpath_callbacks_clean_node_list(php_dom_xpath_callbacks *registry)
|
|
{
|
|
if (registry->node_list) {
|
|
zend_hash_destroy(registry->node_list);
|
|
FREE_HASHTABLE(registry->node_list);
|
|
registry->node_list = NULL;
|
|
}
|
|
}
|
|
|
|
PHP_DOM_EXPORT void php_dom_xpath_callbacks_clean_argument_stack(xmlXPathParserContextPtr ctxt, uint32_t num_args)
|
|
{
|
|
for (uint32_t i = 0; i < num_args; i++) {
|
|
xmlXPathObjectPtr obj = valuePop(ctxt);
|
|
xmlXPathFreeObject(obj);
|
|
}
|
|
|
|
/* Don't push a sentinel value here. If this is called from an error situation, then by *not* pushing a sentinel
|
|
* the execution will halt. If this is called from a regular situation, then it is the caller's responsibility
|
|
* to ensure the stack remains balanced. */
|
|
}
|
|
|
|
PHP_DOM_EXPORT void php_dom_xpath_callbacks_dtor(php_dom_xpath_callbacks *registry)
|
|
{
|
|
if (registry->php_ns) {
|
|
php_dom_xpath_callback_ns_dtor(registry->php_ns);
|
|
efree(registry->php_ns);
|
|
}
|
|
if (registry->namespaces) {
|
|
php_dom_xpath_callback_ns *ns;
|
|
ZEND_HASH_MAP_FOREACH_PTR(registry->namespaces, ns) {
|
|
php_dom_xpath_callback_ns_dtor(ns);
|
|
efree(ns);
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
zend_hash_destroy(registry->namespaces);
|
|
FREE_HASHTABLE(registry->namespaces);
|
|
}
|
|
php_dom_xpath_callbacks_clean_node_list(registry);
|
|
}
|
|
|
|
static void php_dom_xpath_callback_ns_get_gc(php_dom_xpath_callback_ns *ns, zend_get_gc_buffer *gc_buffer)
|
|
{
|
|
zend_fcall_info_cache *entry;
|
|
ZEND_HASH_MAP_FOREACH_PTR(&ns->functions, entry) {
|
|
zend_get_gc_buffer_add_fcc(gc_buffer, entry);
|
|
} ZEND_HASH_FOREACH_END();
|
|
}
|
|
|
|
PHP_DOM_EXPORT void php_dom_xpath_callbacks_get_gc(php_dom_xpath_callbacks *registry, zend_get_gc_buffer *gc_buffer)
|
|
{
|
|
if (registry->php_ns) {
|
|
php_dom_xpath_callback_ns_get_gc(registry->php_ns, gc_buffer);
|
|
}
|
|
if (registry->namespaces) {
|
|
php_dom_xpath_callback_ns *ns;
|
|
ZEND_HASH_MAP_FOREACH_PTR(registry->namespaces, ns) {
|
|
php_dom_xpath_callback_ns_get_gc(ns, gc_buffer);
|
|
} ZEND_HASH_FOREACH_END();
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (registry->php_ns || registry->namespaces) {
|
|
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
|
|
php_dom_xpath_callbacks_get_gc(registry, gc_buffer);
|
|
zend_get_gc_buffer_use(gc_buffer, table, n);
|
|
|
|
if (object->properties == NULL && object->ce->default_properties_count == 0) {
|
|
return NULL;
|
|
} else {
|
|
return zend_std_get_properties(object);
|
|
}
|
|
} else {
|
|
return zend_std_get_gc(object, table, n);
|
|
}
|
|
}
|
|
|
|
static bool php_dom_xpath_is_callback_name_valid(const zend_string *name, php_dom_xpath_callback_name_validation name_validation)
|
|
{
|
|
if (ZSTR_LEN(name) == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (name_validation == PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NULLS || name_validation == PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME) {
|
|
if (zend_str_has_nul_byte(name)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (name_validation == PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME) {
|
|
if (xmlValidateNCName(BAD_CAST ZSTR_VAL(name), /* pass 0 to disallow spaces */ 0) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!php_dom_xpath_is_callback_name_valid(name, name_validation)) {
|
|
if (is_array) {
|
|
zend_argument_value_error(1, "must be an array containing valid callback names");
|
|
} else {
|
|
zend_argument_value_error(2, "must be a valid callback name");
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (registry->namespaces) {
|
|
zend_string *namespace;
|
|
php_dom_xpath_callback_ns *ns;
|
|
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(registry->namespaces, namespace, ns) {
|
|
zend_string *name;
|
|
ZEND_HASH_MAP_FOREACH_STR_KEY(&ns->functions, name) {
|
|
register_func(ctxt, namespace, name);
|
|
} ZEND_HASH_FOREACH_END();
|
|
} ZEND_HASH_FOREACH_END();
|
|
}
|
|
}
|
|
|
|
static zend_result 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
|
|
)
|
|
{
|
|
zval *entry, registered_value;
|
|
|
|
if (callable_ht) {
|
|
zend_string *key;
|
|
ZEND_HASH_FOREACH_STR_KEY_VAL(callable_ht, key, entry) {
|
|
zend_fcall_info_cache* fcc = emalloc(sizeof(*fcc));
|
|
char *error;
|
|
if (!zend_is_callable_ex(entry, NULL, 0, NULL, fcc, &error)) {
|
|
zend_argument_type_error(1, "must be an array with valid callbacks as values, %s", error);
|
|
efree(fcc);
|
|
efree(error);
|
|
return FAILURE;
|
|
}
|
|
|
|
zend_fcc_addref(fcc);
|
|
ZVAL_PTR(®istered_value, fcc);
|
|
|
|
if (!key) {
|
|
zend_string *tmp_str;
|
|
zend_string *str = zval_try_get_tmp_string(entry, &tmp_str);
|
|
if (str && php_dom_xpath_is_callback_name_valid_and_throw(str, name_validation, true)) {
|
|
zend_hash_update(&ns->functions, str, ®istered_value);
|
|
if (register_func) {
|
|
register_func(ctxt, namespace, str);
|
|
}
|
|
zend_tmp_string_release(tmp_str);
|
|
} else {
|
|
zend_fcc_dtor(fcc);
|
|
efree(fcc);
|
|
return FAILURE;
|
|
}
|
|
} else {
|
|
if (!php_dom_xpath_is_callback_name_valid_and_throw(key, name_validation, true)) {
|
|
zend_fcc_dtor(fcc);
|
|
efree(fcc);
|
|
return FAILURE;
|
|
}
|
|
zend_hash_update(&ns->functions, key, ®istered_value);
|
|
if (register_func) {
|
|
register_func(ctxt, namespace, key);
|
|
}
|
|
}
|
|
} ZEND_HASH_FOREACH_END();
|
|
ns->mode = PHP_DOM_REG_FUNC_MODE_SET;
|
|
} else if (name) {
|
|
if (!php_dom_xpath_is_callback_name_valid(name, name_validation)) {
|
|
zend_argument_value_error(1, "must be a valid callback name");
|
|
return FAILURE;
|
|
}
|
|
zend_fcall_info_cache* fcc = emalloc(sizeof(*fcc));
|
|
char *error;
|
|
zval tmp;
|
|
ZVAL_STR(&tmp, name);
|
|
if (!zend_is_callable_ex(&tmp, NULL, 0, NULL, fcc, &error)) {
|
|
zend_argument_type_error(1, "must be a callable, %s", error);
|
|
efree(fcc);
|
|
efree(error);
|
|
return FAILURE;
|
|
}
|
|
zend_fcc_addref(fcc);
|
|
ZVAL_PTR(®istered_value, fcc);
|
|
zend_hash_update(&ns->functions, name, ®istered_value);
|
|
if (register_func) {
|
|
register_func(ctxt, namespace, name);
|
|
}
|
|
ns->mode = PHP_DOM_REG_FUNC_MODE_SET;
|
|
} else {
|
|
ns->mode = PHP_DOM_REG_FUNC_MODE_ALL;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
static php_dom_xpath_callback_ns *php_dom_xpath_callbacks_ensure_ns(php_dom_xpath_callbacks *registry, zend_string *ns)
|
|
{
|
|
if (ns == NULL) {
|
|
if (!registry->php_ns) {
|
|
registry->php_ns = emalloc(sizeof(*registry->php_ns));
|
|
php_dom_xpath_callback_ns_ctor(registry->php_ns);
|
|
}
|
|
return registry->php_ns;
|
|
} else {
|
|
if (!registry->namespaces) {
|
|
/* In most cases probably only a single namespace is registered. */
|
|
registry->namespaces = zend_new_array(1);
|
|
}
|
|
php_dom_xpath_callback_ns *namespace = zend_hash_find_ptr(registry->namespaces, ns);
|
|
if (namespace == NULL) {
|
|
namespace = emalloc(sizeof(*namespace));
|
|
php_dom_xpath_callback_ns_ctor(namespace);
|
|
zend_hash_add_new_ptr(registry->namespaces, ns, namespace);
|
|
}
|
|
return namespace;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
php_dom_xpath_callback_ns *namespace = php_dom_xpath_callbacks_ensure_ns(registry, ns);
|
|
return php_dom_xpath_callback_ns_update_method_handler(namespace, ctxt, ns, name, callable_ht, name_validation, register_func);
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!php_dom_xpath_is_callback_name_valid_and_throw(name, name_validation, false)) {
|
|
return FAILURE;
|
|
}
|
|
|
|
php_dom_xpath_callback_ns *namespace = php_dom_xpath_callbacks_ensure_ns(registry, ns);
|
|
zend_fcall_info_cache* allocated_fcc = emalloc(sizeof(*allocated_fcc));
|
|
zend_fcc_dup(allocated_fcc, fcc);
|
|
|
|
zval registered_value;
|
|
ZVAL_PTR(®istered_value, allocated_fcc);
|
|
|
|
zend_hash_update(&namespace->functions, name, ®istered_value);
|
|
if (register_func) {
|
|
register_func(ctxt, ns, name);
|
|
}
|
|
|
|
namespace->mode = PHP_DOM_REG_FUNC_MODE_SET;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (param_count == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
zval *params = safe_emalloc(param_count, sizeof(zval), 0);
|
|
|
|
/* Reverse order to pop values off ctxt stack */
|
|
for (zval *param = params + param_count - 1; param >= params; param--) {
|
|
xmlXPathObjectPtr obj = valuePop(ctxt);
|
|
ZEND_ASSERT(obj != NULL);
|
|
switch (obj->type) {
|
|
case XPATH_STRING:
|
|
ZVAL_STRING(param, (char *)obj->stringval);
|
|
break;
|
|
case XPATH_BOOLEAN:
|
|
ZVAL_BOOL(param, obj->boolval);
|
|
break;
|
|
case XPATH_NUMBER:
|
|
ZVAL_DOUBLE(param, obj->floatval);
|
|
break;
|
|
case XPATH_NODESET:
|
|
if (evaluation_mode == PHP_DOM_XPATH_EVALUATE_NODESET_TO_STRING) {
|
|
char *str = (char *)xmlXPathCastToString(obj);
|
|
ZVAL_STRING(param, str);
|
|
xmlFree(str);
|
|
} else if (evaluation_mode == PHP_DOM_XPATH_EVALUATE_NODESET_TO_NODESET) {
|
|
if (obj->nodesetval && obj->nodesetval->nodeNr > 0) {
|
|
array_init_size(param, obj->nodesetval->nodeNr);
|
|
zend_hash_real_init_packed(Z_ARRVAL_P(param));
|
|
for (int j = 0; j < obj->nodesetval->nodeNr; j++) {
|
|
xmlNodePtr node = obj->nodesetval->nodeTab[j];
|
|
zval child;
|
|
if (UNEXPECTED(node->type == XML_NAMESPACE_DECL)) {
|
|
xmlNodePtr nsparent = node->_private;
|
|
xmlNsPtr original = (xmlNsPtr) node;
|
|
|
|
/* Make sure parent dom object exists, so we can take an extra reference. */
|
|
zval parent_zval; /* don't destroy me, my lifetime is transferred to the fake namespace decl */
|
|
php_dom_create_object(nsparent, &parent_zval, intern);
|
|
dom_object *parent_intern = Z_DOMOBJ_P(&parent_zval);
|
|
|
|
php_dom_create_fake_namespace_decl(nsparent, original, &child, parent_intern);
|
|
} else {
|
|
proxy_factory(node, &child, intern, ctxt);
|
|
}
|
|
zend_hash_next_index_insert_new(Z_ARRVAL_P(param), &child);
|
|
}
|
|
} else {
|
|
ZVAL_EMPTY_ARRAY(param);
|
|
}
|
|
}
|
|
break;
|
|
default: {
|
|
char *str = (char *)xmlXPathCastToString(obj);
|
|
ZVAL_STRING(param, str);
|
|
xmlFree(str);
|
|
break;
|
|
}
|
|
}
|
|
xmlXPathFreeObject(obj);
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
static void php_dom_xpath_callback_cleanup_args(zval *params, uint32_t param_count)
|
|
{
|
|
if (params) {
|
|
for (uint32_t i = 0; i < param_count; i++) {
|
|
zval_ptr_dtor(¶ms[i]);
|
|
}
|
|
efree(params);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
zval callback_retval;
|
|
|
|
if (UNEXPECTED(ns == NULL)) {
|
|
zend_throw_error(NULL, "No callbacks were registered");
|
|
return FAILURE;
|
|
}
|
|
|
|
if (ns->mode == PHP_DOM_REG_FUNC_MODE_ALL) {
|
|
zend_fcall_info fci;
|
|
fci.size = sizeof(fci);
|
|
fci.object = NULL;
|
|
fci.retval = &callback_retval;
|
|
fci.param_count = param_count;
|
|
fci.params = params;
|
|
fci.named_params = NULL;
|
|
ZVAL_STRINGL(&fci.function_name, function_name, function_name_length);
|
|
|
|
zend_call_function(&fci, NULL);
|
|
zend_string_release_ex(Z_STR(fci.function_name), false);
|
|
if (UNEXPECTED(EG(exception))) {
|
|
return FAILURE;
|
|
}
|
|
} else {
|
|
ZEND_ASSERT(ns->mode == PHP_DOM_REG_FUNC_MODE_SET);
|
|
|
|
zval *fcc_wrapper = zend_hash_str_find(&ns->functions, function_name, function_name_length);
|
|
if (fcc_wrapper) {
|
|
zend_call_known_fcc(Z_PTR_P(fcc_wrapper), &callback_retval, param_count, params, NULL);
|
|
} else {
|
|
zend_throw_error(NULL, "No callback handler \"%s\" registered", function_name);
|
|
return FAILURE;
|
|
}
|
|
}
|
|
|
|
if (Z_TYPE(callback_retval) != IS_UNDEF) {
|
|
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) {
|
|
xpath_callbacks->node_list = zend_new_array(0);
|
|
}
|
|
Z_ADDREF_P(&callback_retval);
|
|
zend_hash_next_index_insert_new(xpath_callbacks->node_list, &callback_retval);
|
|
obj = Z_DOMOBJ_P(&callback_retval);
|
|
nodep = dom_object_get_node(obj);
|
|
valuePush(ctxt, xmlXPathNewNodeSet(nodep));
|
|
} 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 DOM nodes can be converted to an XPath expression");
|
|
zval_ptr_dtor(&callback_retval);
|
|
return FAILURE;
|
|
} else {
|
|
zend_string *tmp_str;
|
|
zend_string *str = zval_get_tmp_string(&callback_retval, &tmp_str);
|
|
valuePush(ctxt, xmlXPathNewString(BAD_CAST ZSTR_VAL(str)));
|
|
zend_tmp_string_release(tmp_str);
|
|
}
|
|
zval_ptr_dtor(&callback_retval);
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
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)
|
|
{
|
|
zend_result result = FAILURE;
|
|
|
|
if (UNEXPECTED(num_args == 0)) {
|
|
zend_throw_error(NULL, "Function name must be passed as the first argument");
|
|
goto cleanup_no_obj;
|
|
}
|
|
|
|
uint32_t param_count = num_args - 1;
|
|
zval *params = php_dom_xpath_callback_fetch_args(ctxt, param_count, evaluation_mode, intern, proxy_factory);
|
|
|
|
/* Last element of the stack is the function name */
|
|
xmlXPathObjectPtr obj = valuePop(ctxt);
|
|
if (UNEXPECTED(obj->stringval == NULL)) {
|
|
zend_type_error("Handler name must be a string");
|
|
goto cleanup;
|
|
}
|
|
|
|
const char *function_name = (const char *) obj->stringval;
|
|
size_t function_name_length = strlen(function_name);
|
|
|
|
result = php_dom_xpath_callback_dispatch(xpath_callbacks, xpath_callbacks->php_ns, ctxt, params, param_count, function_name, function_name_length);
|
|
|
|
cleanup:
|
|
xmlXPathFreeObject(obj);
|
|
php_dom_xpath_callback_cleanup_args(params, param_count);
|
|
cleanup_no_obj:
|
|
if (UNEXPECTED(result != SUCCESS)) {
|
|
/* Push sentinel value */
|
|
valuePush(ctxt, xmlXPathNewString((const xmlChar *) ""));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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)
|
|
{
|
|
uint32_t param_count = num_args;
|
|
zval *params = php_dom_xpath_callback_fetch_args(ctxt, param_count, evaluation_mode, intern, proxy_factory);
|
|
|
|
const char *namespace = (const char *) ctxt->context->functionURI;
|
|
/* Impossible because it wouldn't have been registered inside the context. */
|
|
ZEND_ASSERT(xpath_callbacks->namespaces != NULL);
|
|
|
|
php_dom_xpath_callback_ns *ns = zend_hash_str_find_ptr(xpath_callbacks->namespaces, namespace, strlen(namespace));
|
|
/* Impossible because it wouldn't have been registered inside the context. */
|
|
ZEND_ASSERT(ns != NULL);
|
|
|
|
const char *function_name = (const char *) ctxt->context->function;
|
|
size_t function_name_length = strlen(function_name);
|
|
|
|
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);
|
|
if (UNEXPECTED(result != SUCCESS)) {
|
|
/* Push sentinel value */
|
|
valuePush(ctxt, xmlXPathNewString((const xmlChar *) ""));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif
|