From 467e8c597620e0a9f7b1c255dbe3c645f6d4ca3c Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 26 Apr 2025 03:32:16 +0100 Subject: [PATCH 1/6] ext/libxml: Reduce scope of variables --- ext/libxml/libxml.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 429f04c00bc05..d4ea952f8a88d 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -322,10 +322,8 @@ static void php_libxml_node_free(xmlNodePtr node) PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr node) { - xmlNodePtr curnode; - if (node != NULL) { - curnode = node; + xmlNodePtr curnode = node; while (curnode != NULL) { /* If the _private field is set, there's still a userland reference somewhere. We'll delay freeing in this case. */ if (curnode->_private) { @@ -1157,13 +1155,11 @@ PHP_FUNCTION(libxml_get_last_error) /* {{{ Retrieve array of errors */ PHP_FUNCTION(libxml_get_errors) { - xmlErrorPtr error; - ZEND_PARSE_PARAMETERS_NONE(); if (LIBXML(error_list)) { array_init(return_value); - error = zend_llist_get_first(LIBXML(error_list)); + xmlErrorPtr error = zend_llist_get_first(LIBXML(error_list)); while (error != NULL) { zval z_error; @@ -1285,16 +1281,15 @@ zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node ex PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object) { - zend_class_entry *ce = NULL; xmlNodePtr node = NULL; - php_libxml_func_handler *export_hnd; if (Z_TYPE_P(object) == IS_OBJECT) { - ce = Z_OBJCE_P(object); + zend_class_entry *ce = Z_OBJCE_P(object); while (ce->parent != NULL) { ce = ce->parent; } - if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) { + php_libxml_func_handler *export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name); + if (export_hnd) { node = export_hnd->export_func(object); } } From 42190177ab2f9aadf709d0c067c9fa47aa26baaa Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 26 Apr 2025 03:24:48 +0100 Subject: [PATCH 2/6] ext/libxml: Add some const qualifiers --- ext/libxml/libxml.c | 14 +++++++------- ext/libxml/mime_sniff.c | 4 ++-- ext/libxml/php_libxml.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index d4ea952f8a88d..aed8702b30e52 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -333,11 +333,11 @@ PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr node) if (curnode->type == XML_ELEMENT_NODE) { /* This ensures that namespace references in this subtree are defined within this subtree, * otherwise a use-after-free would be possible when the original namespace holder gets freed. */ - php_libxml_node_ptr *ptr = curnode->_private; + const php_libxml_node_ptr *ptr = curnode->_private; /* Checking in case it runs out of reference */ if (ptr->_private) { - php_libxml_node_object *obj = ptr->_private; + const php_libxml_node_object *obj = ptr->_private; if (!obj->document || obj->document->class_type < PHP_LIBXML_CLASS_MODERN) { xmlReconciliateNs(curnode->doc, curnode); } @@ -524,7 +524,7 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) /* Check if there's been an external transport protocol with an encoding information */ if (enc == XML_CHAR_ENCODING_NONE) { - php_stream *s = (php_stream *) context; + const php_stream *s = (php_stream *) context; zend_string *charset = php_libxml_sniff_charset_from_stream(s); if (charset != NULL) { enc = xmlParseCharEncoding(ZSTR_VAL(charset)); @@ -934,7 +934,7 @@ PHP_LIBXML_API void php_libxml_shutdown(void) } } -PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext) +PHP_LIBXML_API void php_libxml_switch_context(const zval *context, zval *oldcontext) { if (oldcontext) { ZVAL_COPY_VALUE(oldcontext, &LIBXML(stream_context)); @@ -1268,7 +1268,7 @@ int php_libxml_xmlCheckUTF8(const unsigned char *s) return 1; } -zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function) +zval *php_libxml_register_export(const zend_class_entry *ce, php_libxml_export_node export_function) { php_libxml_func_handler export_hnd; @@ -1284,11 +1284,11 @@ PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object) xmlNodePtr node = NULL; if (Z_TYPE_P(object) == IS_OBJECT) { - zend_class_entry *ce = Z_OBJCE_P(object); + const zend_class_entry *ce = Z_OBJCE_P(object); while (ce->parent != NULL) { ce = ce->parent; } - php_libxml_func_handler *export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name); + const php_libxml_func_handler *export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name); if (export_hnd) { node = export_hnd->export_func(object); } diff --git a/ext/libxml/mime_sniff.c b/ext/libxml/mime_sniff.c index 0ca032f9b795e..14034f3db2677 100644 --- a/ext/libxml/mime_sniff.c +++ b/ext/libxml/mime_sniff.c @@ -314,8 +314,8 @@ PHP_LIBXML_API zend_string *php_libxml_sniff_charset_from_stream(const php_strea ZEND_HASH_REVERSE_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) { if (Z_TYPE_P(header) == IS_STRING) { /* If no colon is found in the header, we assume it's the HTTP status line and bail out. */ - char *colon = memchr(Z_STRVAL_P(header), ':', Z_STRLEN_P(header)); - char *space = memchr(Z_STRVAL_P(header), ' ', Z_STRLEN_P(header)); + const char *colon = memchr(Z_STRVAL_P(header), ':', Z_STRLEN_P(header)); + const char *space = memchr(Z_STRVAL_P(header), ' ', Z_STRLEN_P(header)); if (colon == NULL || space < colon) { return NULL; } diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index 3314bf6a7b28c..c05490cf5d998 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -196,7 +196,7 @@ PHP_LIBXML_API unsigned int php_libxml_increment_doc_ref(php_libxml_node_object PHP_LIBXML_API unsigned int php_libxml_decrement_doc_ref_directly(php_libxml_ref_obj *document); PHP_LIBXML_API unsigned int php_libxml_decrement_doc_ref(php_libxml_node_object *object); PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object); -PHP_LIBXML_API zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function); +PHP_LIBXML_API zval *php_libxml_register_export(const zend_class_entry *ce, php_libxml_export_node export_function); /* When an explicit freeing of node and children is required */ PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr node); PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node); @@ -208,7 +208,7 @@ PHP_LIBXML_API void php_libxml_pretend_ctx_error_ex(const char *file, int line, PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...); PHP_LIBXML_API void php_libxml_error_handler_va(php_libxml_error_level error_type, void *ctx, const char *msg, va_list args); PHP_LIBXML_API int php_libxml_xmlCheckUTF8(const unsigned char *s); -PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext); +PHP_LIBXML_API void php_libxml_switch_context(const zval *context, zval *oldcontext); PHP_LIBXML_API void php_libxml_issue_error(int level, const char *msg); PHP_LIBXML_API bool php_libxml_disable_entity_loader(bool disable); PHP_LIBXML_API void php_libxml_set_old_ns(xmlDocPtr doc, xmlNsPtr ns); From fd39f39bd6dc6ff5ee12f4c3e8c87cc3818c8555 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 26 Apr 2025 03:27:45 +0100 Subject: [PATCH 3/6] ext/libxml: Use bool type instead of int type --- ext/libxml/libxml.c | 28 ++++++++++++++-------------- ext/libxml/php_libxml.h | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index aed8702b30e52..21d8535f5b71b 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -49,8 +49,8 @@ #include "libxml_arginfo.h" /* a true global for initialization */ -static int php_libxml_initialized = 0; -static int php_libxml_per_request_initialization = 1; +static bool php_libxml_initialized = false; +static bool php_libxml_per_request_initialization = true; static xmlExternalEntityLoader php_libxml_default_entity_loader; typedef struct php_libxml_func_handler { @@ -404,7 +404,7 @@ PHP_LIBXML_API php_stream_context *php_libxml_get_stream_context(void) /* Channel libxml file io layer through the PHP streams subsystem. * This allows use of ftps:// and https:// urls */ -static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char *mode, const int read_only) +static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char *mode, const bool read_only) { php_stream_statbuf ssbuf; char *resolved_path; @@ -480,12 +480,12 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char static void *php_libxml_streams_IO_open_read_wrapper(const char *filename) { - return php_libxml_streams_IO_open_wrapper(filename, "rb", 1); + return php_libxml_streams_IO_open_wrapper(filename, "rb", true); } static void *php_libxml_streams_IO_open_write_wrapper(const char *filename) { - return php_libxml_streams_IO_open_wrapper(filename, "wb", 0); + return php_libxml_streams_IO_open_wrapper(filename, "wb", false); } static int php_libxml_streams_IO_read(void *context, char *buffer, int len) @@ -916,7 +916,7 @@ PHP_LIBXML_API void php_libxml_initialize(void) zend_hash_init(&php_libxml_exports, 0, NULL, php_libxml_exports_dtor, 1); - php_libxml_initialized = 1; + php_libxml_initialized = true; } } @@ -930,7 +930,7 @@ PHP_LIBXML_API void php_libxml_shutdown(void) zend_hash_destroy(&php_libxml_exports); xmlSetExternalEntityLoader(php_libxml_default_entity_loader); - php_libxml_initialized = 0; + php_libxml_initialized = false; } } @@ -962,7 +962,7 @@ static PHP_MINIT_FUNCTION(libxml) for (sapi_name = supported_sapis; *sapi_name; sapi_name++) { if (strcmp(sapi_module.name, *sapi_name) == 0) { - php_libxml_per_request_initialization = 0; + php_libxml_per_request_initialization = false; break; } } @@ -1242,7 +1242,7 @@ PHP_FUNCTION(libxml_get_external_entity_loader) /* }}} */ /* {{{ Common functions shared by extensions */ -int php_libxml_xmlCheckUTF8(const unsigned char *s) +bool php_libxml_xmlCheckUTF8(const unsigned char *s) { size_t i; unsigned char c; @@ -1251,21 +1251,21 @@ int php_libxml_xmlCheckUTF8(const unsigned char *s) if ((c & 0x80) == 0) { } else if ((c & 0xe0) == 0xc0) { if ((s[i++] & 0xc0) != 0x80) { - return 0; + return false; } } else if ((c & 0xf0) == 0xe0) { if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) { - return 0; + return false; } } else if ((c & 0xf8) == 0xf0) { if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) { - return 0; + return false; } } else { - return 0; + return false; } } - return 1; + return true; } zval *php_libxml_register_export(const zend_class_entry *ce, php_libxml_export_node export_function) diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index c05490cf5d998..ea7961dc2f1a7 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -207,7 +207,7 @@ PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...); PHP_LIBXML_API void php_libxml_pretend_ctx_error_ex(const char *file, int line, int column, const char *msg,...); PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...); PHP_LIBXML_API void php_libxml_error_handler_va(php_libxml_error_level error_type, void *ctx, const char *msg, va_list args); -PHP_LIBXML_API int php_libxml_xmlCheckUTF8(const unsigned char *s); +PHP_LIBXML_API bool php_libxml_xmlCheckUTF8(const unsigned char *s); PHP_LIBXML_API void php_libxml_switch_context(const zval *context, zval *oldcontext); PHP_LIBXML_API void php_libxml_issue_error(int level, const char *msg); PHP_LIBXML_API bool php_libxml_disable_entity_loader(bool disable); From f17502f6d8c462fb41fca91d31eaf00b539b6033 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 26 Apr 2025 03:45:02 +0100 Subject: [PATCH 4/6] ext/libxml: libxml_set_external_entity_loader() always returns true --- ext/libxml/libxml.stub.php | 2 +- ext/libxml/libxml_arginfo.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/libxml/libxml.stub.php b/ext/libxml/libxml.stub.php index 519861e918d6a..ce257b54ab4d5 100644 --- a/ext/libxml/libxml.stub.php +++ b/ext/libxml/libxml.stub.php @@ -182,6 +182,6 @@ function libxml_clear_errors(): void {} #[\Deprecated(since: '8.0', message: 'as external entity loading is disabled by default')] function libxml_disable_entity_loader(bool $disable = true): bool {} -function libxml_set_external_entity_loader(?callable $resolver_function): bool {} +function libxml_set_external_entity_loader(?callable $resolver_function): true {} function libxml_get_external_entity_loader(): ?callable {} diff --git a/ext/libxml/libxml_arginfo.h b/ext/libxml/libxml_arginfo.h index 86336d09c7d1b..b2eac9399df5c 100644 --- a/ext/libxml/libxml_arginfo.h +++ b/ext/libxml/libxml_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e0d2498c10cba72adb97504fd13e178e090de2cd */ + * Stub hash: 6dceb619736a3de55b84609a9e3aeb13405bbfde */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_set_streams_context, 0, 1, IS_VOID, 0) ZEND_ARG_INFO(0, context) @@ -22,7 +22,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_disable_entity_loader, 0, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, disable, _IS_BOOL, 0, "true") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_set_external_entity_loader, 0, 1, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_set_external_entity_loader, 0, 1, IS_TRUE, 0) ZEND_ARG_TYPE_INFO(0, resolver_function, IS_CALLABLE, 1) ZEND_END_ARG_INFO() From 1537f9100ef5eafc9d13f4fcbd054b15d7a45f24 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 26 Apr 2025 03:34:11 +0100 Subject: [PATCH 5/6] ext/libxml: Minor code nits --- ext/libxml/libxml.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 21d8535f5b71b..fa67d1d7bfbe6 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -409,7 +409,7 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char php_stream_statbuf ssbuf; char *resolved_path; const char *path_to_open = NULL; - bool isescaped = false; + bool is_escaped = false; if (strstr(filename, "%00")) { php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes"); @@ -420,7 +420,7 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char if (uri && (uri->scheme == NULL || (xmlStrncmp(BAD_CAST uri->scheme, BAD_CAST "file", 4) == 0))) { resolved_path = xmlURIUnescapeString(filename, 0, NULL); - isescaped = 1; + is_escaped = true; #if LIBXML_VERSION >= 20902 && LIBXML_VERSION < 21300 && defined(PHP_WIN32) /* Libxml 2.9.2 prefixes local paths with file:/ instead of file://, thus the php stream wrapper will fail on a valid case. For this @@ -458,7 +458,7 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char php_stream_wrapper *wrapper = php_stream_locate_url_wrapper(resolved_path, &path_to_open, 0); if (wrapper && read_only && wrapper->wops->url_stat) { if (wrapper->wops->url_stat(wrapper, path_to_open, PHP_STREAM_URL_STAT_QUIET, &ssbuf, NULL) == -1) { - if (isescaped) { + if (is_escaped) { xmlFree(resolved_path); } return NULL; @@ -472,7 +472,7 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char /* Prevent from closing this by fclose() */ ret_val->flags |= PHP_STREAM_FLAG_NO_FCLOSE; } - if (isescaped) { + if (is_escaped) { xmlFree(resolved_path); } return ret_val; @@ -513,13 +513,14 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) return NULL; } - if (URI == NULL) - return(NULL); + if (URI == NULL) { + return NULL; + } context = php_libxml_streams_IO_open_read_wrapper(URI); if (context == NULL) { - return(NULL); + return NULL; } /* Check if there's been an external transport protocol with an encoding information */ @@ -544,7 +545,7 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) } else php_libxml_streams_IO_close(context); - return(ret); + return ret; } static xmlOutputBufferPtr @@ -559,8 +560,9 @@ php_libxml_output_buffer_create_filename(const char *URI, void *context = NULL; char *unescaped = NULL; - if (URI == NULL) + if (URI == NULL) { goto err; + } if (strstr(URI, "%00")) { php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes"); @@ -569,8 +571,9 @@ php_libxml_output_buffer_create_filename(const char *URI, puri = xmlParseURI(URI); if (puri != NULL) { - if (puri->scheme != NULL) + if (puri->scheme != NULL) { unescaped = xmlURIUnescapeString(URI, 0, NULL); + } xmlFreeURI(puri); } @@ -596,7 +599,7 @@ php_libxml_output_buffer_create_filename(const char *URI, ret->closecallback = php_libxml_streams_IO_close; } - return(ret); + return ret; err: /* Similarly to __xmlOutputBufferCreateFilename we should also close the encoder on failure. */ @@ -992,7 +995,7 @@ static PHP_RINIT_FUNCTION(libxml) * other threads/requests that might have disabled the loader * do not affect the current request. */ - LIBXML(entity_loader_disabled) = 0; + LIBXML(entity_loader_disabled) = false; return SUCCESS; } @@ -1094,7 +1097,7 @@ PHP_FUNCTION(libxml_use_internal_errors) RETURN_BOOL(retval); } - if (use_errors == 0) { + if (use_errors == false) { xmlSetStructuredErrorFunc(NULL, NULL); if (LIBXML(error_list)) { zend_llist_destroy(LIBXML(error_list)); @@ -1196,7 +1199,7 @@ PHP_LIBXML_API bool php_libxml_disable_entity_loader(bool disable) /* {{{ */ /* {{{ Disable/Enable ability to load external entities */ PHP_FUNCTION(libxml_disable_entity_loader) { - bool disable = 1; + bool disable = true; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL From e9667e4d8b07b35279638ae7b2fc183b1c5987c3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 26 Apr 2025 12:52:35 +0100 Subject: [PATCH 6/6] ext/libxml: Get rid of useless php_libxml_func_handler abstraction --- ext/libxml/libxml.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index fa67d1d7bfbe6..6bcb092fd29d6 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -53,10 +53,6 @@ static bool php_libxml_initialized = false; static bool php_libxml_per_request_initialization = true; static xmlExternalEntityLoader php_libxml_default_entity_loader; -typedef struct php_libxml_func_handler { - php_libxml_export_node export_func; -} php_libxml_func_handler; - static HashTable php_libxml_exports; static ZEND_DECLARE_MODULE_GLOBALS(libxml) @@ -901,11 +897,6 @@ PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...) va_end(args); } -static void php_libxml_exports_dtor(zval *zv) -{ - free(Z_PTR_P(zv)); -} - PHP_LIBXML_API void php_libxml_initialize(void) { if (!php_libxml_initialized) { @@ -917,7 +908,7 @@ PHP_LIBXML_API void php_libxml_initialize(void) php_libxml_default_entity_loader = xmlGetExternalEntityLoader(); xmlSetExternalEntityLoader(php_libxml_pre_ext_ent_loader); - zend_hash_init(&php_libxml_exports, 0, NULL, php_libxml_exports_dtor, 1); + zend_hash_init(&php_libxml_exports, 0, NULL, NULL, 1); php_libxml_initialized = true; } @@ -1273,13 +1264,10 @@ bool php_libxml_xmlCheckUTF8(const unsigned char *s) zval *php_libxml_register_export(const zend_class_entry *ce, php_libxml_export_node export_function) { - php_libxml_func_handler export_hnd; - /* Initialize in case this module hasn't been loaded yet */ php_libxml_initialize(); - export_hnd.export_func = export_function; - return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd)); + return zend_hash_add_ptr(&php_libxml_exports, ce->name, export_function); } PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object) @@ -1291,9 +1279,9 @@ PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object) while (ce->parent != NULL) { ce = ce->parent; } - const php_libxml_func_handler *export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name); - if (export_hnd) { - node = export_hnd->export_func(object); + const php_libxml_export_node export_function = zend_hash_find_ptr(&php_libxml_exports, ce->name); + if (export_function) { + node = export_function(object); } } return node;