php-src/Zend/tests/gh11244-004.phpt
Bob Weinand cd53ce838a Track HashTableIterators for copy-on-write copies of HashTables
When executing a foreach ($ht as &$ref), foreach calls zend_hash_iterator_pos_ex() on every iteration. If the HashTable contained in the $ht variable is not the tracked HashTable, it will reset the position to the internal array pointer of the array currently in $ht.
This behaviour is generally fine, but undesirable for copy-on-write copies of the iterated HashTable. This may trivially occur when the iterated over HashTable is assigned to some variable, then the iterated over variable modified, leading to array separation, changing the HashTable pointer in the variable. Thus foreach happily restarting iteration.
This behaviour (despite existing since PHP 7.0) is considered a bug, if not only for the behaviour being unexpected to the user, also copy-on-write should not have trivially observable side-effects by mere assignment.

The bugfix consists of duplicating HashTableIterators whenever zend_array_dup() is called (the primitive used on array separation).
When a further access to the HashPosition through the HashTableIterators API happens and the HashTable does not match the tracked one, all the duplicates (which are tracked by single linked list) are searched for the wanted HashTable. If found, the HashTableIterator is replaced by the found copy and all other copies are removed.
This ensures that we always end up tracking the correct HashTable.

Fixes GH-11244.

Signed-off-by: Bob Weinand <bobwei9@hotmail.com>
2023-08-28 01:25:12 +02:00

19 lines
275 B
PHP

--TEST--
GH-11244: Modifying a copied by-ref iterated array resets the array position (with object)
--FILE--
<?php
$obj = (object)[1,2,3];
foreach ($obj as $p => $v) {
echo "$p : $v\n";
$clone = clone $obj;
$ref = &$obj->$p;
}
?>
--EXPECTF--
0 : 1
1 : 2
2 : 3