diff --git a/NEWS b/NEWS index 89a8dd8dc939..653bcff9e05a 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,8 @@ PHP NEWS 100-continue flow control). (Sjoerd Langkemper) - DOM: + . Fixed NamedNodeMap::getNamedItemNS() with an empty URI not matching + the null namespace in spec-following mode. (Ilia Alshanetsky) . Fixed stale getElementsByClassName() and other node list caches after className/classList writes and attribute removals. (Ilia Alshanetsky) diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c index c6d8157881a3..d665bbbaebdc 100644 --- a/ext/dom/namednodemap.c +++ b/ext/dom/namednodemap.c @@ -106,6 +106,9 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS) objmap = (dom_nnodemap_object *)intern->ptr; if (objmap != NULL) { + if (urilen == 0 && objmap->baseobj != NULL && php_dom_follow_spec_intern(objmap->baseobj)) { + uri = NULL; + } php_dom_obj_map_get_ns_named_item_into_zval(objmap, named, uri, return_value); } } diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c index 4d6479e003f9..14521f333263 100644 --- a/ext/dom/obj_map.c +++ b/ext/dom/obj_map.c @@ -514,7 +514,11 @@ static xmlNodePtr dom_map_get_ns_named_item_prop(dom_nnodemap_object *map, const xmlNodePtr nodep = dom_object_get_node(map->baseobj); if (nodep) { if (ns) { - return (xmlNodePtr) xmlHasNsProp(nodep, BAD_CAST ZSTR_VAL(named), BAD_CAST ns); + xmlNodePtr itemnode = (xmlNodePtr) xmlHasNsProp(nodep, BAD_CAST ZSTR_VAL(named), BAD_CAST ns); + if (itemnode != NULL && itemnode->type == XML_ATTRIBUTE_DECL) { + return NULL; + } + return itemnode; } else { if (php_dom_follow_spec_intern(map->baseobj)) { return (xmlNodePtr) php_dom_get_attribute_node(nodep, BAD_CAST ZSTR_VAL(named), ZSTR_LEN(named)); 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-- +loadXML(''); +$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(''); +$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-- + + + +]> + +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"