feat(php): 扩展 refval 函数支持数组元素和对象属性引用

- 添加对 refval($arr[key]) 数组元素引用的支持
- 添加对 refval($obj->prop) 对象属性引用的支持
- 实现 expandRefvalExpr 方法处理复杂引用表达式
- 增强错误检查验证 refval 参数类型
- 添加相应的单元测试用例
pull/1/head
韩天峰 3 months ago
parent c238892409
commit e7680c5e81
  1. 64
      src/Php/CompilerBase.php
  2. 15
      tests/aot/ref/refval-array.phpt
  3. 16
      tests/aot/ref/refval-object.phpt

@ -3835,15 +3835,23 @@ class CompilerBase extends \PhpAot\Core\Translator
}
} elseif ($this->isFuncCallExpr($arg->value)) {
if ($this->isNameExpr($arg->value->name) and $arg->value->name->toString() === 'refval') {
if (count($arg->value->args) === 1 and $this->isVarExpr($arg->value->args[0]->value)) {
$name = $this->parseVariable($arg->value->args[0]->value);
if (count($arg->value->args) !== 1) {
$this->fatalError($arg, 'The refval function only accepts one parameter');
}
$inner = $arg->value->args[0]->value;
if ($this->isVarExpr($inner)) {
$name = $this->parseVariable($inner);
// 消除 refval() 函数调用,直接使用变量
$arg->value = $arg->value->args[0]->value;
$arg->value = $inner;
$list_args[] = $this->parseArgRefVar($arg, $name);
continue;
} else {
$this->fatalError($arg, 'The refval function only accepts one parameter of variable type');
}
$expr = $this->expandRefvalExpr($inner, $arg);
if ($expr !== null) {
$list_args[] = $expr;
continue;
}
$this->fatalError($arg, 'The refval function only accepts a variable, array element, or object property');
}
} else {
if ($byRef) {
@ -3876,6 +3884,38 @@ class CompilerBase extends \PhpAot\Core\Translator
return Symbol::argList() . '{' . implode(', ', $list_args) . '}';
}
/**
* 展开 refval() 调用中的数组元素或对象属性,返回对应的 C++ 引用表达式。
* 若为普通变量则返回 null,由调用方自行处理。
*/
protected function expandRefvalExpr(NodeAbstract $inner, Node\Arg $arg): ?string
{
if ($this->isPropertyFetch($inner) and $this->isVarExpr($inner->var)) {
$obj = $this->parseIdentifier($inner->var);
if (!$this->hasVar($obj)) {
$this->fatalError($arg, 'Undefined variable `$' . $obj . '`');
}
return $obj . '.attrRef(' . $this->identifierToStr($inner->name) . ')';
}
if ($this->isArrayDimFetch($inner) and $this->isVarExpr($inner->var)) {
$array = $this->parseIdentifier($inner->var);
if ($array === 'GLOBALS') {
$globalVar = $this->parseGlobalsArrayDimFetch($inner);
$ref = $this->addTmpVar(self::TYPE_REF);
$this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();';
return '&' . $ref;
}
if (!$this->hasVar($array)) {
$this->fatalError($arg, 'Undefined variable `$' . $array . '`');
}
if ($inner->dim === null) {
$this->fatalError($arg, 'Array dimension must be a constant expression');
}
return $array . '.itemRef(' . $this->identifierToStr($inner->dim) . ')';
}
return null;
}
/**
* 仅用于动态调用的参数解析
*/
@ -4723,11 +4763,19 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($argInfo->byRef) {
if ($this->isRefvalCall($arg->value)) {
if (count($arg->value->args) === 1 and $this->isVarExpr($arg->value->args[0]->value)) {
if (count($arg->value->args) !== 1) {
$this->fatalError($arg, 'The refval function only accepts one parameter');
}
$inner = $arg->value->args[0]->value;
if ($this->isVarExpr($inner)) {
// 消除 refval() 函数调用,直接使用变量
$arg->value = $arg->value->args[0]->value;
$arg->value = $inner;
} else {
$this->fatalError($arg, 'The refval function only accepts one parameter of variable type');
$expr = $this->expandRefvalExpr($inner, $arg);
if ($expr !== null) {
return $expr;
}
$this->fatalError($arg, 'The refval function only accepts a variable, array element, or object property');
}
}
if ($this->isVarExpr($arg->value)) {

@ -0,0 +1,15 @@
--TEST--
refval with array element
--FILE--
<?php
function main()
{
eval('function array_ref_test(&$val) { $val = "modified"; }');
$arr = ['key' => 'original'];
array_ref_test(refval($arr['key']));
echo $arr['key'];
}
?>
--EXPECT--
modified

@ -0,0 +1,16 @@
--TEST--
refval with object property
--FILE--
<?php
function main()
{
eval('function prop_ref_test(&$val) { $val = "modified"; }');
$obj = new stdClass();
$obj->prop = 'original';
prop_ref_test(refval($obj->prop));
echo $obj->prop;
}
?>
--EXPECT--
modified
Loading…
Cancel
Save