Category
stdlib · AOT / ext/dom
Problem
User-script AOT DOMDocument::loadXML() with a DTD ATTLIST … ID indexes only the first matching element (DomParseSimpleXmlIdsJitHelper::scanFirstAttributeValue). Later IDs return null from getElementById(). The first hit is also a detached shadow node (=== tree child is false; parentNode empty), not the live documentElement child. Zend/VM return the live tree node for every ID.
| Repro |
Zend 8.2+ / VM |
AOT (master @ eb638b2) |
DTD two <e id="a|b">; getElementById('a') |
tree child, parentNode=r, text 1 |
detached text 1, parentNode empty |
getElementById('b') |
tree child text 2 |
null |
Root cause: (1) ID scan is first-only per element name; (2) materializeIndexedElement creates orphan map entries before syncChildrenFromXml; (3) thin-AOT getElementById single-slot cache miss falls through to NestedJIT only, never PROP_ELEMENT_ID_MAP.
php-src reference
PHP implementation target
ext/dom/DomParseSimpleXmlIdsJitHelper.php — scan all DTD ID / xml:id elements (first-wins per id value)
ext/dom/JitDomDocumentElement.php / JitDomLoadXMLUserScript.php — register live syncChildren nodes into the id map; drop orphan materializeIndexedElement pre-pass
ext/dom/JitDomGetElementById.php — on element-cache miss, consult PROP_ELEMENT_ID_MAP before NestedJIT
Repro
./script/docker-exec.sh -- bash -lc 'php bin/compile.php -o /tmp/g.bin test/repro/issue_XXXX_dom_loadxml_multi_dtd_id_aot.php && /tmp/g.bin'
Done when
claim: DOM/XML agent — implementing multi-ID loadXML getElementById + live tree registration.
Category
stdlib· AOT / ext/domProblem
User-script AOT
DOMDocument::loadXML()with a DTDATTLIST … IDindexes only the first matching element (DomParseSimpleXmlIdsJitHelper::scanFirstAttributeValue). Later IDs returnnullfromgetElementById(). The first hit is also a detached shadow node (===tree child is false;parentNodeempty), not the livedocumentElementchild. Zend/VM return the live tree node for every ID.<e id="a|b">;getElementById('a')parentNode=r, text11,parentNodeemptygetElementById('b')2nullRoot cause: (1) ID scan is first-only per element name; (2)
materializeIndexedElementcreates orphan map entries beforesyncChildrenFromXml; (3) thin-AOTgetElementByIdsingle-slot cache miss falls through to NestedJIT only, neverPROP_ELEMENT_ID_MAP.php-src reference
ext/dom/php_dom.c—dom_document_get_element_by_idxmlAddID/ DTD ID attribute typePHP implementation target
ext/dom/DomParseSimpleXmlIdsJitHelper.php— scan all DTD ID /xml:idelements (first-wins per id value)ext/dom/JitDomDocumentElement.php/JitDomLoadXMLUserScript.php— register live syncChildren nodes into the id map; drop orphanmaterializeIndexedElementpre-passext/dom/JitDomGetElementById.php— on element-cache miss, consultPROP_ELEMENT_ID_MAPbefore NestedJITRepro
./script/docker-exec.sh -- bash -lc 'php bin/compile.php -o /tmp/g.bin test/repro/issue_XXXX_dom_loadxml_multi_dtd_id_aot.php && /tmp/g.bin'Done when
getElementByIdfor every DTD ID matches Zend textContent===correspondingdocumentElementchild (parentNodeis the root)dom_loadxml_getelementbyid_user_script.phptstill green./script/aot-smoke.shstays 8/8test/fixtures/aot/cases/claim: DOM/XML agent — implementing multi-ID loadXML getElementById + live tree registration.