From 863e546a8790d5961cec2be1ac2e4632191bdc2f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 1 Feb 2026 10:41:37 +0100 Subject: [PATCH] Fix GH-21097: Accessing Dom\Node properties can can throw TypeError(s) Split the handler again, or defer to instanceof when performance doesn't matter. --- ext/dom/documenttype.c | 4 +- ext/dom/dom_properties.h | 2 + ext/dom/entityreference.c | 8 ++++ ext/dom/node.c | 13 ++++++- ext/dom/php_dom.c | 4 +- ext/dom/tests/modern/common/gh21097.phpt | 49 ++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 ext/dom/tests/modern/common/gh21097.phpt diff --git a/ext/dom/documenttype.c b/ext/dom/documenttype.c index 666dae56dc0f2..6af23fd73c418 100644 --- a/ext/dom/documenttype.c +++ b/ext/dom/documenttype.c @@ -47,7 +47,7 @@ zend_result dom_documenttype_entities_read(dom_object *obj, zval *retval) { DOM_PROP_NODE(xmlDtdPtr, dtdptr, obj); - php_dom_create_iterator(retval, DOM_DTD_NAMEDNODEMAP, php_dom_follow_spec_intern(obj)); + php_dom_create_iterator(retval, DOM_DTD_NAMEDNODEMAP, instanceof_function(obj->std.ce, dom_modern_documenttype_class_entry)); xmlHashTable *entityht = (xmlHashTable *) dtdptr->entities; @@ -68,7 +68,7 @@ zend_result dom_documenttype_notations_read(dom_object *obj, zval *retval) { DOM_PROP_NODE(xmlDtdPtr, dtdptr, obj); - php_dom_create_iterator(retval, DOM_DTD_NAMEDNODEMAP, php_dom_follow_spec_intern(obj)); + php_dom_create_iterator(retval, DOM_DTD_NAMEDNODEMAP, instanceof_function(obj->std.ce, dom_modern_documenttype_class_entry)); xmlHashTable *notationht = (xmlHashTable *) dtdptr->notations; diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index e8fd7535db8cd..f82ce2645af1d 100644 --- a/ext/dom/dom_properties.h +++ b/ext/dom/dom_properties.h @@ -102,6 +102,7 @@ zend_result dom_entity_version_read(dom_object *obj, zval *retval); zend_result dom_entity_reference_child_read(dom_object *obj, zval *retval); zend_result dom_entity_reference_text_content_read(dom_object *obj, zval *retval); zend_result dom_entity_reference_child_nodes_read(dom_object *obj, zval *retval); +zend_result dom_modern_entity_reference_child_nodes_read(dom_object *obj, zval *retval); /* namednodemap properties */ zend_result dom_namednodemap_length_read(dom_object *obj, zval *retval); @@ -119,6 +120,7 @@ zend_result dom_node_node_type_read(dom_object *obj, zval *retval); zend_result dom_node_parent_node_read(dom_object *obj, zval *retval); zend_result dom_node_parent_element_read(dom_object *obj, zval *retval); zend_result dom_node_child_nodes_read(dom_object *obj, zval *retval); +zend_result dom_modern_node_child_nodes_read(dom_object *obj, zval *retval); zend_result dom_node_first_child_read(dom_object *obj, zval *retval); zend_result dom_node_last_child_read(dom_object *obj, zval *retval); zend_result dom_node_previous_sibling_read(dom_object *obj, zval *retval); diff --git a/ext/dom/entityreference.c b/ext/dom/entityreference.c index bea49d85d0f62..215df208f5305 100644 --- a/ext/dom/entityreference.c +++ b/ext/dom/entityreference.c @@ -106,4 +106,12 @@ zend_result dom_entity_reference_child_nodes_read(dom_object *obj, zval *retval) return dom_node_child_nodes_read(obj, retval); } +zend_result dom_modern_entity_reference_child_nodes_read(dom_object *obj, zval *retval) +{ + DOM_PROP_NODE(xmlNodePtr, nodep, obj); + + dom_entity_reference_fetch_and_sync_declaration(nodep); + return dom_modern_node_child_nodes_read(obj, retval); +} + #endif diff --git a/ext/dom/node.c b/ext/dom/node.c index b6a794edcdd88..9c1a508d669e9 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -287,7 +287,18 @@ zend_result dom_node_child_nodes_read(dom_object *obj, zval *retval) { DOM_PROP_NODE(xmlNodePtr, nodep, obj); - php_dom_create_iterator(retval, DOM_NODELIST, php_dom_follow_spec_intern(obj)); + php_dom_create_iterator(retval, DOM_NODELIST, false); + dom_object *intern = Z_DOMOBJ_P(retval); + dom_namednode_iter(obj, XML_ELEMENT_NODE, intern, NULL, NULL, 0, NULL, 0); + + return SUCCESS; +} + +zend_result dom_modern_node_child_nodes_read(dom_object *obj, zval *retval) +{ + DOM_PROP_NODE(xmlNodePtr, nodep, obj); + + php_dom_create_iterator(retval, DOM_NODELIST, true); dom_object *intern = Z_DOMOBJ_P(retval); dom_namednode_iter(obj, XML_ELEMENT_NODE, intern, NULL, NULL, 0, NULL, 0); diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index a4dbec0863161..135b3cdc5caa4 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -897,7 +897,7 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "ownerDocument", dom_node_owner_document_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentNode", dom_node_parent_node_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "parentElement", dom_node_parent_element_read, NULL); - DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "childNodes", dom_node_child_nodes_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "childNodes", dom_modern_node_child_nodes_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "firstChild", dom_node_first_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "lastChild", dom_node_last_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_node_prop_handlers, "previousSibling", dom_node_previous_sibling_read, NULL); @@ -1294,7 +1294,7 @@ PHP_MINIT_FUNCTION(dom) DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "firstChild", dom_entity_reference_child_read, NULL); DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "lastChild", dom_entity_reference_child_read, NULL); DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "textContent", dom_entity_reference_text_content_read, NULL); - DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "childNodes", dom_entity_reference_child_nodes_read, NULL); + DOM_OVERWRITE_PROP_HANDLER(&dom_modern_entity_reference_prop_handlers, "childNodes", dom_modern_entity_reference_child_nodes_read, NULL); zend_hash_add_new_ptr(&classes, dom_modern_entityreference_class_entry->name, &dom_modern_entity_reference_prop_handlers); dom_processinginstruction_class_entry = register_class_DOMProcessingInstruction(dom_node_class_entry); diff --git a/ext/dom/tests/modern/common/gh21097.phpt b/ext/dom/tests/modern/common/gh21097.phpt new file mode 100644 index 0000000000000..db6abd12fcf7e --- /dev/null +++ b/ext/dom/tests/modern/common/gh21097.phpt @@ -0,0 +1,49 @@ +--TEST-- +GH-21097 (Accessing Dom\Node properties can can throw TypeError(s)) +--EXTENSIONS-- +dom +--CREDITS-- +mbeccati +--FILE-- +createDocumentType('html', 'publicId', 'systemId'); + +$r = new \ReflectionClass($node); +foreach ($r->getProperties(\ReflectionProperty::IS_PUBLIC) as $p) { + echo $p->getName(), ": "; + var_dump($node->{$p->getName()}); +} + +?> +--EXPECT-- +nodeType: int(10) +nodeName: string(4) "html" +baseURI: string(11) "about:blank" +isConnected: bool(false) +ownerDocument: NULL +parentNode: NULL +parentElement: NULL +childNodes: object(Dom\NodeList)#24 (1) { + ["length"]=> + int(0) +} +firstChild: NULL +lastChild: NULL +previousSibling: NULL +nextSibling: NULL +nodeValue: NULL +textContent: string(0) "" +name: string(4) "html" +entities: object(Dom\DtdNamedNodeMap)#24 (1) { + ["length"]=> + int(0) +} +notations: object(Dom\DtdNamedNodeMap)#24 (1) { + ["length"]=> + int(0) +} +publicId: string(8) "publicId" +systemId: string(8) "systemId" +internalSubset: NULL