fix foreach reference on dynamic variables

master
韩天峰 1 day ago
parent 7bdb6dcd44
commit d66f3d7e10
  1. 12
      src/Parser/ForeachTrait.php
  2. 88
      tests/compiler/loop/foreach-byref-dynamic-variable.phpt

@ -194,9 +194,15 @@ trait ForeachTrait
$name = $this->parseIdentifier($node->expr);
if ($this->hasVar($name)) {
$type = $this->getVarType($name);
if ($type === Type::ARRAY) {
return $this->parseForeachIterable($node, $name);
} elseif ($type === Type::OBJECT) {
// A by-reference foreach must operate on the original variable.
// Copying a dynamically typed iterable into a temporary triggers
// normal PHP array COW, so references would update only that
// temporary instead of the source variable. ForeachIterator
// performs the runtime array/object validation itself.
if ($type === Type::ARRAY
|| $type === Type::OBJECT
|| ($node->byRef && ($type === Type::VAR || $type === Type::REF))
) {
return $this->parseForeachIterable($node, $name);
} elseif ($this->isStdContainerType($type)) {
return $this->parseForeachStdContainer($node);

@ -0,0 +1,88 @@
--TEST--
Foreach by reference mutates the original dynamically typed variable without a COW temporary
--FILE--
<?php
function mutateUntyped($items): array {
foreach ($items as &$item) {
$item['value']++;
}
return $items;
}
function mutateMixed(mixed $items): array {
foreach ($items as &$item) {
$item['value'] += 2;
}
return $items;
}
function mutateNullable(?array $items): array {
foreach ($items as &$item) {
$item['value'] += 3;
}
return $items;
}
function mutateTyped(array $items): array {
foreach ($items as &$item) {
$item['value'] += 4;
}
return $items;
}
function main(): void {
var_dump(mutateUntyped([['value' => 1], ['value' => 10]]));
var_dump(mutateMixed([['value' => 1], ['value' => 10]]));
var_dump(mutateNullable([['value' => 1], ['value' => 10]]));
var_dump(mutateTyped([['value' => 1], ['value' => 10]]));
}
?>
--EXPECT--
array(2) {
[0]=>
array(1) {
["value"]=>
int(2)
}
[1]=>
array(1) {
["value"]=>
int(11)
}
}
array(2) {
[0]=>
array(1) {
["value"]=>
int(3)
}
[1]=>
array(1) {
["value"]=>
int(12)
}
}
array(2) {
[0]=>
array(1) {
["value"]=>
int(4)
}
[1]=>
array(1) {
["value"]=>
int(13)
}
}
array(2) {
[0]=>
array(1) {
["value"]=>
int(5)
}
[1]=>
array(1) {
["value"]=>
int(14)
}
}
Loading…
Cancel
Save