diff --git a/src/Parser/AssignOpTrait.php b/src/Parser/AssignOpTrait.php index dabef684..5fdb32b4 100644 --- a/src/Parser/AssignOpTrait.php +++ b/src/Parser/AssignOpTrait.php @@ -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'); diff --git a/tests/compiler/ref/array-ref-assign-001.phpt b/tests/compiler/ref/array-ref-assign-001.phpt new file mode 100644 index 00000000..3273f29e --- /dev/null +++ b/tests/compiler/ref/array-ref-assign-001.phpt @@ -0,0 +1,29 @@ +--TEST-- +array reference assignment: append and element assignment write back through reference +--FILE-- + +--EXPECT-- +array(3) { + [0]=> + &int(123) + [1]=> + &int(456) + [2]=> + int(3) +} +array(2) { + [0]=> + &int(123) + [1]=> + &int(456) +} diff --git a/tests/compiler/ref/array-ref-assign-002.phpt b/tests/compiler/ref/array-ref-assign-002.phpt new file mode 100644 index 00000000..025e6bad --- /dev/null +++ b/tests/compiler/ref/array-ref-assign-002.phpt @@ -0,0 +1,34 @@ +--TEST-- +array reference assignment to element: $arr[$k] = &$v writes back through reference +--FILE-- + +--EXPECT-- +int(100) +int(200) +int(111) +int(222) +int(77)