fix(parser): 修复数组元素引用赋值写回问题

pull/33/head
Yurun 1 month ago
parent 2287695b44
commit c7d4b562c0
  1. 17
      src/Parser/AssignOpTrait.php
  2. 29
      tests/compiler/ref/array-ref-assign-001.phpt
  3. 34
      tests/compiler/ref/array-ref-assign-002.phpt

@ -42,11 +42,24 @@ trait AssignOpTrait
$tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, Type::VAR);
// 仅当目标是 php::Array 时使用 item/newItem:
// - item(dim, true) 直接返回元素 zval 地址,赋值时能穿透 IS_REFERENCE 写回,
// 修复 $arr = [&$x] / $arr[] = &$x 这类数组元素引用的写回问题;
// - 对于 ArrayAccess 对象(如 ArrayObject)或类型未知(VAR)的变量,item 不存在或语义不符,
// 必须继续使用 offsetSet(对象数组元素的引用写回由对象自身保证,编译器不负责)。
$isPhpArray = $this->getVarType($array) === Type::ARRAY;
if ($left->dim === null) {
if ($isPhpArray) {
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.newItem() = {$tmp}" . '), ' . $tmp . ')';
}
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet(" . self::VALUE_NULL . ", {$tmp})" . '), ' . $tmp . ')';
}
$dim = $this->parseIdentifier($left->dim);
if ($isPhpArray) {
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.item({$dim}, true) = {$tmp}" . '), ' . $tmp . ')';
}
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet({$dim}, {$tmp})" . '), ' . $tmp . ')';
}
@ -828,7 +841,9 @@ trait AssignOpTrait
$left = $this->parseIdentifier($expr->var);
$rightExpr = $tmpVar . ' = ' . $this->emitStaticPropertyFetchRef($expr->expr, $expr);
} elseif ($this->isArrayDimFetch($expr->expr)) {
$left = $this->parseIdentifier($expr->var);
// $left 已在函数开头通过 parseWritableIdentifier($expr->var) 正确计算,
// 这里不可再用 parseIdentifier() 覆盖,否则当左值是数组追加($arr[] = &$x)
// 或数组元素($arr[$k] = &$x)时会被当作读取而报错 "Cannot use [] for reading"。
$array = $this->parseWritableIdentifier($expr->expr->var);
if ($expr->expr->dim == null) {
$this->fatalError($expr, 'Cannot assign reference to array dim fetch without dim');

@ -0,0 +1,29 @@
--TEST--
array reference assignment: append and element assignment write back through reference
--FILE--
<?php
function main()
{
$arr1 = [1, 2, 3];
$arr2 = [&$arr1[0]];
$arr2[0] = 123;
$arr2[] = &$arr1[1];
$arr2[1] = 456;
var_dump($arr1, $arr2);
}
?>
--EXPECT--
array(3) {
[0]=>
&int(123)
[1]=>
&int(456)
[2]=>
int(3)
}
array(2) {
[0]=>
&int(123)
[1]=>
&int(456)
}

@ -0,0 +1,34 @@
--TEST--
array reference assignment to element: $arr[$k] = &$v writes back through reference
--FILE--
<?php
function main()
{
$x = 10;
$y = 20;
$arr = [1, 2, 3];
$arr[0] = &$x; // 覆盖已有元素为引用
$arr[5] = &$y; // 新建元素为引用
$x = 100;
$y = 200;
var_dump($arr[0], $arr[5]); // 100, 200
// 通过元素引用写回
$arr[0] = 111;
$arr[5] = 222;
var_dump($x, $y); // 111, 222
// 嵌套:引用赋值到多维数组元素
$z = 7;
$m = [[1], [2]];
$m[0][0] = &$z;
$z = 77;
var_dump($m[0][0]); // 77
}
?>
--EXPECT--
int(100)
int(200)
int(111)
int(222)
int(77)
Loading…
Cancel
Save