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"