Skip to content

Commit 9469db9

Browse files
committed
DOMNodeList elements are accessible through array notation
Fixes #67949
1 parent 71c3961 commit 9469db9

File tree

5 files changed

+152
-28
lines changed

5 files changed

+152
-28
lines changed

ext/dom/dom_properties.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ int dom_node_text_content_write(dom_object *obj, zval *newval TSRMLS_DC);
137137

138138
/* nodelist properties */
139139
int dom_nodelist_length_read(dom_object *obj, zval **retval TSRMLS_DC);
140+
xmlNodePtr dom_nodelist_xml_item(dom_nnodemap_object *objmap, long index);
141+
xmlNodePtr dom_nodelist_baseobj_item(dom_nnodemap_object *objmap, long index);
140142

141143
/* notation properties */
142144
int dom_notation_public_id_read(dom_object *obj, zval **retval TSRMLS_DC);

ext/dom/nodelist.c

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,47 @@ int dom_nodelist_length_read(dom_object *obj, zval **retval TSRMLS_DC)
9898

9999
/* }}} */
100100

101+
xmlNodePtr dom_nodelist_xml_item(dom_nnodemap_object *objmap, long index) /* {{{ */
102+
{
103+
xmlNodePtr itemnode = NULL;
104+
105+
if (objmap->nodetype == XML_ENTITY_NODE) {
106+
itemnode = php_dom_libxml_hash_iter(objmap->ht, index);
107+
} else {
108+
itemnode = php_dom_libxml_notation_iter(objmap->ht, index);
109+
}
110+
111+
return itemnode;
112+
} /* }}} end dom_nodelist_xml_item */
113+
114+
xmlNodePtr dom_nodelist_baseobj_item(dom_nnodemap_object *objmap, long index) /* {{{ */
115+
{
116+
xmlNodePtr itemnode = NULL;
117+
xmlNodePtr nodep, curnode;
118+
int count = 0;
119+
120+
nodep = dom_object_get_node(objmap->baseobj);
121+
if (nodep) {
122+
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
123+
curnode = nodep->children;
124+
while (count < index && curnode != NULL) {
125+
count++;
126+
curnode = curnode->next;
127+
}
128+
itemnode = curnode;
129+
} else {
130+
if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
131+
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
132+
} else {
133+
nodep = nodep->children;
134+
}
135+
itemnode = dom_get_elements_by_tag_name_ns_raw(nodep, (char *) objmap->ns, (char *) objmap->local, &count, index);
136+
}
137+
}
138+
139+
return itemnode;
140+
} /* }}} end dom_nodelist_baseobj_item */
141+
101142
/* {{{ proto DOMNode dom_nodelist_item(int index);
102143
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-844377136
103144
Since:
@@ -111,8 +152,6 @@ PHP_FUNCTION(dom_nodelist_item)
111152
xmlNodePtr itemnode = NULL;
112153

113154
dom_nnodemap_object *objmap;
114-
xmlNodePtr nodep, curnode;
115-
int count = 0;
116155
HashTable *nodeht;
117156
zval **entry;
118157

@@ -126,38 +165,16 @@ PHP_FUNCTION(dom_nodelist_item)
126165
objmap = (dom_nnodemap_object *)intern->ptr;
127166
if (objmap != NULL) {
128167
if (objmap->ht) {
129-
if (objmap->nodetype == XML_ENTITY_NODE) {
130-
itemnode = php_dom_libxml_hash_iter(objmap->ht, index);
131-
} else {
132-
itemnode = php_dom_libxml_notation_iter(objmap->ht, index);
133-
}
168+
itemnode = dom_nodelist_xml_item(objmap, index);
134169
} else {
135170
if (objmap->nodetype == DOM_NODESET) {
136171
nodeht = HASH_OF(objmap->baseobjptr);
137172
if (zend_hash_index_find(nodeht, index, (void **) &entry)==SUCCESS) {
138-
*return_value = **entry;
139-
zval_copy_ctor(return_value);
173+
MAKE_COPY_ZVAL(entry, return_value);
140174
return;
141175
}
142176
} else if (objmap->baseobj) {
143-
nodep = dom_object_get_node(objmap->baseobj);
144-
if (nodep) {
145-
if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) {
146-
curnode = nodep->children;
147-
while (count < index && curnode != NULL) {
148-
count++;
149-
curnode = curnode->next;
150-
}
151-
itemnode = curnode;
152-
} else {
153-
if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
154-
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
155-
} else {
156-
nodep = nodep->children;
157-
}
158-
itemnode = dom_get_elements_by_tag_name_ns_raw(nodep, objmap->ns, objmap->local, &count, index);
159-
}
160-
}
177+
itemnode = dom_nodelist_baseobj_item(objmap, index);
161178
}
162179
}
163180
}

ext/dom/php_dom.c

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ zend_class_entry *dom_namespace_node_class_entry;
7272
/* }}} */
7373

7474
zend_object_handlers dom_object_handlers;
75+
zend_object_handlers dom_nnodemap_object_handlers;
7576

7677
static HashTable classes;
7778
/* {{{ prop handler tables */
@@ -668,6 +669,10 @@ PHP_MINIT_FUNCTION(dom)
668669
dom_object_handlers.has_property = dom_property_exists;
669670
dom_object_handlers.get_debug_info = dom_get_debug_info;
670671

672+
memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
673+
dom_nnodemap_object_handlers.read_dimension = dom_nodelist_read_dimension;
674+
dom_nnodemap_object_handlers.has_dimension = dom_nodelist_has_dimension;
675+
671676
zend_hash_init(&classes, 0, NULL, NULL, 1);
672677

