Skip to content
Open
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 NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ PHP NEWS
- Core:
. Fixed out-of-bounds reads during automatic UTF-16/32 encoding detection.
(Yudai Takada)
. Fixed incorrect foreach iterator positions when compacting arrays with
holes. (Weilin Du)
. Fixed bug GH-15375 (Nested "yield from" skips items after a valid() or
next() call on the inner generator). (iliaal)
. Fixed bug GH-23232 (lone namespace separator asks the autoloader for an
Expand Down
36 changes: 36 additions & 0 deletions Zend/tests/array_dup_multiple_iterators.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
Array duplication updates iterators at both a hole and the next defined element
--FILE--
<?php
function test(array $values): void {
$outerVisits = [];
$innerVisits = [];
$first = true;
foreach ($values as $outerKey => &$outerValue) {
$outerVisits[] = "$outerKey=>$outerValue";
if ($first) {
$first = false;
foreach ($values as $innerKey => &$innerValue) {
$innerVisits[] = "$innerKey=>$innerValue";
if ($innerValue === 11) {
// The outer cursor is at a hole, the inner at the next value.
unset($values['a'], $values['b']);
$copy = $values;
// Trigger copy-on-write duplication, which compacts the holes.
$values['i'] = 18;
}
}
unset($innerValue);
}
}
unset($outerValue);
echo 'outer: ', implode(' ', $outerVisits), "\n";
echo 'inner: ', implode(' ', $innerVisits), "\n";
}

test(['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13,
'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17]);
?>
--EXPECT--
outer: a=>10 c=>12 d=>13 e=>14 f=>15 g=>16 h=>17 i=>18
inner: a=>10 b=>11 c=>12 d=>13 e=>14 f=>15 g=>16 h=>17 i=>18
40 changes: 40 additions & 0 deletions Zend/tests/rehash_multiple_iterators.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
Rehashing updates iterators at both a hole and the next defined element
--FILE--
<?php
function test(array $values, $firstKey, $secondKey, $newKey, int $newValue): void {
$outerVisits = [];
$innerVisits = [];
$first = true;
foreach ($values as $outerKey => &$outerValue) {
$outerVisits[] = "$outerKey=>$outerValue";
if ($first) {
$first = false;
foreach ($values as $innerKey => &$innerValue) {
$innerVisits[] = "$innerKey=>$innerValue";
if ($innerValue === 11) {
// The outer cursor is at a hole, the inner at the next value.
unset($values[$firstKey], $values[$secondKey]);
$values[$newKey] = $newValue;
}
}
unset($innerValue);
}
}
unset($outerValue);
echo 'outer: ', implode(' ', $outerVisits), "\n";
echo 'inner: ', implode(' ', $innerVisits), "\n";
}

// Adding a string key converts packed storage and compacts its holes.
test([10, 11, 12, 13, 14], 0, 1, 'new', 15);

// Inserting into a full mixed table compacts its holes without growing it.
test(['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13,
'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17], 'a', 'b', 'i', 18);
?>
--EXPECT--
outer: 0=>10 2=>12 3=>13 4=>14 new=>15
inner: 0=>10 1=>11 2=>12 3=>13 4=>14 new=>15
outer: a=>10 c=>12 d=>13 e=>14 f=>15 g=>16 h=>17 i=>18
inner: a=>10 b=>11 c=>12 d=>13 e=>14 f=>15 g=>16 h=>17 i=>18
4 changes: 2 additions & 2 deletions Zend/zend_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1412,7 +1412,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
do {
zend_hash_iterators_update(ht, iter_pos, j);
iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1);
} while (iter_pos < i);
} while (iter_pos <= i);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: while at it, you can also fix zend_array_dup_elements()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. and I think this is the similar fix to the existing one so we don't need to add more NEWS entry.

}
q++;
j++;
Expand Down Expand Up @@ -2428,7 +2428,7 @@ static zend_always_inline uint32_t zend_array_dup_elements(HashTable *source, Ha
do {
zend_hash_iterators_update(target, iter_pos, target_idx);
iter_pos = zend_hash_iterators_lower_pos(target, iter_pos + 1);
} while (iter_pos < idx);
} while (iter_pos <= idx);
}
target_idx++; q++;
}
Expand Down
Loading