- Implement parseForeachIterableRef method to handle reference iteration - Add support for PropertyFetch, StaticPropertyFetch and ArrayDimFetch in foreach by ref - Generate proper itemRef calls for array dimension fetches in reference context - Update iterable type handling to distinguish between VAR and REF types - Add test case for foreach by reference with live object property mutationpull/44/head
parent
6cfab3bf9a
commit
25e5b47482
2 changed files with 162 additions and 2 deletions
@ -0,0 +1,136 @@ |
||||
--TEST-- |
||||
foreach by reference supports live mutation of object array properties |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class ForeachReferenceNode |
||||
{ |
||||
public array $children = []; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$node = new ForeachReferenceNode(); |
||||
$node->children = ['a', 'b', 'c']; |
||||
$copy = $node->children; |
||||
|
||||
foreach ($node->children as &$child) { |
||||
$child = 'X' . $child; |
||||
} |
||||
unset($child); |
||||
|
||||
var_dump($node->children, $copy); |
||||
|
||||
$node->children = [1, 2]; |
||||
$copy = $node->children; |
||||
$seen = []; |
||||
foreach ($node->children as $key => &$child) { |
||||
$seen[] = [$key, $child]; |
||||
if ($key === 0) { |
||||
$node->children[] = 3; |
||||
} |
||||
$child *= 10; |
||||
} |
||||
unset($child); |
||||
|
||||
var_dump($seen, $node->children, $copy); |
||||
|
||||
$node->children = [1, 2, 3, 4]; |
||||
$seen = []; |
||||
foreach ($node->children as $key => &$child) { |
||||
$seen[] = $key; |
||||
if ($key === 0) { |
||||
unset($node->children[1]); |
||||
} |
||||
$child *= 10; |
||||
} |
||||
unset($child); |
||||
|
||||
var_dump($seen, $node->children); |
||||
|
||||
$root = new ForeachReferenceNode(); |
||||
$original = new ForeachReferenceNode(); |
||||
$root->children = [$original]; |
||||
|
||||
$replaceChildren = static function (ForeachReferenceNode $current): void { |
||||
foreach ($current->children as &$child) { |
||||
$child = new ForeachReferenceNode(); |
||||
} |
||||
unset($child); |
||||
}; |
||||
$replaceChildren($root); |
||||
|
||||
var_dump($root->children[0] !== $original); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(3) { |
||||
[0]=> |
||||
string(2) "Xa" |
||||
[1]=> |
||||
string(2) "Xb" |
||||
[2]=> |
||||
string(2) "Xc" |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
string(1) "a" |
||||
[1]=> |
||||
string(1) "b" |
||||
[2]=> |
||||
string(1) "c" |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
array(2) { |
||||
[0]=> |
||||
int(0) |
||||
[1]=> |
||||
int(1) |
||||
} |
||||
[1]=> |
||||
array(2) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
} |
||||
[2]=> |
||||
array(2) { |
||||
[0]=> |
||||
int(2) |
||||
[1]=> |
||||
int(3) |
||||
} |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
int(10) |
||||
[1]=> |
||||
int(20) |
||||
[2]=> |
||||
int(30) |
||||
} |
||||
array(2) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
int(0) |
||||
[1]=> |
||||
int(2) |
||||
[2]=> |
||||
int(3) |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
int(10) |
||||
[2]=> |
||||
int(30) |
||||
[3]=> |
||||
int(40) |
||||
} |
||||
bool(true) |
||||
Loading…
Reference in new issue