Skip to content

DOMNodeList elements are accessible through array notation #842

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

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 2 additions & 0 deletions ext/dom/dom_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ int dom_node_text_content_write(dom_object *obj, zval *newval TSRMLS_DC);

/* nodelist properties */
int dom_nodelist_length_read(dom_object *obj, zval *retval TSRMLS_DC);
xmlNodePtr dom_nodelist_xml_item(dom_nnodemap_object *objmap, zend_long index);
xmlNodePtr dom_nodelist_baseobj_item(dom_nnodemap_object *objmap, zend_long index);

/* notation properties */
int dom_notation_public_id_read(dom_object *obj, zval *retval TSRMLS_DC);
Expand Down
69 changes: 43 additions & 26 deletions ext/dom/nodelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,47 @@ int dom_nodelist_length_read(dom_object *obj, zval *retval TSRMLS_DC)

/* }}} */

xmlNodePtr dom_nodelist_xml_item(dom_nnodemap_object *objmap, zend_long index) /* {{{ */
{
xmlNodePtr itemnode = NULL;
if (objmap->nodetype == XML_ENTITY_NODE) {
itemnode = php_dom_libxml_hash_iter(objmap->ht, index);
} else {
itemnode = php_dom_libxml_notation_iter(objmap->ht, index);
}
return itemnode;
}
/* }}} end dom_nodelist_xml_item */

xmlNodePtr dom_nodelist_baseobj_item(dom_nnodemap_object *objmap, zend_long index) /* {{{ */
{
xmlNodePtr itemnode = NULL;
xmlNodePtr nodep, curnode;
int count = 0;

nodep = dom_object_get_node(objmap->baseobj);
if (nodep) {
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
curnode = nodep->children;
while (count < index && curnode != NULL) {
count++;
curnode = curnode->next;
}
itemnode = curnode;
} else {
if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
} else {
nodep = nodep->children;
}
itemnode = dom_get_elements_by_tag_name_ns_raw(
nodep, (char *) objmap->ns, (char *) objmap->local, &count, index);
}
}

return itemnode;
} /* }}} end dom_nodelist_baseobj_item */

/* {{{ proto DOMNode dom_nodelist_item(int index);
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-844377136
Since:
Expand All @@ -111,8 +152,6 @@ PHP_FUNCTION(dom_nodelist_item)
xmlNodePtr itemnode = NULL;

dom_nnodemap_object *objmap;
xmlNodePtr nodep, curnode;
int count = 0;

if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &id, dom_nodelist_class_entry, &index) == FAILURE) {
return;
Expand All @@ -124,11 +163,7 @@ PHP_FUNCTION(dom_nodelist_item)
objmap = (dom_nnodemap_object *)intern->ptr;
if (objmap != NULL) {
if (objmap->ht) {
if (objmap->nodetype == XML_ENTITY_NODE) {
itemnode = php_dom_libxml_hash_iter(objmap->ht, index);
} else {
itemnode = php_dom_libxml_notation_iter(objmap->ht, index);
}
itemnode = dom_nodelist_xml_item(objmap, index);
} else {
if (objmap->nodetype == DOM_NODESET) {
HashTable *nodeht = HASH_OF(&objmap->baseobj_zv);
Expand All @@ -138,25 +173,7 @@ PHP_FUNCTION(dom_nodelist_item)
return;
}
} else if (objmap->baseobj) {
nodep = dom_object_get_node(objmap->baseobj);
if (nodep) {
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
curnode = nodep->children;
while (count < index && curnode != NULL) {
count++;
curnode = curnode->next;
}
itemnode = curnode;
} else {
if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
} else {
nodep = nodep->children;
}
itemnode = dom_get_elements_by_tag_name_ns_raw(
nodep, (char *) objmap->ns, (char *) objmap->local, &count, index);
}
}
itemnode = dom_nodelist_baseobj_item(objmap, index);
}
}
}
Expand Down
67 changes: 67 additions & 0 deletions ext/dom/php_dom.c
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,8 @@ PHP_MINIT_FUNCTION(dom)
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.dtor_obj = dom_nnodemap_object_dtor;
dom_nnodemap_object_handlers.read_dimension = dom_nodelist_read_dimension;
dom_nnodemap_object_handlers.has_dimension = dom_nodelist_has_dimension;

zend_hash_init(&classes, 0, NULL, NULL, 1);

Expand Down Expand Up @@ -1542,6 +1544,71 @@ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
}
/* }}} end dom_get_nsdecl */

