From cb1f9327c4796bfdd37136973161e23b100608ea Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 24 Oct 2025 21:17:00 +0200 Subject: [PATCH] Fix GH-20281: \Dom\Document::getElementById() is inconsistent after nodes are removed This worked for non-parsed elements already, but not for elements where xmlAddID() returns early due to the ID already existing. In that case what was missing is marking the attribute as an ID. Closes GH-20283. --- NEWS | 2 ++ ext/dom/html5_parser.c | 5 ++++- .../tests/modern/html/interactions/gh20281.phpt | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 ext/dom/tests/modern/html/interactions/gh20281.phpt diff --git a/NEWS b/NEWS index 1a7af0a2eb1..35f06bd93b2 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,8 @@ PHP NEWS - DOM: . Partially fixed bug GH-16317 (DOM classes do not allow __debugInfo() overrides to work). (nielsdos) + . Fixed bug GH-20281 (\Dom\Document::getElementById() is inconsistent + after nodes are removed). (nielsdos) - Exif: . Fix possible memory leak when tag is empty. (nielsdos) diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c index f1dc2db53b2..d5fe3d5c277 100644 --- a/ext/dom/html5_parser.c +++ b/ext/dom/html5_parser.c @@ -268,7 +268,10 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( /* xmlIsID does some other stuff too that is irrelevant here. */ if (local_name_length == 2 && local_name[0] == 'i' && local_name[1] == 'd' && attr->node.ns == LXB_NS_HTML) { - xmlAddID(NULL, lxml_doc, value, lxml_attr); + if (xmlAddID(NULL, lxml_doc, value, lxml_attr) == 0) { + /* If the ID already exists, the ID attribute still needs to be marked as an ID. */ + lxml_attr->atype = XML_ATTRIBUTE_ID; + } } /* libxml2 doesn't support line numbers on this anyway, it derives them instead, so don't bother */ diff --git a/ext/dom/tests/modern/html/interactions/gh20281.phpt b/ext/dom/tests/modern/html/interactions/gh20281.phpt new file mode 100644 index 00000000000..324c5a27563 --- /dev/null +++ b/ext/dom/tests/modern/html/interactions/gh20281.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-20281 (\Dom\Document::getElementById() is inconsistent after nodes are removed) +--EXTENSIONS-- +dom +--CREDITS-- +cscott +--FILE-- +b
c
', LIBXML_NOERROR); +$p = $d->getElementById('a'); +$p->remove(); +echo $d->getElementById('a')->textContent, "\n"; +?> +--EXPECT-- +c