From eb900c3b632d0160d86a5388f55722f1407cb647 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 3 May 2021 17:40:42 -0500 Subject: [PATCH] Fix #77686: Immediately remove ID from document when removing child Prior a removed node was still returned from DOMDocument::getElementById() until the DOMElement object was destroyed. Now null is returned from getElementById() even if a reference remains to the removed DOMElement object. --- ext/dom/node.c | 28 ++++++++++++++++++++++++++++ ext/dom/tests/bug77686.phpt | 18 ++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 ext/dom/tests/bug77686.phpt diff --git a/ext/dom/node.c b/ext/dom/node.c index 72f0b9337d39d..7b7d00ac501db 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -925,6 +925,33 @@ static xmlNodePtr _php_dom_insert_fragment(xmlNodePtr nodep, xmlNodePtr prevsib, } /* }}} */ +void dom_node_remove_ids_from_doc(xmlNodePtr node) /* {{{ */ +{ + xmlNodePtr child; + xmlAttrPtr attrp; + + if (node->type == XML_DTD_NODE) { + return; // Skip DTD nodes, see bug #54601 + } + + child = node->children; + while (child) { + dom_node_remove_ids_from_doc(child); + child = child->next; + } + + attrp = node->properties; + while (attrp) { + if (attrp->atype == XML_ATTRIBUTE_ID) { + xmlRemoveID(attrp->doc, attrp); + attrp->atype = 0; + break; + } + attrp = attrp->next; + } +} +/* }}} */ + /* {{{ proto domnode dom_node_insert_before(DomNode newChild, DomNode refChild); URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-952280727 Since: @@ -1217,6 +1244,7 @@ PHP_FUNCTION(dom_node_remove_child) while (children) { if (children == child) { xmlUnlinkNode(child); + dom_node_remove_ids_from_doc(child); DOM_RET_OBJ(child, &ret, intern); return; } diff --git a/ext/dom/tests/bug77686.phpt b/ext/dom/tests/bug77686.phpt new file mode 100644 index 0000000000000..530b75252275f --- /dev/null +++ b/ext/dom/tests/bug77686.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #77686 Removed elements are still returned by getElementById +--EXTENSIONS-- +dom +--FILE-- +loadHTML('
'); +$body = $doc->getElementById('x'); +$div = $doc->getElementById('y'); +$span = $doc->getElementById('z'); +$body->removeChild($div); +var_dump($doc->getElementById('y')); +var_dump($doc->getElementById('z')); +?> +--EXPECT-- +NULL +NULL