From d73b2f782e6e2487fde0d6d770454dc798dde335 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 29 Jan 2026 18:26:50 +0100 Subject: [PATCH 1/4] Fix GH-21077: Accessing Dom\Node::baseURI can throw TypeError Prior to this patch there was a common read handler, and it relied on the dom class set in the intern document. However, Dom\Implementation allows creating DTDs unassociated with a document, so we can't rely on an intern document and the check fails. This causes the ZVAL_NULL() path to be taken. To solve this, just split the handler. Closes GH-21082. --- NEWS | 4 +++ ext/dom/dom_properties.h | 1 + ext/dom/node.c | 25 ++++++++++++----- ext/dom/php_dom.c | 2 +- ext/dom/tests/modern/common/gh21077.phpt | 28 +++++++++++++++++++ ...ent_implementation_createDocumentType.phpt | 8 +++--- ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt | 2 +- 7 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 ext/dom/tests/modern/common/gh21077.phpt diff --git a/NEWS b/NEWS index dbac366b38aa..cb2206ec1f5b 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,10 @@ PHP NEWS . Fixed bug GH-21023 (CURLOPT_XFERINFOFUNCTION crash with a null callback). (David Carlier) +- DOM: + . Fixed bug GH-21077 (Accessing Dom\Node::baseURI can throw TypeError). + (ndossche) + - PDO_PGSQL: . Fixed bug GH-21055 (connection attribute status typo for GSS negotiation). (lsaos) diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index 338b4acc449f..e8fd7535db8c 100644 --- a/ext/dom/dom_properties.h +++ b/ext/dom/dom_properties.h @@ -134,6 +134,7 @@ zend_result dom_modern_node_prefix_read(dom_object *obj, zval *retval); zend_result dom_node_prefix_write(dom_object *obj, zval *newval); zend_result dom_node_local_name_read(dom_object *obj, zval *retval); zend_result dom_node_base_uri_read(dom_object *obj, zval *retval); +zend_result dom_modern_node_base_uri_read(dom_object *obj, zval *retval); zend_result dom_node_text_content_read(dom_object *obj, zval *retval); zend_result dom_node_text_content_write(dom_object *obj, zval *newval); diff --git a/ext/dom/node.c b/ext/dom/node.c index 0dd1b959752e..b6a794edcdd8 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -659,14 +659,25 @@ zend_result dom_node_base_uri_read(dom_object *obj, zval *retval) ZVAL_STRING(retval, (const char *) baseuri); xmlFree(baseuri); } else { - if (php_dom_follow_spec_intern(obj)) { - if (nodep->doc->URL) { - ZVAL_STRING(retval, (const char *) nodep->doc->URL); - } else { - ZVAL_STRING(retval, "about:blank"); - } + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +zend_result dom_modern_node_base_uri_read(dom_object *obj, zval *retval) +{ + DOM_PROP_NODE(xmlNodePtr, nodep, obj); + + xmlChar *baseuri = xmlNodeGetBase(nodep->doc, nodep); + if (baseuri) { + ZVAL_STRING(retval, (const char *) baseuri); + xmlFree(baseuri); + } else { + if (nodep->doc && nodep->doc->URL) { + ZVAL_STRING(retval, (const char *) nodep->doc->URL); } else { - ZVAL_NULL(retval); + ZVAL_STRING(retval, "about:blank"); } } diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 22d52f123a13..a4dbec086316 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -892,7 +892,7 @@ PHP_MINIT_FUNCTION(dom) zend_hash_init(&dom_modern_node_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeType", dom_node_node_type_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "nodeName", dom_node_node_name_read, NULL); - DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "baseURI", dom_node_base_uri_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "baseURI", dom_modern_node_base_uri_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "isConnected", dom_node_is_connected_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL); diff --git a/ext/dom/tests/modern/common/gh21077.phpt b/ext/dom/tests/modern/common/gh21077.phpt new file mode 100644 index 000000000000..d9f9478925ee --- /dev/null +++ b/ext/dom/tests/modern/common/gh21077.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-21077 (Accessing Dom\Node::baseURI can throw TypeError) +--EXTENSIONS-- +dom +--CREDITS-- +mbeccati +--FILE-- +createDocumentType('html', 'publicId', 'systemId'); + +var_dump($node->baseURI); + +$dom = Dom\XMLDocument::createEmpty(); +$dom->append($node = $dom->importNode($node)); + +var_dump($dom->saveXML()); + +var_dump($node->baseURI); + +?> +--EXPECT-- +string(11) "about:blank" +string(84) " + +" +string(11) "about:blank" diff --git a/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt b/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt index bfb150b5cca8..74deed8bdc34 100644 --- a/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt +++ b/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt @@ -43,7 +43,7 @@ object(Dom\DocumentType)#3 (19) { ["nodeName"]=> string(5) "qname" ["baseURI"]=> - NULL + string(11) "about:blank" ["isConnected"]=> bool(false) ["parentNode"]=> @@ -86,7 +86,7 @@ object(Dom\DocumentType)#2 (19) { ["nodeName"]=> string(5) "qname" ["baseURI"]=> - NULL + string(11) "about:blank" ["isConnected"]=> bool(false) ["parentNode"]=> @@ -129,7 +129,7 @@ object(Dom\DocumentType)#1 (19) { ["nodeName"]=> string(5) "qname" ["baseURI"]=> - NULL + string(11) "about:blank" ["isConnected"]=> bool(false) ["parentNode"]=> @@ -172,7 +172,7 @@ object(Dom\DocumentType)#4 (19) { ["nodeName"]=> string(5) "qname" ["baseURI"]=> - NULL + string(11) "about:blank" ["isConnected"]=> bool(false) ["parentNode"]=> diff --git a/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt b/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt index fb0853939f88..f9bb1f7a996e 100644 --- a/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt +++ b/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt @@ -148,7 +148,7 @@ object(Dom\Notation)#4 (13) { ["nodeName"]=> string(3) "GIF" ["baseURI"]=> - NULL + string(11) "about:blank" ["isConnected"]=> bool(false) ["parentNode"]=> From d1c1a9fa82bdfbdb4a0a2e0f72b428552167ebe8 Mon Sep 17 00:00:00 2001 From: Arshid Date: Fri, 30 Jan 2026 23:28:25 +0530 Subject: [PATCH 2/4] [skip ci] Remove unused arg from zend_throw_incdec_ref_error() (GH-21081) --- Zend/zend_execute.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e95931276ef5..42f8d46d5a3d 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2185,8 +2185,7 @@ static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *re return NULL; } -static ZEND_COLD zend_long zend_throw_incdec_ref_error( - zend_reference *ref, zend_property_info *error_prop OPLINE_DC) +static ZEND_COLD zend_long zend_throw_incdec_ref_error(zend_property_info *error_prop OPLINE_DC) { zend_string *type_str = zend_type_to_string(error_prop->type); if (ZEND_IS_INCREMENT(opline->opcode)) { @@ -2247,7 +2246,7 @@ static void zend_incdec_typed_ref(zend_reference *ref, zval *copy OPLINE_DC EXEC if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(copy) == IS_LONG) { zend_property_info *error_prop = zend_get_prop_not_accepting_double(ref); if (UNEXPECTED(error_prop)) { - zend_long val = zend_throw_incdec_ref_error(ref, error_prop OPLINE_CC); + zend_long val = zend_throw_incdec_ref_error(error_prop OPLINE_CC); ZVAL_LONG(var_ptr, val); } } else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, EX_USES_STRICT_TYPES()))) { From 6a0871853b2435f913fbfa668e01d33986b56ba5 Mon Sep 17 00:00:00 2001 From: Arshid Date: Sat, 31 Jan 2026 02:14:16 +0530 Subject: [PATCH 3/4] Drop unused priority argument from zend_ast_export_var() (GH-21096) --- Zend/zend_ast.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 764cac3f12af..54bf4c0bf2e6 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1718,7 +1718,7 @@ static ZEND_COLD bool zend_ast_var_needs_braces(char ch) return ch == '[' || zend_ast_valid_var_char(ch); } -static ZEND_COLD void zend_ast_export_var(smart_str *str, zend_ast *ast, int priority, int indent) +static ZEND_COLD void zend_ast_export_var(smart_str *str, zend_ast *ast, int indent) { if (ast->kind == ZEND_AST_ZVAL) { zval *zv = zend_ast_get_zval(ast); @@ -2417,7 +2417,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio /* 1 child node */ case ZEND_AST_VAR: smart_str_appendc(str, '$'); - zend_ast_export_var(str, ast->child[0], 0, indent); + zend_ast_export_var(str, ast->child[0], indent); break; case ZEND_AST_CONST: zend_ast_export_ns_name(str, ast->child[0], 0, indent); @@ -2532,12 +2532,12 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_NULLSAFE_PROP: zend_ast_export_ex(str, ast->child[0], 0, indent); smart_str_appends(str, ast->kind == ZEND_AST_NULLSAFE_PROP ? "?->" : "->"); - zend_ast_export_var(str, ast->child[1], 0, indent); + zend_ast_export_var(str, ast->child[1], indent); break; case ZEND_AST_STATIC_PROP: zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::$"); - zend_ast_export_var(str, ast->child[1], 0, indent); + zend_ast_export_var(str, ast->child[1], indent); break; case ZEND_AST_CALL: { zend_ast *left = ast->child[0]; @@ -2857,7 +2857,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_NULLSAFE_METHOD_CALL: zend_ast_export_ex(str, ast->child[0], 0, indent); smart_str_appends(str, ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL ? "?->" : "->"); - zend_ast_export_var(str, ast->child[1], 0, indent); + zend_ast_export_var(str, ast->child[1], indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[2], 0, indent); smart_str_appendc(str, ')'); @@ -2865,7 +2865,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_STATIC_CALL: zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); - zend_ast_export_var(str, ast->child[1], 0, indent); + zend_ast_export_var(str, ast->child[1], indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[2], 0, indent); smart_str_appendc(str, ')'); @@ -2901,7 +2901,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_catch_name_list(str, zend_ast_get_list(ast->child[0]), indent); if (ast->child[1]) { smart_str_appends(str, " $"); - zend_ast_export_var(str, ast->child[1], 0, indent); + zend_ast_export_var(str, ast->child[1], indent); } smart_str_appends(str, ") {\n"); zend_ast_export_stmt(str, ast->child[2], indent + 1); From 927b9eecb6da1824d1d1bc050a88486ae1e5eba8 Mon Sep 17 00:00:00 2001 From: Arshid Date: Sat, 31 Jan 2026 02:16:26 +0530 Subject: [PATCH 4/4] Drop unused error_type argument from zend_check_magic_method_public() (GH-21095) --- Zend/zend_API.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5ae7c7a79340..a2a50e3a79fc 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2743,7 +2743,7 @@ static void zend_check_magic_method_static( } static void zend_check_magic_method_public( - const zend_class_entry *ce, const zend_function *fptr, int error_type) + const zend_class_entry *ce, const zend_function *fptr) { // TODO: Remove this warning after adding proper visibility handling. if (!(fptr->common.fn_flags & ZEND_ACC_PUBLIC)) { @@ -2786,77 +2786,77 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, } else if (zend_string_equals_literal(lcname, ZEND_GET_FUNC_NAME)) { zend_check_magic_method_args(1, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING); } else if (zend_string_equals_literal(lcname, ZEND_SET_FUNC_NAME)) { zend_check_magic_method_args(2, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID); } else if (zend_string_equals_literal(lcname, ZEND_UNSET_FUNC_NAME)) { zend_check_magic_method_args(1, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID); } else if (zend_string_equals_literal(lcname, ZEND_ISSET_FUNC_NAME)) { zend_check_magic_method_args(1, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_BOOL); } else if (zend_string_equals_literal(lcname, ZEND_CALL_FUNC_NAME)) { zend_check_magic_method_args(2, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING); zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_ARRAY); } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) { zend_check_magic_method_args(2, ce, fptr, error_type); zend_check_magic_method_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING); zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_ARRAY); } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_STRING); } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, (MAY_BE_ARRAY | MAY_BE_NULL)); } else if (zend_string_equals_literal(lcname, "__serialize")) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_ARRAY); } else if (zend_string_equals_literal(lcname, "__unserialize")) { zend_check_magic_method_args(1, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ARRAY); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID); } else if (zend_string_equals_literal(lcname, "__set_state")) { zend_check_magic_method_args(1, ce, fptr, error_type); zend_check_magic_method_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ARRAY); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_OBJECT); } else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) { zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); } else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_SLEEP))) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_ARRAY); } else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_WAKEUP))) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID); } }