673678
INIT_CLASS_ENTRY(ce, "DOMException", php_dom_domexception_class_functions);
@@ -1297,7 +1302,7 @@ zend_object_value dom_nnodemap_objects_new(zend_class_entry *class_type TSRMLS_D
12971302

12981303
retval.handle = zend_objects_store_put(intern, dom_nnodemap_object_dtor, (zend_objects_free_object_storage_t)dom_nnodemap_objects_free_storage, dom_objects_clone TSRMLS_CC);
12991304
intern->handle = retval.handle;
1300-
retval.handlers = dom_get_obj_handlers(TSRMLS_C);
1305+
retval.handlers = &dom_nnodemap_object_handlers;
13011306

13021307
return retval;
13031308
}
@@ -1674,6 +1679,83 @@ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
16741679
}
16751680
/* }}} end dom_get_nsdecl */
16761681

1682+
static int dom_nodelist_fetch_dimension(xmlNodePtr *itemnode, zval *offset, dom_nnodemap_object *objmap, zval *rv TSRMLS_DC) /* {{{ */
1683+
{
1684+
convert_to_long(offset);
1685+
long index = Z_LVAL_P(offset);
1686+
HashTable *nodeht;
1687+
zval **entry;
1688+
int ret = 0;
1689+
1690+
if (objmap->ht) {
1691+
*itemnode = dom_nodelist_xml_item(objmap, index);
1692+
} else {
1693+
if (objmap->nodetype == DOM_NODESET) {
1694+
nodeht = HASH_OF(objmap->baseobjptr);
1695+
if (zend_hash_index_find(nodeht, index, (void **) &entry) == SUCCESS) {
1696+
if (itemnode != NULL && rv != NULL) {
1697+
/* Passed by read_dimension */
1698+
MAKE_COPY_ZVAL(entry, rv);
1699+
}
1700+
ret = 1;
1701+
}
1702+
} else if (objmap->baseobj) {
1703+
if (itemnode == NULL && rv == NULL) {
1704+
/* Passed by has_dimension */
1705+
if (dom_nodelist_baseobj_item(objmap, index)) {
1706+
ret = 1;
1707+
}
1708+
} else {
1709+
*itemnode = dom_nodelist_baseobj_item(objmap, index);
1710+
}
1711+
}
1712+
}
1713+
1714+
if (rv != NULL && itemnode != NULL) {
1715+
if (*itemnode) {
1716+
ret = 1;
1717+
}
1718+
}
1719+
1720+
return ret;
1721+
} /* }}} end dom_nodelist_fetch_dimension */
1722+
1723+
zval *dom_nodelist_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
1724+
{
1725+
dom_object *intern;
1726+
xmlNodePtr itemnode = NULL;
1727+
dom_nnodemap_object *objmap;
1728+
zval *rv;
1729+
int found;
1730+
1731+
ALLOC_INIT_ZVAL(rv);
1732+
1733+
intern = (dom_object *) zend_object_store_get_object(object TSRMLS_CC);
1734+
1735+
objmap = (dom_nnodemap_object *)intern->ptr;
1736+
1737+
if (dom_nodelist_fetch_dimension(&itemnode, offset, objmap, rv TSRMLS_CC)) {
1738+
if (itemnode) {
1739+
php_dom_create_object(itemnode, &found, rv, objmap->baseobj TSRMLS_CC);
1740+
}
1741+
}
1742+
1743+
Z_DELREF_P(rv);
1744+
1745+
return rv;
1746+
} /* }}} end dom_nodelist_read_dimension */
1747+
1748+
int dom_nodelist_has_dimension(zval *object, zval *member, int check_empty TSRMLS_DC)
1749+
{
1750+
dom_object *intern;
1751+
dom_nnodemap_object *objmap;
1752+
1753+
intern = (dom_object *) zend_object_store_get_object(object TSRMLS_CC);
1754+
objmap = (dom_nnodemap_object *)intern->ptr;
1755+
1756+
return dom_nodelist_fetch_dimension(NULL, member, objmap, NULL TSRMLS_CC);
1757+
} /* }}} end dom_nodelist_has_dimension */
1758+
16771759
#endif /* HAVE_DOM */
16781760

16791761
/*

ext/dom/php_dom.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index);
123123
xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index);
124124
zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC);
125125
int dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce TSRMLS_DC);
126+
zval *dom_nodelist_read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
127+
int dom_nodelist_has_dimension(zval *object, zval *member, int check_empty TSRMLS_DC);
128+
static int dom_nodelist_fetch_dimension(xmlNodePtr *itemnode, zval *offset, dom_nnodemap_object *objmap, zval *rv TSRMLS_DC);
126129

127130
#define REGISTER_DOM_CLASS(ce, name, parent_ce, funcs, entry) \
128131
INIT_CLASS_ENTRY(ce, name, funcs); \

ext/dom/tests/bug67949.phpt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Bug #67949: DOMNodeList elements should be accessible through array notation
3+
--FILE--
4+
<?php
5+
6+
$html = <<<HTML
7+
<div>data</div>
8+
HTML;
9+
$doc = new DOMDocument;
10+
$doc->loadHTML($html);
11+
var_dump($doc->getElementsByTagName('div')[0]->textContent);
12+
var_dump($doc->getElementsByTagName('div')['test']->textContent); // testing that weak casting works
13+
var_dump(isset($doc->getElementsByTagName('div')[0]));
14+
var_dump(isset($doc->getElementsByTagName('div')[1]));
15+
16+
--EXPECT--
17+
string(4) "data"
18+
string(4) "data"
19+
bool(true)
20+
bool(false)

0 commit comments

Comments
 (0)