Skip to content

Refactor implementation of DOM nodelists, named maps, and iterators #18892

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ext/dom/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ if test "$PHP_DOM" != "no"; then
node.c
nodelist.c
notation.c
obj_map.c
parentnode/css_selectors.c
parentnode/tree.c
php_dom.c
Expand Down
2 changes: 1 addition & 1 deletion ext/dom/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ if (PHP_DOM == "yes") {
entity.c nodelist.c html_collection.c text.c comment.c \
entityreference.c \
token_list.c \
notation.c xpath.c dom_iterators.c \
notation.c obj_map.c xpath.c dom_iterators.c \
namednodemap.c xpath_callbacks.c", null, "/I ext/lexbor");

ADD_EXTENSION_DEP('dom', 'lexbor');
Expand Down
4 changes: 2 additions & 2 deletions ext/dom/documenttype.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ zend_result dom_documenttype_entities_read(dom_object *obj, zval *retval)
xmlHashTable *entityht = (xmlHashTable *) dtdptr->entities;

dom_object *intern = Z_DOMOBJ_P(retval);
dom_namednode_iter(obj, XML_ENTITY_NODE, intern, entityht, NULL, NULL);
dom_namednode_iter(obj, intern, entityht, NULL, NULL, &php_dom_obj_map_entities);

return SUCCESS;
}
Expand All @@ -74,7 +74,7 @@ zend_result dom_documenttype_notations_read(dom_object *obj, zval *retval)
xmlHashTable *notationht = (xmlHashTable *) dtdptr->notations;

dom_object *intern = Z_DOMOBJ_P(retval);
dom_namednode_iter(obj, XML_NOTATION_NODE, intern, notationht, NULL, NULL);
dom_namednode_iter(obj, intern, notationht, NULL, NULL, &php_dom_obj_map_notations);

return SUCCESS;
}
Expand Down
137 changes: 21 additions & 116 deletions ext/dom/dom_iterators.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const
}
/* }}} */

static xmlNode *php_dom_libxml_hash_iter_ex(xmlHashTable *ht, int index)
xmlNodePtr php_dom_libxml_hash_iter(xmlHashTable *ht, int index)
{
int htsize;

Expand All @@ -84,18 +84,6 @@ static xmlNode *php_dom_libxml_hash_iter_ex(xmlHashTable *ht, int index)
}
}

xmlNode *php_dom_libxml_hash_iter(dom_nnodemap_object *objmap, int index)
{
xmlNode *curnode = php_dom_libxml_hash_iter_ex(objmap->ht, index);

if (curnode != NULL && objmap->nodetype != XML_ENTITY_NODE) {
xmlNotation *notation = (xmlNotation *) curnode;
curnode = create_notation(notation->name, notation->PublicID, notation->SystemID);
}

return curnode;
}

