Skip to content

Commit 9be9f70

Browse files
committed
Fix weird unpack behaviour in DOM
Engine pitfall: the iter index is only updated by foreach opcodes, so the existing code that used it as an index for the nodes w.r.t. the start did not work properly. Fix it by using our own counter. Closes GH-18004.
1 parent c7d3dc6 commit 9be9f70

File tree

4 files changed

+44
-5
lines changed

4 files changed

+44
-5
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.3.19
44

5+
- DOM:
6+
. Fix weird unpack behaviour in DOM. (nielsdos)
7+
58
- GD:
69
. Fixed bug GH-17984 (calls with arguments as array with references).
710
(David Carlier)

ext/dom/dom_iterators.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key)
158158
zval *object = &iterator->intern.data;
159159

160160
if (instanceof_function(Z_OBJCE_P(object), dom_nodelist_class_entry)) {
161-
ZVAL_LONG(key, iter->index);
161+
ZVAL_LONG(key, iterator->index);
162162
} else {
163163
dom_object *intern = Z_DOMOBJ_P(&iterator->curobj);
164164

@@ -189,6 +189,8 @@ static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
189189
return;
190190
}
191191

192+
iterator->index++;
193+
192194
intern = Z_DOMOBJ_P(&iterator->curobj);
193195
object = &iterator->intern.data;
194196
nnmap = Z_DOMOBJ_P(object);
@@ -227,18 +229,18 @@ static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
227229
curnode = basenode->children;
228230
}
229231
} else {
230-
previndex = iter->index - 1;
232+
previndex = iterator->index - 1;
231233
curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node;
232234
}
233235
curnode = dom_get_elements_by_tag_name_ns_raw(
234-
basenode, curnode, (char *) objmap->ns, (char *) objmap->local, &previndex, iter->index);
236+
basenode, curnode, (char *) objmap->ns, (char *) objmap->local, &previndex, iterator->index);
235237
}
236238
}
237239
} else {
238240
if (objmap->nodetype == XML_ENTITY_NODE) {
239-
curnode = php_dom_libxml_hash_iter(objmap->ht, iter->index);
241+
curnode = php_dom_libxml_hash_iter(objmap->ht, iterator->index);
240242
} else {
241-
curnode = php_dom_libxml_notation_iter(objmap->ht, iter->index);
243+
curnode = php_dom_libxml_notation_iter(objmap->ht, iterator->index);
242244
}
243245
}
244246
}

ext/dom/php_dom.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ typedef struct {
9898
zend_object_iterator intern;
9999
zval curobj;
100100
HashPosition pos;
101+
/* intern->index is only updated for FE_* opcodes, not for e.g. unpacking,
102+
* yet we need to track the position of the node relative to the start. */
103+
zend_ulong index;
101104
php_libxml_cache_tag cache_tag;
102105
} php_dom_iterator;
103106

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Unpacking vs foreach behaviour
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = new DOMDocument;
9+
$dom->loadXML('<root><a/><b/></root>');
10+
11+
echo "--- By foreach: ---\n";
12+
13+
foreach ($dom->documentElement->getElementsByTagName('*') as $node) {
14+
var_dump($node->localName);
15+
}
16+
17+
echo "--- By unpacking: ---\n";
18+
19+
$iter = $dom->documentElement->getElementsByTagName('*');
20+
foreach ([...$iter] as $node) {
21+
var_dump($node->localName);
22+
}
23+
24+
?>
25+
--EXPECT--
26+
--- By foreach: ---
27+
string(1) "a"
28+
string(1) "b"
29+
--- By unpacking: ---
30+
string(1) "a"
31+
string(1) "b"

0 commit comments

Comments
 (0)