static int dom_nodelist_fetch_dimension(xmlNodePtr *itemnode, zval *offset, dom_nnodemap_object *objmap, zval *rv TSRMLS_DC) /* {{{ */
{
int index = zval_get_long(offset);
int ret = 0;

if (objmap->ht) {
*itemnode = dom_nodelist_xml_item(objmap, index);
} else {
if (objmap->nodetype == DOM_NODESET) {
HashTable *nodeht = HASH_OF(&objmap->baseobj_zv);
zval *entry = zend_hash_index_find(nodeht, index);
if (entry) {
if (rv != NULL && itemnode != NULL) {
/* Passed by read_dimension */
ZVAL_COPY(rv, entry);
}
ret = 1;
}
} else if (objmap->baseobj) {
if (rv == NULL && itemnode == NULL) {
/* Passed by has_dimension */
if (dom_nodelist_baseobj_item(objmap, index)) {
ret = 1;
}
} else {
*itemnode = dom_nodelist_baseobj_item(objmap, index);
}
}
}

if (rv != NULL && itemnode != NULL) {
/* Not passed by has_dimension */
if (*itemnode) {
ret = 1;
}
}

return ret;
} /* }}} end dom_nodelist_fetch_dimension */

zval *dom_nodelist_read_dimension(zval *object, zval *offset, int type, zval *rv TSRMLS_DC) /* {{{ */
{
xmlNodePtr itemnode = NULL;
dom_object *intern = Z_DOMOBJ_P(object);
dom_nnodemap_object *objmap = (dom_nnodemap_object *)intern->ptr;

if (dom_nodelist_fetch_dimension(&itemnode, offset, objmap, rv TSRMLS_CC)) {
if (itemnode) {
php_dom_create_object(itemnode, rv, objmap->baseobj TSRMLS_CC);
}
}
return rv;
}
/* }}} end dom_nodelist_read_dimension */

int dom_nodelist_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) /* {{{ */
{
dom_object *intern = Z_DOMOBJ_P(object);
dom_nnodemap_object *objmap = (dom_nnodemap_object *)intern->ptr;

return dom_nodelist_fetch_dimension(NULL, offset, objmap, NULL TSRMLS_CC);
}
/* }}} end dom_nodelist_has_dimension */


#endif /* HAVE_DOM */

/*
Expand Down
3 changes: 3 additions & 0 deletions ext/dom/php_dom.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index);
xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index);
zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC);
int dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce TSRMLS_DC);
zval *dom_nodelist_read_dimension(zval *object, zval *offset, int type, zval *rv TSRMLS_DC);
int dom_nodelist_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC);
static int dom_nodelist_fetch_dimension(xmlNodePtr *itemnode, zval *offset, dom_nnodemap_object *objmap, zval *rv TSRMLS_DC);

#define REGISTER_DOM_CLASS(ce, name, parent_ce, funcs, entry) \
INIT_CLASS_ENTRY(ce, name, funcs); \
Expand Down
20 changes: 20 additions & 0 deletions ext/dom/tests/bug67949.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Bug #67949: DOMNodeList elements should be accessible through array notation
--FILE--
<?php

$html = <<<HTML
<div>data</div>
HTML;
$doc = new DOMDocument;
$doc->loadHTML($html);
var_dump($doc->getElementsByTagName('div')[0]->textContent);
var_dump($doc->getElementsByTagName('div')['test']->textContent); // testing that weak casting works
var_dump(isset($doc->getElementsByTagName('div')[0]));
var_dump(isset($doc->getElementsByTagName('div')[1]));

--EXPECT--
string(4) "data"
string(4) "data"
bool(true)
bool(false)