static void php_dom_iterator_dtor(zend_object_iterator *iter) /* {{{ */
{
php_dom_iterator *iterator = (php_dom_iterator *)iter;
Expand All @@ -109,7 +97,7 @@ static zend_result php_dom_iterator_valid(zend_object_iterator *iter) /* {{{ */
{
php_dom_iterator *iterator = (php_dom_iterator *)iter;

if (Z_TYPE(iterator->curobj) != IS_UNDEF) {
if (!Z_ISNULL(iterator->curobj)) {
return SUCCESS;
} else {
return FAILURE;
Expand All @@ -120,7 +108,7 @@ static zend_result php_dom_iterator_valid(zend_object_iterator *iter) /* {{{ */
zval *php_dom_iterator_current_data(zend_object_iterator *iter) /* {{{ */
{
php_dom_iterator *iterator = (php_dom_iterator *)iter;
return Z_ISUNDEF(iterator->curobj) ? NULL : &iterator->curobj;
return Z_ISNULL(iterator->curobj) ? NULL : &iterator->curobj;
}
/* }}} */

Expand All @@ -131,14 +119,14 @@ static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key)

/* Only dtd named node maps, i.e. the ones based on a libxml hash table or attribute collections,
* are keyed by the name because in that case the name is unique. */
if (!objmap->ht && objmap->nodetype != XML_ATTRIBUTE_NODE) {
if (objmap->handler->nameless) {
ZVAL_LONG(key, iterator->index);
} else {
dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);

if (intern != NULL && intern->ptr != NULL) {
if (intern->ptr != NULL) {
xmlNodePtr curnode = ((php_libxml_node_ptr *)intern->ptr)->node;
if (objmap->nodetype == XML_ATTRIBUTE_NODE && php_dom_follow_spec_intern(intern)) {
if (curnode->type == XML_ATTRIBUTE_NODE && php_dom_follow_spec_intern(intern)) {
ZVAL_NEW_STR(key, dom_node_get_node_name_attribute_or_element(curnode, false));
} else {
ZVAL_STRINGL(key, (const char *) curnode->name, xmlStrlen(curnode->name));
Expand All @@ -150,99 +138,36 @@ static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key)
}
/* }}} */

static xmlNodePtr dom_fetch_first_iteration_item(dom_nnodemap_object *objmap)
{
xmlNodePtr basep = dom_object_get_node(objmap->baseobj);
if (!basep) {
return NULL;
}
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
if (objmap->nodetype == XML_ATTRIBUTE_NODE) {
return (xmlNodePtr) basep->properties;
} else {
return dom_nodelist_iter_start_first_child(basep);
}
} else {
zend_long curindex = 0;
xmlNodePtr nodep = php_dom_first_child_of_container_node(basep);
return dom_get_elements_by_tag_name_ns_raw(
basep, nodep, objmap->ns, objmap->local, objmap->local_lower, &curindex, 0);
}
}

static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
{
xmlNodePtr curnode = NULL;

php_dom_iterator *iterator = (php_dom_iterator *)iter;
if (Z_ISUNDEF(iterator->curobj)) {
if (Z_ISNULL(iterator->curobj)) {
return;
}

iterator->index++;
zval garbage;
ZVAL_COPY_VALUE(&garbage, &iterator->curobj);
ZVAL_NULL(&iterator->curobj);

dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);
dom_nnodemap_object *objmap = php_dom_iterator_get_nnmap(iterator);

if (intern != NULL && intern->ptr != NULL) {
if (objmap->nodetype != XML_ENTITY_NODE &&
objmap->nodetype != XML_NOTATION_NODE) {
if (objmap->nodetype == DOM_NODESET) {
HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv);
zval *entry = zend_hash_index_find(nodeht, iterator->index);
if (entry) {
zval_ptr_dtor(&iterator->curobj);
ZVAL_COPY(&iterator->curobj, entry);
return;
}
} else {
if (objmap->nodetype == XML_ATTRIBUTE_NODE ||
objmap->nodetype == XML_ELEMENT_NODE) {

/* Note: keep legacy behaviour for non-spec mode. */
if (php_dom_follow_spec_intern(intern) && php_dom_is_cache_tag_stale_from_doc_ptr(&iterator->cache_tag, intern->document)) {
php_dom_mark_cache_tag_up_to_date_from_doc_ref(&iterator->cache_tag, intern->document);
curnode = dom_fetch_first_iteration_item(objmap);
zend_ulong index = 0;
while (curnode != NULL && index++ < iterator->index) {
curnode = curnode->next;
}
} else {
curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
curnode = curnode->next;
}
} else {
/* The collection is live, we nav the tree from the base object if we cannot
* use the cache to restart from the last point. */
xmlNodePtr basenode = dom_object_get_node(objmap->baseobj);

/* We have a strong reference to the base node via baseobj_zv, this cannot become NULL */
ZEND_ASSERT(basenode != NULL);

zend_long previndex;
if (php_dom_is_cache_tag_stale_from_node(&iterator->cache_tag, basenode)) {
php_dom_mark_cache_tag_up_to_date_from_node(&iterator->cache_tag, basenode);
previndex = 0;
curnode = php_dom_first_child_of_container_node(basenode);
} else {
previndex = iterator->index - 1;
curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
}
curnode = dom_get_elements_by_tag_name_ns_raw(
basenode, curnode, objmap->ns, objmap->local, objmap->local_lower, &previndex, iterator->index);
}
if (intern->ptr != NULL) {
/* Note: keep legacy behaviour for non-spec mode. */
/* TODO: make this prettier */
if (!php_dom_follow_spec_intern(intern) && (objmap->handler == &php_dom_obj_map_attributes || objmap->handler == &php_dom_obj_map_child_nodes)) {
xmlNodePtr curnode = ((php_libxml_node_ptr *) intern->ptr)->node;
curnode = curnode->next;
if (curnode) {
php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
}
} else {
curnode = php_dom_libxml_hash_iter(objmap, iterator->index);
objmap->handler->get_item(objmap, (zend_long) iterator->index, &iterator->curobj);
}
}

zval_ptr_dtor(&iterator->curobj);
ZVAL_UNDEF(&iterator->curobj);

if (curnode) {
php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
}
zval_ptr_dtor(&garbage);
}
/* }}} */

Expand All @@ -261,7 +186,6 @@ zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, i
{
dom_object *intern;
dom_nnodemap_object *objmap;
xmlNodePtr curnode=NULL;
php_dom_iterator *iterator;

if (by_ref) {
Expand All @@ -277,26 +201,7 @@ zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, i

intern = Z_DOMOBJ_P(object);
objmap = (dom_nnodemap_object *)intern->ptr;
if (objmap != NULL) {
if (objmap->nodetype != XML_ENTITY_NODE &&
objmap->nodetype != XML_NOTATION_NODE) {
if (objmap->nodetype == DOM_NODESET) {
HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv);
zval *entry = zend_hash_index_find(nodeht, 0);
if (entry) {
ZVAL_COPY(&iterator->curobj, entry);
}
} else {
curnode = dom_fetch_first_iteration_item(objmap);
}
} else {
curnode = php_dom_libxml_hash_iter(objmap, 0);
}
}

if (curnode) {
php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj);
}
objmap->handler->get_item(objmap, 0, &iterator->curobj);

return &iterator->intern;
}
Expand Down
4 changes: 2 additions & 2 deletions ext/dom/element.c
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, z

object_init_ex(return_value, iter_ce);
namednode = Z_DOMOBJ_P(return_value);
dom_namednode_iter(intern, 0, namednode, NULL, name, NULL);
dom_namednode_iter(intern, namednode, NULL, name, NULL, &php_dom_obj_map_by_tag_name);
}

PHP_METHOD(DOMElement, getElementsByTagName)
Expand Down Expand Up @@ -1257,7 +1257,7 @@ static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS

object_init_ex(return_value, iter_ce);
namednode = Z_DOMOBJ_P(return_value);
dom_namednode_iter(intern, 0, namednode, NULL, name, uri);
dom_namednode_iter(intern, namednode, NULL, name, uri, &php_dom_obj_map_by_tag_name);
}

PHP_METHOD(DOMElement, getElementsByTagNameNS)
Expand Down
Loading