From e7680c5e81fece9b6fa43038e3ad41c8f4c5cf82 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 4 Jun 2026 16:11:30 +0800 Subject: [PATCH] =?UTF-8?q?feat(php):=20=E6=89=A9=E5=B1=95=20refval=20?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E6=94=AF=E6=8C=81=E6=95=B0=E7=BB=84=E5=85=83?= =?UTF-8?q?=E7=B4=A0=E5=92=8C=E5=AF=B9=E8=B1=A1=E5=B1=9E=E6=80=A7=E5=BC=95?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加对 refval($arr[key]) 数组元素引用的支持 - 添加对 refval($obj->prop) 对象属性引用的支持 - 实现 expandRefvalExpr 方法处理复杂引用表达式 - 增强错误检查验证 refval 参数类型 - 添加相应的单元测试用例 --- src/Php/CompilerBase.php | 64 ++++++++++++++++++++++++++++---- tests/aot/ref/refval-array.phpt | 15 ++++++++ tests/aot/ref/refval-object.phpt | 16 ++++++++ 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 tests/aot/ref/refval-array.phpt create mode 100644 tests/aot/ref/refval-object.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 1dc90cac..2c21ba02 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -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)) { diff --git a/tests/aot/ref/refval-array.phpt b/tests/aot/ref/refval-array.phpt new file mode 100644 index 00000000..ed4721f9 --- /dev/null +++ b/tests/aot/ref/refval-array.phpt @@ -0,0 +1,15 @@ +--TEST-- +refval with array element +--FILE-- + 'original']; + array_ref_test(refval($arr['key'])); + echo $arr['key']; +} +?> +--EXPECT-- +modified diff --git a/tests/aot/ref/refval-object.phpt b/tests/aot/ref/refval-object.phpt new file mode 100644 index 00000000..ef90633c --- /dev/null +++ b/tests/aot/ref/refval-object.phpt @@ -0,0 +1,16 @@ +--TEST-- +refval with object property +--FILE-- +prop = 'original'; + prop_ref_test(refval($obj->prop)); + echo $obj->prop; +} +?> +--EXPECT-- +modified