diff --git a/NEWS b/NEWS index c34aa2388d0..9e7596f70ce 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,9 @@ PHP NEWS . Fixed bug GH-9316 ($http_response_header is wrong for long status line). (cmb, timwolla) +- XML: + . Added libxml_get_external_entity_loader() function. (Tim Starling) + 18 Aug 2022, PHP 8.2.0beta3 - Core: diff --git a/UPGRADING b/UPGRADING index aacbeea92df..6d1904b3994 100644 --- a/UPGRADING +++ b/UPGRADING @@ -251,6 +251,9 @@ PHP 8.2 UPGRADE NOTES ini_parse_quantity(). The `memory_limit` setting accepts values higher than PHP_INT_MAX, than can not be parsed by ini_parse_quantity(). +- XML: + . libxml_get_external_entity_loader() + ======================================== 7. New Classes and Interfaces ======================================== diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 5821a5b5f35..fa8c1162788 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1538,6 +1538,10 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * SEPARATE_ZVAL_NOREF(_arg); \ } +/* get the zval* for a previously parsed argument */ +#define Z_PARAM_GET_PREV_ZVAL(dest) \ + zend_parse_arg_zval_deref(_arg, &dest, 0); + /* old "|" */ #define Z_PARAM_OPTIONAL \ _optional = 1; @@ -1700,6 +1704,10 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * #define Z_PARAM_FUNC_OR_NULL(dest_fci, dest_fcc) \ Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0) +#define Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(dest_fci, dest_fcc, dest_zp) \ + Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0) \ + Z_PARAM_GET_PREV_ZVAL(dest_zp) + /* old "h" */ #define Z_PARAM_ARRAY_HT_EX2(dest, check_null, deref, separate) \ Z_PARAM_PROLOGUE(deref, separate); \ diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 5813f437d22..270a0367481 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -228,23 +228,11 @@ static PHP_GINIT_FUNCTION(libxml) ZVAL_UNDEF(&libxml_globals->stream_context); libxml_globals->error_buffer.s = NULL; libxml_globals->error_list = NULL; - ZVAL_UNDEF(&libxml_globals->entity_loader.object); + ZVAL_NULL(&libxml_globals->entity_loader.callback); libxml_globals->entity_loader.fci.size = 0; libxml_globals->entity_loader_disabled = 0; } -static void _php_libxml_destroy_fci(zend_fcall_info *fci, zval *object) -{ - if (fci->size > 0) { - zval_ptr_dtor(&fci->function_name); - fci->size = 0; - } - if (!Z_ISUNDEF_P(object)) { - zval_ptr_dtor(object); - ZVAL_UNDEF(object); - } -} - /* Channel libxml file io layer through the PHP streams subsystem. * This allows use of ftps:// and https:// urls */ @@ -851,7 +839,9 @@ static PHP_RINIT_FUNCTION(libxml) static PHP_RSHUTDOWN_FUNCTION(libxml) { - _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object); + LIBXML(entity_loader).fci.size = 0; + zval_ptr_dtor_nogc(&LIBXML(entity_loader).callback); + ZVAL_NULL(&LIBXML(entity_loader).callback); return SUCCESS; } @@ -1071,29 +1061,36 @@ PHP_FUNCTION(libxml_disable_entity_loader) /* {{{ Changes the default external entity loader */ PHP_FUNCTION(libxml_set_external_entity_loader) { + zval *callback; zend_fcall_info fci; zend_fcall_info_cache fcc; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC_OR_NULL(fci, fcc) + Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(fci, fcc, callback) ZEND_PARSE_PARAMETERS_END(); - _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object); - if (ZEND_FCI_INITIALIZED(fci)) { /* argument not null */ LIBXML(entity_loader).fci = fci; - Z_ADDREF(fci.function_name); - if (fci.object != NULL) { - ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object); - Z_ADDREF(LIBXML(entity_loader).object); - } LIBXML(entity_loader).fcc = fcc; + } else { + LIBXML(entity_loader).fci.size = 0; } - + if (!Z_ISNULL(LIBXML(entity_loader).callback)) { + zval_ptr_dtor_nogc(&LIBXML(entity_loader).callback); + } + ZVAL_COPY(&LIBXML(entity_loader).callback, callback); RETURN_TRUE; } /* }}} */ +/* {{{ Get the current external entity loader, or null if the default loader is installer. */ +PHP_FUNCTION(libxml_get_external_entity_loader) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_COPY(&LIBXML(entity_loader).callback); +} +/* }}} */ + /* {{{ Common functions shared by extensions */ int php_libxml_xmlCheckUTF8(const unsigned char *s) { diff --git a/ext/libxml/libxml.stub.php b/ext/libxml/libxml.stub.php index 747e43d035b..97793f0cdee 100644 --- a/ext/libxml/libxml.stub.php +++ b/ext/libxml/libxml.stub.php @@ -177,3 +177,5 @@ function libxml_clear_errors(): void {} function libxml_disable_entity_loader(bool $disable = true): bool {} function libxml_set_external_entity_loader(?callable $resolver_function): bool {} + +function libxml_get_external_entity_loader(): ?callable {} diff --git a/ext/libxml/libxml_arginfo.h b/ext/libxml/libxml_arginfo.h index 58ca8c7540c..bd143cbed4d 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: d0c03ca63f0403ea38a59bde94510715e993b423 */ + * Stub hash: 8fa6f4fa2f3eb8db944626b5b3d814f5e3ae04a3 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_set_streams_context, 0, 1, IS_VOID, 0) ZEND_ARG_INFO(0, context) @@ -26,6 +26,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_set_external_entity_loade ZEND_ARG_TYPE_INFO(0, resolver_function, IS_CALLABLE, 1) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_get_external_entity_loader, 0, 0, IS_CALLABLE, 1) +ZEND_END_ARG_INFO() + ZEND_FUNCTION(libxml_set_streams_context); ZEND_FUNCTION(libxml_use_internal_errors); @@ -34,6 +37,7 @@ ZEND_FUNCTION(libxml_get_errors); ZEND_FUNCTION(libxml_clear_errors); ZEND_FUNCTION(libxml_disable_entity_loader); ZEND_FUNCTION(libxml_set_external_entity_loader); +ZEND_FUNCTION(libxml_get_external_entity_loader); static const zend_function_entry ext_functions[] = { @@ -44,6 +48,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(libxml_clear_errors, arginfo_libxml_clear_errors) ZEND_DEP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader) ZEND_FE(libxml_set_external_entity_loader, arginfo_libxml_set_external_entity_loader) + ZEND_FE(libxml_get_external_entity_loader, arginfo_libxml_get_external_entity_loader) ZEND_FE_END }; diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index 0e014f451af..96217fd7786 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -43,8 +43,8 @@ ZEND_BEGIN_MODULE_GLOBALS(libxml) smart_str error_buffer; zend_llist *error_list; struct _php_libxml_entity_resolver { - zval object; - zend_fcall_info fci; + zval callback; + zend_fcall_info fci; zend_fcall_info_cache fcc; } entity_loader; bool entity_loader_disabled; diff --git a/ext/libxml/tests/libxml_get_external_entity_loader.phpt b/ext/libxml/tests/libxml_get_external_entity_loader.phpt new file mode 100644 index 00000000000..48751d5cf2d --- /dev/null +++ b/ext/libxml/tests/libxml_get_external_entity_loader.phpt @@ -0,0 +1,36 @@ +--TEST-- +libxml_get_external_entity_loader() returns current handler +--EXTENSIONS-- +libxml +--FILE-- +name = $name; + } + + public function handle($public, $system, $context) { + return null; + } + + public function __toString() { + return "Handler#{$this->name}"; + } +} + +var_dump(libxml_get_external_entity_loader()); +libxml_set_external_entity_loader([new Handler('A'), 'handle']); +print libxml_get_external_entity_loader()[0] . "\n"; +libxml_set_external_entity_loader([new Handler('B'), 'handle']); +print libxml_get_external_entity_loader()[0] . "\n"; +libxml_set_external_entity_loader(null); +var_dump(libxml_get_external_entity_loader()); + +--EXPECT-- +NULL +Handler#A +Handler#B +NULL diff --git a/ext/libxml/tests/libxml_set_external_entity_loader_error2.phpt b/ext/libxml/tests/libxml_set_external_entity_loader_error2.phpt new file mode 100644 index 00000000000..053551f5ff3 --- /dev/null +++ b/ext/libxml/tests/libxml_set_external_entity_loader_error2.phpt @@ -0,0 +1,14 @@ +--TEST-- +libxml_set_external_entity_loader() with non-callable argument +--EXTENSIONS-- +libxml +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Exception: libxml_set_external_entity_loader(): Argument #1 ($resolver_function) must be a valid callback or null, function "nonexistent_function" not found or invalid function name