Commit d73b2f782e6 for php.net
commit d73b2f782e6e2487fde0d6d770454dc798dde335
Author: Niels Dossche <7771979+ndossche@users.noreply.github.com>
Date: Thu Jan 29 18:26:50 2026 +0100
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.
diff --git a/NEWS b/NEWS
index dbac366b38a..cb2206ec1f5 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 338b4acc449..e8fd7535db8 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 0dd1b959752..b6a794edcdd 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 22d52f123a1..a4dbec08631 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 00000000000..d9f9478925e
--- /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--
+<?php
+
+$implementation = new Dom\Implementation();
+$node = $implementation->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) "<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "publicId" "systemId">
+"
+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 bfb150b5cca..74deed8bdc3 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 @@
["nodeName"]=>
string(5) "qname"
["baseURI"]=>
- NULL
+ string(11) "about:blank"
["isConnected"]=>
bool(false)
["parentNode"]=>
@@ -86,7 +86,7 @@
["nodeName"]=>
string(5) "qname"
["baseURI"]=>
- NULL
+ string(11) "about:blank"
["isConnected"]=>
bool(false)
["parentNode"]=>
@@ -129,7 +129,7 @@
["nodeName"]=>
string(5) "qname"
["baseURI"]=>
- NULL
+ string(11) "about:blank"
["isConnected"]=>
bool(false)
["parentNode"]=>
@@ -172,7 +172,7 @@
["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 fb0853939f8..f9bb1f7a996 100644
--- a/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt
+++ b/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt
@@ -148,7 +148,7 @@
["nodeName"]=>
string(3) "GIF"
["baseURI"]=>
- NULL
+ string(11) "about:blank"
["isConnected"]=>
bool(false)
["parentNode"]=>