Skip to content

Commit ce94e92

Browse files
committed
Fix GH-17572: getElementsByTagName returns collections with tagName-based indexing, causing loss of elements when converted to arrays
Only dtd named node maps should have string-based indexing. The ce check is fragile, just check for the presence of an xml hash table.
1 parent 5c066e0 commit ce94e92

File tree

2 files changed

+51
-7
lines changed

2 files changed

+51
-7
lines changed

ext/dom/dom_iterators.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ static void itemHashScanner (void *payload, void *data, xmlChar *name)
4949
}
5050
/* }}} */
5151

52+
static dom_nnodemap_object *php_dom_iterator_get_nnmap(const php_dom_iterator *iterator)
53+
{
54+
const zval *object = &iterator->intern.data;
55+
dom_object *nnmap = Z_DOMOBJ_P(object);
56+
return nnmap->ptr;
57+
}
58+
5259
xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID) /* {{{ */
5360
{
5461
xmlEntityPtr ret = xmlMalloc(sizeof(xmlEntity));
@@ -120,11 +127,11 @@ zval *php_dom_iterator_current_data(zend_object_iterator *iter) /* {{{ */
120127
static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
121128
{
122129
php_dom_iterator *iterator = (php_dom_iterator *)iter;
123-
zval *object = &iterator->intern.data;
124-
zend_class_entry *ce = Z_OBJCE_P(object);
130+
dom_nnodemap_object *objmap = php_dom_iterator_get_nnmap(iterator);
125131

126-
/* Nodelists have the index as a key while named node maps have the name as a key. */
127-
if (instanceof_function(ce, dom_nodelist_class_entry) || instanceof_function(ce, dom_modern_nodelist_class_entry)) {
132+
/* Only (dtd) named node maps, i.e. the ones based on a libxml hash table, are keyed by the name
133+
* because in that case the name is unique. */
134+
if (!objmap->ht) {
128135
ZVAL_LONG(key, iter->index);
129136
} else {
130137
dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);
@@ -169,9 +176,7 @@ static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
169176
}
170177

171178
dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);
172-
zval *object = &iterator->intern.data;
173-
dom_object *nnmap = Z_DOMOBJ_P(object);
174-
dom_nnodemap_object *objmap = nnmap->ptr;
179+
dom_nnodemap_object *objmap = php_dom_iterator_get_nnmap(iterator);
175180

176181
if (intern != NULL && intern->ptr != NULL) {
177182
if (objmap->nodetype != XML_ENTITY_NODE &&

ext/dom/tests/modern/xml/gh17572.phpt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
GH-17572 (getElementsByTagName returns collections with tagName-based indexing, causing loss of elements when converted to arrays)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = Dom\XMLDocument::createFromString(<<<XML
9+
<!DOCTYPE root [
10+
<!ENTITY a "a">
11+
]>
12+
<root>
13+
<p/><p/>
14+
</root>
15+
XML);
16+
17+
foreach ($dom->querySelectorAll('p') as $k => $v) {
18+
var_dump($k, $v->nodeName);
19+
}
20+
21+
foreach ($dom->getElementsByTagName('p') as $k => $v) {
22+
var_dump($k, $v->nodeName);
23+
}
24+
foreach ($dom->doctype->entities as $k => $v) {
25+
var_dump($k, $v->nodeName);
26+
}
27+
28+
?>
29+
--EXPECT--
30+
int(0)
31+
string(1) "p"
32+
int(1)
33+
string(1) "p"
34+
int(0)
35+
string(1) "p"
36+
int(1)
37+
string(1) "p"
38+
string(1) "a"
39+
string(1) "a"

0 commit comments

Comments
 (0)