Commit 388c230352b for php
commit 388c230352b5796e4a7f83f0222a81bcb4925e78
Author: lazerg <lazerg2@gmail.com>
Date: Thu Sep 24 18:32:26 2026 +0500
Fix GH-23887: HTMLCollection::namedItem() mishandles the first child
dom_html_collection_named_item() seeded the iterator with basep->children,
but the handlers disagreed on what that seed meant: the noop handler never
moved it, the class name handler skipped the first child, and the children
handler never advanced past a non-matching first element. The iterator now
starts with a NULL candidate, and the children and tag name handlers start
from basep->children when they see NULL.
Close GH-23888
diff --git a/NEWS b/NEWS
index f0e89aa088b..12b499085d8 100644
--- a/NEWS
+++ b/NEWS
@@ -27,6 +27,8 @@ PHP NEWS
. Fixed Dom\HTMLDocument giving attributes the namespace of their element
when a fragment is parsed with an xlink, xml or xmlns context element.
(Ilia Alshanetsky)
+ . Fixed bug GH-23887 (Dom\HTMLCollection::namedItem() assertion failure,
+ hang, or missed first element). (Lazizbek Ergashev)
- FTP:
. Fixed bug GH-23619 (cryptic error on servers that don't support TLS
diff --git a/ext/dom/html_collection.c b/ext/dom/html_collection.c
index 2f156de2532..a3c2b16f15b 100644
--- a/ext/dom/html_collection.c
+++ b/ext/dom/html_collection.c
@@ -48,7 +48,6 @@ static dom_named_item dom_html_collection_named_item(zend_string *key, zend_obje
xmlNodePtr basep = dom_object_get_node(objmap->baseobj);
if (basep != NULL && basep->children != NULL) {
php_dom_obj_map_collection_iter iter = {0};
- iter.candidate = basep->children;
iter.basep = basep;
while (true) {
diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c
index 88d7075be53..275c9e9930e 100644
--- a/ext/dom/obj_map.c
+++ b/ext/dom/obj_map.c
@@ -311,9 +311,7 @@ static void dom_map_get_elements_item(dom_nnodemap_object *map, zend_long index,
static void dom_map_collection_named_item_elements_iter(dom_nnodemap_object *map, php_dom_obj_map_collection_iter *iter)
{
- if (iter->candidate != iter->basep->children) {
- iter->candidate = iter->candidate->next;
- }
+ iter->candidate = iter->candidate ? iter->candidate->next : iter->basep->children;
while (iter->candidate && iter->candidate->type != XML_ELEMENT_NODE) {
iter->candidate = iter->candidate->next;
}
@@ -369,7 +367,8 @@ static void dom_map_get_by_class_name_item(dom_nnodemap_object *map, zend_long i
static void dom_map_collection_named_item_by_tag_name_iter(dom_nnodemap_object *map, php_dom_obj_map_collection_iter *iter)
{
- iter->candidate = dom_get_elements_by_tag_name_ns_raw(iter->basep, iter->candidate, map->ns, map->local, map->local_lower, &iter->cur, iter->next);
+ xmlNodePtr nodep = iter->candidate ? iter->candidate : iter->basep->children;
+ iter->candidate = dom_get_elements_by_tag_name_ns_raw(iter->basep, nodep, map->ns, map->local, map->local_lower, &iter->cur, iter->next);
iter->next = iter->cur + 1;
}
diff --git a/ext/dom/tests/modern/html/gh23887.phpt b/ext/dom/tests/modern/html/gh23887.phpt
new file mode 100644
index 00000000000..a61f9c0b6a3
--- /dev/null
+++ b/ext/dom/tests/modern/html/gh23887.phpt
@@ -0,0 +1,39 @@
+--TEST--
+GH-23887 (Dom\HTMLCollection::namedItem() assertion failure, hang, or missed first element)
+--CREDITS--
+Lu Maltsis (lmaltsis)
+--EXTENSIONS--
+dom
+--FILE--
+<?php
+
+$dom = Dom\HTMLDocument::createFromString(<<<HTML
+<!DOCTYPE html>
+<b id="container">
+
+HTML);
+var_dump($dom->getElementById('container')->getElementsByClassName('')->namedItem("here"));
+
+$dom = Dom\HTMLDocument::createFromString('<!DOCTYPE html><div id="c"><p id="a" class="x"></p><p id="b" class="x"></p></div>', LIBXML_NOERROR);
+$c = $dom->getElementById('c');
+
+var_dump($c->getElementsByClassName('')->namedItem("a"));
+var_dump($c->getElementsByClassName('x')->namedItem("a")->id);
+var_dump($c->getElementsByClassName('x')->namedItem("b")->id);
+var_dump($c->children->namedItem("a")->id);
+var_dump($c->children->namedItem("b")->id);
+var_dump($c->children->namedItem("c"));
+var_dump($c->getElementsByTagName('p')->namedItem("a")->id);
+var_dump($c->getElementsByTagName('p')->namedItem("b")->id);
+
+?>
+--EXPECT--
+NULL
+NULL
+string(1) "a"
+string(1) "b"
+string(1) "a"
+string(1) "b"
+NULL
+string(1) "a"
+string(1) "b"