[php-src] master: [DOM] Fix getNamedItemNS() with empty URI not matching null namespace

Ilia Alshanetsky <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Date: 2026-08-30T12:32:26-04:00

Commit: https://github.com/php/php-src/commit/e832b5e3e7477189d7e4f33291941a1e2a00f9a6
Raw diff: https://github.com/php/php-src/commit/e832b5e3e7477189d7e4f33291941a1e2a00f9a6.diff

[DOM] Fix getNamedItemNS() with empty URI not matching null namespace

Normalize an empty URI to NULL in spec-following mode so
xmlHasNsProp() matches null-namespace attributes, and skip
XML_ATTRIBUTE_DECL results which cannot be wrapped as nodes.

Closes GH-23498

Changed paths:
  A  ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS.phpt
  A  ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS_dtd_default.phpt
  M  NEWS
  M  ext/dom/namednodemap.c


Diff:

diff --git a/NEWS b/NEWS
index 208ccaef35fb..608bacc29109 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,8 @@ PHP                                                                        NEWS
     return value of php_cli_server_client_send_through()). (Lazizbek Ergashev)
 
 - DOM:
+  . Fixed NamedNodeMap::getNamedItemNS() with an empty URI not matching
+    the null namespace in spec-following mode. (Ilia Alshanetsky)
   . Fixed a use-after-free when cloning a DOMNameSpaceNode after
     DOMDocument::xinclude(). (iliaal)
   . Fixed bug GH-23331 (UAF when node_list_unlink() skips attribute children
diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c
index bc867aba4384..ac368600e20e 100644
--- a/ext/dom/namednodemap.c
+++ b/ext/dom/namednodemap.c
@@ -213,6 +213,9 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS)
 	objmap = (dom_nnodemap_object *)intern->ptr;
 
 	if (objmap != NULL) {
+		if (urilen == 0 && objmap->baseobj != NULL && objmap->nodetype != XML_NOTATION_NODE && objmap->nodetype != XML_ENTITY_NODE && php_dom_follow_spec_intern(objmap->baseobj)) {
+			uri = NULL;
+		}
 		if ((objmap->nodetype == XML_NOTATION_NODE) ||
 			objmap->nodetype == XML_ENTITY_NODE) {
 			if (objmap->ht) {
@@ -229,6 +232,9 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS)
 			nodep = dom_object_get_node(objmap->baseobj);
 			if (nodep) {
 				itemnode = (xmlNodePtr)xmlHasNsProp(nodep, BAD_CAST named, BAD_CAST uri);
+				if (itemnode != NULL && itemnode->type == XML_ATTRIBUTE_DECL) {
+					itemnode = NULL;
+				}
 			}
 		}
 	}
diff --git a/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS.phpt b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS.phpt
new file mode 100644
index 000000000000..17d0659678e6
--- /dev/null
+++ b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS.phpt
@@ -0,0 +1,25 @@
+--TEST--
+getNamedItemNS() with an empty URI must look up the null namespace
+--EXTENSIONS--
+dom
+--FILE--
+<?php
+$d = new DOMDocument();
+$d->loadXML('<root xmlns:q="urn:q" bar="no-ns" q:bar="ns"/>');
+$a = $d->documentElement->attributes->getNamedItemNS('', 'bar');
+var_dump($a === null ? null : $a->nodeValue);
+$b = $d->documentElement->attributes->getNamedItemNS('urn:q', 'bar');
+var_dump($b === null ? null : $b->nodeValue);
+$d2 = Dom\XMLDocument::createFromString('<root xmlns:q="urn:q" bar="no-ns" q:bar="ns"/>');
+$a2 = $d2->documentElement->attributes->getNamedItemNS('', 'bar');
+var_dump($a2 === null ? null : $a2->nodeValue);
+var_dump($d2->documentElement->hasAttributeNS('', 'bar'));
+$c = $d2->documentElement->attributes->getNamedItemNS('urn:q', 'bar');
+var_dump($c === null ? null : $c->nodeValue);
+?>
+--EXPECT--
+NULL
+string(2) "ns"
+string(5) "no-ns"
+bool(true)
+string(2) "ns"
diff --git a/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS_dtd_default.phpt b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS_dtd_default.phpt
new file mode 100644
index 000000000000..c661af974c77
--- /dev/null
+++ b/ext/dom/tests/modern/spec/NamedNodeMap_getNamedItemNS_dtd_default.phpt
@@ -0,0 +1,24 @@
+--TEST--
+getNamedItemNS() with empty URI must not throw on DTD default attributes
+--EXTENSIONS--
+dom
+--FILE--
+<?php
+$xml = <<<XML
+<?xml version="1.0"?>
+<!DOCTYPE root [
+<!ELEMENT root EMPTY>
+<!ATTLIST root defaulted CDATA "from-dtd">
+]>
+<root real="present"/>
+XML;
+
+$el = Dom\XMLDocument::createFromString($xml)->documentElement;
+$defaulted = $el->attributes->getNamedItemNS('', 'defaulted');
+var_dump($defaulted === null ? null : $defaulted->nodeValue);
+$real = $el->attributes->getNamedItemNS('', 'real');
+var_dump($real === null ? null : $real->nodeValue);
+?>
+--EXPECT--
+NULL
+string(7) "present"
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.