diff --git a/NEWS b/NEWS index fe91a1f30ef74..f3c92852e4db5 100644 --- a/NEWS +++ b/NEWS @@ -74,6 +74,7 @@ PHP NEWS - DOM: . Added Dom\Element::$outerHTML. (nielsdos) . Added Dom\Element::insertAdjacentHTML(). (nielsdos) + . Added $children property to ParentNode implementations. (nielsdos) - Enchant: . Added enchant_dict_remove_from_session(). (nielsdos) diff --git a/UPGRADING b/UPGRADING index 4d968ef4daf80..5491d85f529d8 100644 --- a/UPGRADING +++ b/UPGRADING @@ -185,6 +185,7 @@ PHP 8.5 UPGRADE NOTES - DOM: . Added Dom\Element::$outerHTML. + . Added $children property to Dom\ParentNode implementations. - EXIF: . Add OffsetTime* Exif tags. diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index f9402c929d937..100c6c3d3e78b 100644 --- a/ext/dom/dom_properties.h +++ b/ext/dom/dom_properties.h @@ -109,6 +109,7 @@ zend_result dom_entity_reference_child_nodes_read(dom_object *obj, zval *retval) zend_result dom_namednodemap_length_read(dom_object *obj, zval *retval); /* parent node properties */ +zend_result dom_parent_node_children_read(dom_object *obj, zval *retval); zend_result dom_parent_node_first_element_child_read(dom_object *obj, zval *retval); zend_result dom_parent_node_last_element_child_read(dom_object *obj, zval *retval); zend_result dom_parent_node_child_element_count(dom_object *obj, zval *retval); diff --git a/ext/dom/element.c b/ext/dom/element.c index 27e2845c9f0cb..ea18d0a817ede 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -177,18 +177,21 @@ zend_result dom_element_class_name_write(dom_object *obj, zval *newval) } /* }}} */ -zval *dom_element_class_list_zval(dom_object *obj) +zval *dom_get_prop_checked_offset(dom_object *obj, uint32_t offset, const char *name) { - const uint32_t PROP_INDEX = 0; - #if ZEND_DEBUG - zend_string *class_list_str = ZSTR_INIT_LITERAL("classList", false); - const zend_property_info *prop_info = zend_get_property_info(dom_modern_element_class_entry, class_list_str, 0); - zend_string_release_ex(class_list_str, false); - ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); + zend_string *name_zstr = ZSTR_INIT_LITERAL(name, false); + const zend_property_info *prop_info = zend_get_property_info(obj->std.ce, name_zstr, 0); + zend_string_release_ex(name_zstr, false); + ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == offset); #endif - return OBJ_PROP_NUM(&obj->std, PROP_INDEX); + return OBJ_PROP_NUM(&obj->std, offset); +} + +zval *dom_element_class_list_zval(dom_object *obj) +{ + return dom_get_prop_checked_offset(obj, 1, "classList"); } /* {{{ classList TokenList diff --git a/ext/dom/html_collection.c b/ext/dom/html_collection.c index e5dca84de75ff..ce56b77ecd958 100644 --- a/ext/dom/html_collection.c +++ b/ext/dom/html_collection.c @@ -50,12 +50,23 @@ static dom_named_item dom_html_collection_named_item(zend_string *key, zend_obje zend_long cur = 0; zend_long next = cur; /* not +1, otherwise we skip the first candidate */ xmlNodePtr candidate = basep->children; + bool iterate_tag_name = objmap->handler == &php_dom_obj_map_by_tag_name; while (candidate != NULL) { - candidate = dom_get_elements_by_tag_name_ns_raw(basep, candidate, objmap->ns, objmap->local, objmap->local_lower, &cur, next); - if (candidate == NULL) { - break; + if (iterate_tag_name) { + candidate = dom_get_elements_by_tag_name_ns_raw(basep, candidate, objmap->ns, objmap->local, objmap->local_lower, &cur, next); + if (candidate == NULL) { + break; + } + next = cur + 1; + } else { + if (candidate->type != XML_ELEMENT_NODE) { + candidate = candidate->next; + continue; + } } + ZEND_ASSERT(candidate->type == XML_ELEMENT_NODE); + xmlAttrPtr attr; /* it has an ID which is key; */ @@ -73,7 +84,9 @@ static dom_named_item dom_html_collection_named_item(zend_string *key, zend_obje } } - next = cur + 1; + if (!iterate_tag_name) { + candidate = candidate->next; + } } } @@ -141,4 +154,23 @@ int dom_html_collection_has_dimension(zend_object *object, zval *member, int che } } +HashTable *dom_html_collection_get_gc(zend_object *object, zval **table, int *n) +{ + dom_nnodemap_object *objmap = php_dom_obj_from_obj(object)->ptr; + + if (objmap->baseobj) { + zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); + zend_get_gc_buffer_add_obj(gc_buffer, &objmap->baseobj->std); + zend_get_gc_buffer_use(gc_buffer, table, n); + + if (object->properties == NULL && object->ce->default_properties_count == 0) { + return NULL; + } else { + return zend_std_get_properties(object); + } + } else { + return zend_std_get_gc(object, table, n); + } +} + #endif diff --git a/ext/dom/html_collection.h b/ext/dom/html_collection.h index a94daa1aae805..59ab50abc1b05 100644 --- a/ext/dom/html_collection.h +++ b/ext/dom/html_collection.h @@ -19,5 +19,6 @@ zval *dom_html_collection_read_dimension(zend_object *object, zval *offset, int type, zval *rv); int dom_html_collection_has_dimension(zend_object *object, zval *member, int check_empty); +HashTable *dom_html_collection_get_gc(zend_object *object, zval **table, int *n); #endif diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c index 19d7faef0dbb2..954403ca1c5df 100644 --- a/ext/dom/html_document.c +++ b/ext/dom/html_document.c @@ -84,16 +84,7 @@ typedef struct dom_decoding_encoding_ctx { /* https://dom.spec.whatwg.org/#dom-document-implementation */ zend_result dom_modern_document_implementation_read(dom_object *obj, zval *retval) { - const uint32_t PROP_INDEX = 0; - -#if ZEND_DEBUG - zend_string *implementation_str = ZSTR_INIT_LITERAL("implementation", false); - const zend_property_info *prop_info = zend_get_property_info(dom_abstract_base_document_class_entry, implementation_str, 0); - zend_string_release_ex(implementation_str, false); - ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); -#endif - - zval *cached_implementation = OBJ_PROP_NUM(&obj->std, PROP_INDEX); + zval *cached_implementation = dom_get_prop_checked_offset(obj, 1, "implementation"); if (Z_ISUNDEF_P(cached_implementation)) { php_dom_create_implementation(cached_implementation, true); } diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c index fe71a6f47a38a..367171c43007e 100644 --- a/ext/dom/obj_map.c +++ b/ext/dom/obj_map.c @@ -28,10 +28,7 @@ static zend_always_inline void objmap_cache_release_cached_obj(dom_nnodemap_object *objmap) { if (objmap->cached_obj) { - /* Since the DOM is a tree there can be no cycles. */ - if (GC_DELREF(&objmap->cached_obj->std) == 0) { - zend_objects_store_del(&objmap->cached_obj->std); - } + OBJ_RELEASE(&objmap->cached_obj->std); objmap->cached_obj = NULL; objmap->cached_obj_index = 0; } @@ -82,6 +79,20 @@ static zend_long dom_map_get_nodes_length(dom_nnodemap_object *map) return count; } +static zend_long dom_map_get_elements_length(dom_nnodemap_object *map) +{ + zend_long count = 0; + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + if (nodep) { + for (xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep); curnode; curnode = curnode->next) { + if (curnode->type == XML_ELEMENT_NODE) { + count++; + } + } + } + return count; +} + static zend_long dom_map_get_by_tag_name_length(dom_nnodemap_object *map) { xmlNodePtr nodep = dom_object_get_node(map->baseobj); @@ -223,6 +234,38 @@ static void dom_map_get_nodes_item(dom_nnodemap_object *map, zend_long index, zv } } +static void dom_map_get_elements_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + xmlNodePtr itemnode = NULL; + if (nodep && index >= 0) { + dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index); + if (start_point.node) { + /* Guaranteed to be an element */ + itemnode = start_point.node; + } else { + /* Fetch first element child */ + itemnode = nodep->children; + while (itemnode && itemnode->type != XML_ELEMENT_NODE) { + itemnode = itemnode->next; + } + } + + for (; start_point.index > 0 && itemnode; --start_point.index) { + do { + itemnode = itemnode->next; + } while (itemnode && itemnode->type != XML_ELEMENT_NODE); + } + if (itemnode && itemnode->type != XML_ELEMENT_NODE) { + itemnode = NULL; + } + } + dom_ret_node_to_zobj(map, itemnode, return_value); + if (itemnode) { + dom_map_cache_obj(map, itemnode, index, return_value); + } +} + static void dom_map_get_by_tag_name_item(dom_nnodemap_object *map, zend_long index, zval *return_value) { xmlNodePtr nodep = dom_object_get_node(map->baseobj); @@ -456,6 +499,15 @@ const php_dom_obj_map_handler php_dom_obj_map_notations = { .nameless = false, }; +const php_dom_obj_map_handler php_dom_obj_map_child_elements = { + .length = dom_map_get_elements_length, + .get_item = dom_map_get_elements_item, + .get_named_item = dom_map_get_named_item_null, + .has_named_item = dom_map_has_named_item_null, + .use_cache = true, + .nameless = true, +}; + const php_dom_obj_map_handler php_dom_obj_map_noop = { .length = dom_map_get_zero_length, .get_item = dom_map_get_null_item, diff --git a/ext/dom/obj_map.h b/ext/dom/obj_map.h index e1de9addd98f2..dc2b33bd24fec 100644 --- a/ext/dom/obj_map.h +++ b/ext/dom/obj_map.h @@ -57,6 +57,7 @@ zend_long php_dom_get_nodelist_length(dom_object *obj); extern const php_dom_obj_map_handler php_dom_obj_map_attributes; extern const php_dom_obj_map_handler php_dom_obj_map_by_tag_name; +extern const php_dom_obj_map_handler php_dom_obj_map_child_elements; extern const php_dom_obj_map_handler php_dom_obj_map_child_nodes; extern const php_dom_obj_map_handler php_dom_obj_map_nodeset; extern const php_dom_obj_map_handler php_dom_obj_map_entities; diff --git a/ext/dom/parentnode/tree.c b/ext/dom/parentnode/tree.c index f57fd1cc7335b..c51bd2753cd8d 100644 --- a/ext/dom/parentnode/tree.c +++ b/ext/dom/parentnode/tree.c @@ -22,9 +22,34 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "../php_dom.h" +#include "../obj_map.h" #include "../internal_helpers.h" #include "../dom_properties.h" +zval *dom_parent_node_children(dom_object *obj) +{ + return dom_get_prop_checked_offset(obj, 0, "children"); +} + +zend_result dom_parent_node_children_read(dom_object *obj, zval *retval) +{ + zval *cached_children = dom_parent_node_children(obj); + if (Z_ISUNDEF_P(cached_children)) { + object_init_ex(cached_children, dom_html_collection_class_entry); + php_dom_create_obj_map(obj, Z_DOMOBJ_P(cached_children), NULL, NULL, NULL, &php_dom_obj_map_child_elements); + + /* Handle cycles for potential TMPVARs (could also be CV but we can't differentiate). + * RC == 2 because of 1 TMPVAR and 1 in HTMLCollection. */ + if (GC_REFCOUNT(&obj->std) == 2) { + gc_possible_root(Z_COUNTED_P(cached_children)); + } + } + + ZVAL_OBJ_COPY(retval, Z_OBJ_P(cached_children)); + + return SUCCESS; +} + /* {{{ firstElementChild DomParentNode readonly=yes URL: https://www.w3.org/TR/dom/#dom-parentnode-firstelementchild diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index f1571ca78c4f2..5df8a1cb1e1ce 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -96,6 +96,7 @@ PHP_DOM_EXPORT zend_class_entry *dom_namespace_info_class_entry; static zend_object_handlers dom_object_handlers; static zend_object_handlers dom_nnodemap_object_handlers; static zend_object_handlers dom_nodelist_object_handlers; +static zend_object_handlers dom_unset_children_property_object_handlers; static zend_object_handlers dom_modern_nnodemap_object_handlers; static zend_object_handlers dom_modern_nodelist_object_handlers; static zend_object_handlers dom_html_collection_object_handlers; @@ -668,14 +669,35 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ static zend_object *dom_modern_element_clone_obj(zend_object *zobject) { zend_object *clone = dom_objects_store_clone_obj(zobject); + dom_object *intern = php_dom_obj_from_obj(clone); /* The $classList property is unique per element, and cached due to its [[SameObject]] requirement. * Remove it from the clone so the clone will get a fresh instance upon demand. */ - zval *class_list = dom_element_class_list_zval(php_dom_obj_from_obj(clone)); + zval *class_list = dom_element_class_list_zval(intern); if (!Z_ISUNDEF_P(class_list)) { zval_ptr_dtor(class_list); ZVAL_UNDEF(class_list); } + /* Likewise for $children */ + zval *children = dom_parent_node_children(intern); + if (!Z_ISUNDEF_P(children)) { + zval_ptr_dtor(children); + ZVAL_UNDEF(children); + } + + return clone; +} + +static zend_object *dom_clone_obj_unset_children_property(zend_object *zobject) +{ + zend_object *clone = dom_objects_store_clone_obj(zobject); + dom_object *intern = php_dom_obj_from_obj(clone); + + zval *children = dom_parent_node_children(intern); + if (!Z_ISUNDEF_P(children)) { + zval_ptr_dtor(children); + ZVAL_UNDEF(children); + } return clone; } @@ -777,6 +799,9 @@ PHP_MINIT_FUNCTION(dom) memcpy(&dom_modern_element_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_modern_element_object_handlers.clone_obj = dom_modern_element_clone_obj; + memcpy(&dom_unset_children_property_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); + dom_unset_children_property_object_handlers.clone_obj = dom_clone_obj_unset_children_property; + memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage; dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension; @@ -797,6 +822,8 @@ PHP_MINIT_FUNCTION(dom) memcpy(&dom_html_collection_object_handlers, &dom_modern_nodelist_object_handlers, sizeof(zend_object_handlers)); dom_html_collection_object_handlers.read_dimension = dom_html_collection_read_dimension; dom_html_collection_object_handlers.has_dimension = dom_html_collection_has_dimension; + dom_html_collection_object_handlers.get_gc = dom_html_collection_get_gc; + dom_html_collection_object_handlers.clone_obj = NULL; memcpy(&dom_object_namespace_node_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_object_namespace_node_handlers.offset = XtOffsetOf(dom_object_namespace_node, dom.std); @@ -911,9 +938,10 @@ PHP_MINIT_FUNCTION(dom) dom_modern_documentfragment_class_entry = register_class_Dom_DocumentFragment(dom_modern_node_class_entry, dom_modern_parentnode_class_entry); dom_modern_documentfragment_class_entry->create_object = dom_objects_new; - dom_modern_documentfragment_class_entry->default_object_handlers = &dom_object_handlers; + dom_modern_documentfragment_class_entry->default_object_handlers = &dom_unset_children_property_object_handlers; zend_hash_init(&dom_modern_documentfragment_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "children", dom_parent_node_children_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); @@ -922,7 +950,7 @@ PHP_MINIT_FUNCTION(dom) zend_hash_add_new_ptr(&classes, dom_modern_documentfragment_class_entry->name, &dom_modern_documentfragment_prop_handlers); dom_abstract_base_document_class_entry = register_class_Dom_Document(dom_modern_node_class_entry, dom_modern_parentnode_class_entry); - dom_abstract_base_document_class_entry->default_object_handlers = &dom_object_handlers; + dom_abstract_base_document_class_entry->default_object_handlers = &dom_unset_children_property_object_handlers; zend_hash_init(&dom_abstract_base_document_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "implementation", dom_modern_document_implementation_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "URL", dom_document_document_uri_read, dom_document_document_uri_write); @@ -932,6 +960,7 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "inputEncoding", dom_document_encoding_read, dom_html_document_encoding_write); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "doctype", dom_document_doctype_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "children", dom_parent_node_children_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); @@ -1118,6 +1147,7 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "classList", dom_element_class_list_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "attributes", dom_node_attributes_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "children", dom_parent_node_children_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); @@ -1534,8 +1564,8 @@ void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */ dom_nnodemap_object *objmap = (dom_nnodemap_object *)intern->ptr; if (objmap) { - if (objmap->cached_obj && GC_DELREF(&objmap->cached_obj->std) == 0) { - zend_objects_store_del(&objmap->cached_obj->std); + if (objmap->cached_obj) { + OBJ_RELEASE(&objmap->cached_obj->std); } if (objmap->release_local) { dom_zend_string_release_from_char_pointer(objmap->local); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 0ff8692c4cc74..34cc4af85c568 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -160,7 +160,11 @@ bool php_dom_create_nullable_object(xmlNodePtr obj, zval *return_value, dom_obje xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive); void dom_set_document_ref_pointers(xmlNodePtr node, php_libxml_ref_obj *document); void dom_set_document_ref_pointers_attr(xmlAttrPtr attr, php_libxml_ref_obj *document); + +/* Prop getters by offset */ +zval *dom_get_prop_checked_offset(dom_object *obj, uint32_t offset, const char *name); zval *dom_element_class_list_zval(dom_object *obj); +zval *dom_parent_node_children(dom_object *obj); typedef enum { DOM_LOAD_STRING = 0, diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index 686ebe87b71ca..fd0927fc0ea12 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -1584,6 +1584,36 @@ class Element extends Node implements ParentNode, ChildNode */ public string $tagName; + /** + * @readonly + */ + public HTMLCollection $children; + /** + * @readonly + * @virtual + */ + public ?Element $firstElementChild; + /** + * @readonly + * @virtual + */ + public ?Element $lastElementChild; + /** + * @readonly + * @virtual + */ + public int $childElementCount; + /** + * @readonly + * @virtual + */ + public ?Element $previousElementSibling; + /** + * @readonly + * @virtual + */ + public ?Element $nextElementSibling; + /** @virtual */ public string $id; /** @virtual */ @@ -1634,32 +1664,6 @@ public function insertAdjacentElement(AdjacentPosition $where, Element $element) public function insertAdjacentText(AdjacentPosition $where, string $data): void {} public function insertAdjacentHTML(AdjacentPosition $where, string $string): void {} - /** - * @readonly - * @virtual - */ - public ?Element $firstElementChild; - /** - * @readonly - * @virtual - */ - public ?Element $lastElementChild; - /** - * @readonly - * @virtual - */ - public int $childElementCount; - /** - * @readonly - * @virtual - */ - public ?Element $previousElementSibling; - /** - * @readonly - * @virtual - */ - public ?Element $nextElementSibling; - /** @implementation-alias DOMElement::setIdAttribute */ public function setIdAttribute(string $qualifiedName, bool $isId): void {} /** @implementation-alias DOMElement::setIdAttributeNS */ @@ -1863,6 +1867,10 @@ public function replaceWith(Node|string ...$nodes): void {} class DocumentFragment extends Node implements ParentNode { + /** + * @readonly + */ + public HTMLCollection $children; /** * @readonly * @virtual @@ -1931,6 +1939,26 @@ class Notation extends Node abstract class Document extends Node implements ParentNode { + /** + * @readonly + */ + public HTMLCollection $children; + /** + * @readonly + * @virtual + */ + public ?Element $firstElementChild; + /** + * @readonly + * @virtual + */ + public ?Element $lastElementChild; + /** + * @readonly + * @virtual + */ + public int $childElementCount; + /** @readonly */ public Implementation $implementation; /** @virtual */ @@ -1979,22 +2007,6 @@ public function createAttribute(string $localName): Attr {} /** @implementation-alias DOMDocument::createAttributeNS */ public function createAttributeNS(?string $namespace, string $qualifiedName): Attr {} - /** - * @readonly - * @virtual - */ - public ?Element $firstElementChild; - /** - * @readonly - * @virtual - */ - public ?Element $lastElementChild; - /** - * @readonly - * @virtual - */ - public int $childElementCount; - /** @implementation-alias DOMDocument::getElementById */ public function getElementById(string $elementId): ?Element {} diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index cf73a85226e4f..922f03240e0f8 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1c8b81daeaf360b0ecab9ebbdf4f8865f521f43d */ + * Stub hash: 2119512797f6d51d9835660cd0eccd3ba83417a9 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_dom_import_simplexml, 0, 1, DOMAttr|DOMElement, 0) ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0) @@ -3025,31 +3025,12 @@ static zend_class_entry *register_class_Dom_Element(zend_class_entry *class_entr zend_declare_typed_property(class_entry, property_tagName_name, &property_tagName_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release(property_tagName_name); - zval property_id_default_value; - ZVAL_UNDEF(&property_id_default_value); - zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, 1); - zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_id_name); - - zval property_className_default_value; - ZVAL_UNDEF(&property_className_default_value); - zend_string *property_className_name = zend_string_init("className", sizeof("className") - 1, 1); - zend_declare_typed_property(class_entry, property_className_name, &property_className_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_className_name); - - zval property_classList_default_value; - ZVAL_UNDEF(&property_classList_default_value); - zend_string *property_classList_name = zend_string_init("classList", sizeof("classList") - 1, 1); - zend_string *property_classList_class_Dom_TokenList = zend_string_init("Dom\\TokenList", sizeof("Dom\\TokenList")-1, 1); - zend_declare_typed_property(class_entry, property_classList_name, &property_classList_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_classList_class_Dom_TokenList, 0, 0)); - zend_string_release(property_classList_name); - - zval property_attributes_default_value; - ZVAL_UNDEF(&property_attributes_default_value); - zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); - zend_string *property_attributes_class_Dom_NamedNodeMap = zend_string_init("Dom\\\116amedNodeMap", sizeof("Dom\\\116amedNodeMap")-1, 1); - zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_attributes_class_Dom_NamedNodeMap, 0, 0)); - zend_string_release(property_attributes_name); + zval property_children_default_value; + ZVAL_UNDEF(&property_children_default_value); + zend_string *property_children_name = zend_string_init("children", sizeof("children") - 1, 1); + zend_string *property_children_class_Dom_HTMLCollection = zend_string_init("Dom\\HTMLCollection", sizeof("Dom\\HTMLCollection")-1, 1); + zend_declare_typed_property(class_entry, property_children_name, &property_children_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_children_class_Dom_HTMLCollection, 0, 0)); + zend_string_release(property_children_name); zval property_firstElementChild_default_value; ZVAL_UNDEF(&property_firstElementChild_default_value); @@ -3085,6 +3066,32 @@ static zend_class_entry *register_class_Dom_Element(zend_class_entry *class_entr zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextElementSibling_class_Dom_Element, 0, MAY_BE_NULL)); zend_string_release(property_nextElementSibling_name); + zval property_id_default_value; + ZVAL_UNDEF(&property_id_default_value); + zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, 1); + zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_id_name); + + zval property_className_default_value; + ZVAL_UNDEF(&property_className_default_value); + zend_string *property_className_name = zend_string_init("className", sizeof("className") - 1, 1); + zend_declare_typed_property(class_entry, property_className_name, &property_className_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_className_name); + + zval property_classList_default_value; + ZVAL_UNDEF(&property_classList_default_value); + zend_string *property_classList_name = zend_string_init("classList", sizeof("classList") - 1, 1); + zend_string *property_classList_class_Dom_TokenList = zend_string_init("Dom\\TokenList", sizeof("Dom\\TokenList")-1, 1); + zend_declare_typed_property(class_entry, property_classList_name, &property_classList_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_classList_class_Dom_TokenList, 0, 0)); + zend_string_release(property_classList_name); + + zval property_attributes_default_value; + ZVAL_UNDEF(&property_attributes_default_value); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); + zend_string *property_attributes_class_Dom_NamedNodeMap = zend_string_init("Dom\\\116amedNodeMap", sizeof("Dom\\\116amedNodeMap")-1, 1); + zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_attributes_class_Dom_NamedNodeMap, 0, 0)); + zend_string_release(property_attributes_name); + zval property_innerHTML_default_value; ZVAL_UNDEF(&property_innerHTML_default_value); zend_string *property_innerHTML_name = zend_string_init("innerHTML", sizeof("innerHTML") - 1, 1); @@ -3309,6 +3316,13 @@ static zend_class_entry *register_class_Dom_DocumentFragment(zend_class_entry *c class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Dom_Node, 0); zend_class_implements(class_entry, 1, class_entry_Dom_ParentNode); + zval property_children_default_value; + ZVAL_UNDEF(&property_children_default_value); + zend_string *property_children_name = zend_string_init("children", sizeof("children") - 1, 1); + zend_string *property_children_class_Dom_HTMLCollection = zend_string_init("Dom\\HTMLCollection", sizeof("Dom\\HTMLCollection")-1, 1); + zend_declare_typed_property(class_entry, property_children_name, &property_children_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_children_class_Dom_HTMLCollection, 0, 0)); + zend_string_release(property_children_name); + zval property_firstElementChild_default_value; ZVAL_UNDEF(&property_firstElementChild_default_value); zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); @@ -3400,6 +3414,33 @@ static zend_class_entry *register_class_Dom_Document(zend_class_entry *class_ent class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Dom_Node, ZEND_ACC_ABSTRACT); zend_class_implements(class_entry, 1, class_entry_Dom_ParentNode); + zval property_children_default_value; + ZVAL_UNDEF(&property_children_default_value); + zend_string *property_children_name = zend_string_init("children", sizeof("children") - 1, 1); + zend_string *property_children_class_Dom_HTMLCollection = zend_string_init("Dom\\HTMLCollection", sizeof("Dom\\HTMLCollection")-1, 1); + zend_declare_typed_property(class_entry, property_children_name, &property_children_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_children_class_Dom_HTMLCollection, 0, 0)); + zend_string_release(property_children_name); + + zval property_firstElementChild_default_value; + ZVAL_UNDEF(&property_firstElementChild_default_value); + zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); + zend_string *property_firstElementChild_class_Dom_Element = zend_string_init("Dom\\Element", sizeof("Dom\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_Dom_Element, 0, MAY_BE_NULL)); + zend_string_release(property_firstElementChild_name); + + zval property_lastElementChild_default_value; + ZVAL_UNDEF(&property_lastElementChild_default_value); + zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); + zend_string *property_lastElementChild_class_Dom_Element = zend_string_init("Dom\\Element", sizeof("Dom\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_Dom_Element, 0, MAY_BE_NULL)); + zend_string_release(property_lastElementChild_name); + + zval property_childElementCount_default_value; + ZVAL_UNDEF(&property_childElementCount_default_value); + zend_string *property_childElementCount_name = zend_string_init("childElementCount", sizeof("childElementCount") - 1, 1); + zend_declare_typed_property(class_entry, property_childElementCount_name, &property_childElementCount_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_childElementCount_name); + zval property_implementation_default_value; ZVAL_UNDEF(&property_implementation_default_value); zend_string *property_implementation_name = zend_string_init("implementation", sizeof("implementation") - 1, 1); @@ -3451,26 +3492,6 @@ static zend_class_entry *register_class_Dom_Document(zend_class_entry *class_ent zend_declare_typed_property(class_entry, property_documentElement_name, &property_documentElement_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_documentElement_class_Dom_Element, 0, MAY_BE_NULL)); zend_string_release(property_documentElement_name); - zval property_firstElementChild_default_value; - ZVAL_UNDEF(&property_firstElementChild_default_value); - zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); - zend_string *property_firstElementChild_class_Dom_Element = zend_string_init("Dom\\Element", sizeof("Dom\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_Dom_Element, 0, MAY_BE_NULL)); - zend_string_release(property_firstElementChild_name); - - zval property_lastElementChild_default_value; - ZVAL_UNDEF(&property_lastElementChild_default_value); - zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); - zend_string *property_lastElementChild_class_Dom_Element = zend_string_init("Dom\\Element", sizeof("Dom\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_Dom_Element, 0, MAY_BE_NULL)); - zend_string_release(property_lastElementChild_name); - - zval property_childElementCount_default_value; - ZVAL_UNDEF(&property_childElementCount_default_value); - zend_string *property_childElementCount_name = zend_string_init("childElementCount", sizeof("childElementCount") - 1, 1); - zend_declare_typed_property(class_entry, property_childElementCount_name, &property_childElementCount_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(property_childElementCount_name); - zval property_body_default_value; ZVAL_UNDEF(&property_body_default_value); zend_string *property_body_name = zend_string_init("body", sizeof("body") - 1, 1); diff --git a/ext/dom/tests/gh15192.phpt b/ext/dom/tests/gh15192.phpt index c7bf0a543bb93..f6031c2a40b02 100644 --- a/ext/dom/tests/gh15192.phpt +++ b/ext/dom/tests/gh15192.phpt @@ -11,7 +11,7 @@ $dom = new DomDocument(); var_dump($element); ?> --EXPECT-- -object(Dom\HTMLElement)#3 (30) { +object(Dom\HTMLElement)#3 (31) { ["namespaceURI"]=> string(28) "http://www.w3.org/1999/xhtml" ["prefix"]=> @@ -28,6 +28,8 @@ object(Dom\HTMLElement)#3 (30) { string(22) "(object value omitted)" ["attributes"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> diff --git a/ext/dom/tests/gh16356.phpt b/ext/dom/tests/gh16356.phpt index ad09c2681806e..53d90d8490e29 100644 --- a/ext/dom/tests/gh16356.phpt +++ b/ext/dom/tests/gh16356.phpt @@ -13,7 +13,7 @@ var_dump($e1, $e2); ?> --EXPECT-- -object(Dom\Element)#3 (30) { +object(Dom\Element)#3 (31) { ["namespaceURI"]=> string(12) "urn:example1" ["prefix"]=> @@ -30,6 +30,8 @@ object(Dom\Element)#3 (30) { string(22) "(object value omitted)" ["attributes"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> @@ -75,7 +77,7 @@ object(Dom\Element)#3 (30) { ["textContent"]=> string(0) "" } -object(Dom\Element)#4 (30) { +object(Dom\Element)#4 (31) { ["namespaceURI"]=> string(12) "urn:example2" ["prefix"]=> @@ -92,6 +94,8 @@ object(Dom\Element)#4 (30) { string(22) "(object value omitted)" ["attributes"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/common/ParentNode_children.phpt b/ext/dom/tests/modern/common/ParentNode_children.phpt new file mode 100644 index 0000000000000..54db132f1b161 --- /dev/null +++ b/ext/dom/tests/modern/common/ParentNode_children.phpt @@ -0,0 +1,48 @@ +--TEST-- +ParentNode::$children +--EXTENSIONS-- +dom +--FILE-- + + + + + + + +XML); +$children = $dom->documentElement->children; +var_dump($children === $dom->documentElement->children); // Tests caching behaviour +var_dump($children !== (clone $dom->documentElement)->children); // Tests caching behaviour does not persist across clones +var_dump(count($children)); +var_dump($children->length); + +foreach ($children as $key => $child) { + var_dump($key, $child->nodeName); +} + +foreach ($children->namedItem('foo')->children as $key => $child) { + var_dump($key, $child->nodeName); +} + +?> +--EXPECT-- +bool(true) +bool(true) +int(4) +int(4) +int(0) +string(1) "a" +int(1) +string(1) "b" +int(2) +string(1) "c" +int(3) +string(1) "e" +int(0) +string(2) "c1" +int(1) +string(2) "c2" diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt index 5051c3f9aabf6..c6347ae485894 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt @@ -23,7 +23,7 @@ var_dump(get_class($dom->getElementsByTagName("p")->item(0))); ?> --EXPECT-- -object(Dom\HTMLDocument)#1 (28) { +object(Dom\HTMLDocument)#1 (29) { ["implementation"]=> string(22) "(object value omitted)" ["URL"]=> @@ -40,6 +40,8 @@ object(Dom\HTMLDocument)#1 (28) { NULL ["documentElement"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt index b160c72f0a54f..d7dea308b5d2a 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt @@ -23,7 +23,7 @@ var_dump(get_class($dom->getElementsByTagName("p")->item(0))); ?> --EXPECT-- -object(Dom\HTMLDocument)#1 (28) { +object(Dom\HTMLDocument)#1 (29) { ["implementation"]=> string(22) "(object value omitted)" ["URL"]=> @@ -40,6 +40,8 @@ object(Dom\HTMLDocument)#1 (28) { NULL ["documentElement"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt b/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt index ae49a6a494c9a..4cf1b3888feed 100644 --- a/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt +++ b/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt @@ -37,7 +37,7 @@ echo $dom->implementation->createDocument(null, "", $dtd)->saveXml(), "\n"; ?> --EXPECT-- --- (null, "") --- -object(Dom\XMLDocument)#3 (32) { +object(Dom\XMLDocument)#3 (33) { ["xmlEncoding"]=> string(5) "UTF-8" ["xmlStandalone"]=> @@ -62,6 +62,8 @@ object(Dom\XMLDocument)#3 (32) { NULL ["documentElement"]=> NULL + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt b/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt index 67a964a7ec515..a88b4fc808b08 100644 --- a/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt +++ b/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt @@ -18,7 +18,9 @@ var_dump($element->parentNode); ?> --EXPECT-- Exception: Cannot have more than one element child in a document -object(Dom\DocumentFragment)#2 (17) { +object(Dom\DocumentFragment)#2 (18) { + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/xml/XMLDocument_debug.phpt b/ext/dom/tests/modern/xml/XMLDocument_debug.phpt index e2d6ebffe89cd..7067e5607bdb6 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_debug.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_debug.phpt @@ -10,7 +10,7 @@ var_dump($dom); ?> --EXPECT-- -object(Dom\XMLDocument)#1 (32) { +object(Dom\XMLDocument)#1 (33) { ["xmlEncoding"]=> string(5) "UTF-8" ["xmlStandalone"]=> @@ -35,6 +35,8 @@ object(Dom\XMLDocument)#1 (32) { NULL ["documentElement"]=> NULL + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt index 62d64a05f9b2a..6276d1e9a98cb 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt @@ -10,7 +10,7 @@ var_dump($dom); ?> --EXPECT-- -object(Dom\XMLDocument)#1 (32) { +object(Dom\XMLDocument)#1 (33) { ["xmlEncoding"]=> string(5) "UTF-8" ["xmlStandalone"]=> @@ -35,6 +35,8 @@ object(Dom\XMLDocument)#1 (32) { NULL ["documentElement"]=> NULL + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt b/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt index e18c43f05ae82..89cfe83de710f 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt @@ -13,7 +13,7 @@ var_dump($element->ownerDocument); ?> --EXPECTF-- -object(Dom\XMLDocument)#1 (32) { +object(Dom\XMLDocument)#1 (33) { ["xmlEncoding"]=> string(5) "UTF-8" ["xmlStandalone"]=> @@ -38,6 +38,8 @@ object(Dom\XMLDocument)#1 (32) { NULL ["documentElement"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=>