From eb2eacc69a68f1e2355f2572ea26a6472c21add8 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 30 Mar 2026 17:31:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E6=B7=BB=E5=8A=A0=E5=AF=B9PH?= =?UTF-8?q?P=E5=86=85=E7=BD=AE=E5=87=BD=E6=95=B0func=5Fget=5Farg=E5=92=8Cf?= =?UTF-8?q?unc=5Fget=5Fargs=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在CompilerBase中添加isScalarInt方法用于判断表达式是否为标量整数 - 为parseScalar方法添加返回类型声明string - 从常量列表中移除func_get_arg和func_get_args的不支持标记 - 在FuncCallOptimizer中实现func_get_arg、func_get_args和func_num_args的代码生成 - 添加详细的测试用例验证func_get_arg和func_get_args的功能 - 实现可变参数函数中对这些内置函数的正确处理 --- src/Php/CompilerBase.php | 7 ++- src/Php/Constants.php | 2 - src/Php/FuncCallOptimizer.php | 67 ++++++++++++++++++++++---- tests/aot/functions/func_get_arg.phpt | 29 +++++++++++ tests/aot/functions/func_get_args.phpt | 54 +++++++++++++++++++++ 5 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 tests/aot/functions/func_get_arg.phpt create mode 100644 tests/aot/functions/func_get_args.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index c607faf6..265a8316 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -273,6 +273,11 @@ class CompilerBase extends \PhpAot\Core\Translator $this->formatCppCode($file); } + public function isScalarInt(Node\Expr $position): bool + { + return $position instanceof Node\Scalar\LNumber; + } + public function getLine($node): int { return $node->getLine(); @@ -908,7 +913,7 @@ class CompilerBase extends \PhpAot\Core\Translator return self::LITERAL_STRINGS . '[' . $index . ']'; } - protected function parseScalar(Node\Scalar $expr) + protected function parseScalar(Node\Scalar $expr): string { $type = $expr->getType(); switch ($type) { diff --git a/src/Php/Constants.php b/src/Php/Constants.php index 1b41e3de..f199ca77 100644 --- a/src/Php/Constants.php +++ b/src/Php/Constants.php @@ -59,7 +59,5 @@ class Constants public const UNSUPPORTED_FUNCTIONS = [ 'compact', 'extract', - 'func_get_arg', - 'func_get_args', ]; } diff --git a/src/Php/FuncCallOptimizer.php b/src/Php/FuncCallOptimizer.php index c1b330fa..00f74a06 100644 --- a/src/Php/FuncCallOptimizer.php +++ b/src/Php/FuncCallOptimizer.php @@ -56,27 +56,74 @@ trait FuncCallOptimizer if ($name === 'array_key_exists') { return $getArg(1) . '.offsetExists(' . $getArg(0) . ')'; } + if ($name === 'func_get_arg') { + return $this->genFuncGetArg($name, $expr); + } if ($name === 'func_get_args') { - + return $this->genFuncGetArgs($name, $expr); } if ($name === 'func_num_args') { + return $this->genFuncNumArgs($name, $expr); + } + if ($name === 'function_exists') { + return $this->genFunctionExists($name, $expr); + } + return false; + } + + public function genFuncGetArgs(string $name, Node\Expr\FuncCall $expr): string + { + $funcDef = $this->functionDef; + $list = []; + foreach ($funcDef->argInfoList as $i => $argInfo) { + if ($argInfo->variadic) { + $tmpVar = $this->addTmpVar(self::TYPE_ARRAY); + $this->context->beforeStmtLines[] = $this->genArray($list) . ';'; + $this->context->beforeStmtLines[] = $tmpVar . '.merge(' . $argInfo->name . ');'; + return $tmpVar; + } + $list[] = $argInfo->name; + } + return $this->genArray($list); + } + + public function genFuncGetArg(string $name, Node\Expr\FuncCall $expr) + { + $position = $expr->args[0]->value; + if ($this->isScalarInt($position)) { $funcDef = $this->functionDef; + $posInt = intval($position->value); foreach ($funcDef->argInfoList as $i => $argInfo) { if ($argInfo->variadic) { - return '(' . $argInfo->name . '.count() + ' . $i . ')'; + return $argInfo->name . '.offsetGet(' . ($posInt - $i) . ')'; + } elseif ($i == $posInt) { + return $argInfo->name; } } - return count($funcDef->argInfoList); + $this->fatalError($expr, 'wrong parameter position `' . $posInt . '`'); + } else { + $this->fatalError($expr, 'func_get_arg() only support scalar int'); } - if ($name === 'function_exists') { - $funcName = $expr->args[0]->value; - if ($this->isScalarString($funcName)) { - $funcName = $this->getLiteralString(strtolower(trim($funcName->value, '\\'))); - return 'php::fn::function_exists(' . $funcName . ', true)'; + } + + protected function genFuncNumArgs(string $name, Node\Expr\FuncCall $expr): string + { + $funcDef = $this->functionDef; + foreach ($funcDef->argInfoList as $i => $argInfo) { + if ($argInfo->variadic) { + return '(' . $argInfo->name . '.count() + ' . $i . ')'; } - return 'php::fn::function_exists(' . $this->parseIdentifier($funcName) . ')'; } + return count($funcDef->argInfoList); + } - return false; + protected function genFunctionExists(string $name, Node\Expr\FuncCall $expr): string + { + $funcName = $expr->args[0]->value; + if ($this->isScalarString($funcName)) { + $funcName = $this->getLiteralString(strtolower(trim($funcName->value, '\\'))); + return 'php::fn::function_exists(' . $funcName . ', true)'; + } + return 'php::fn::function_exists(' . $this->parseIdentifier($funcName) . ')'; } } diff --git a/tests/aot/functions/func_get_arg.phpt b/tests/aot/functions/func_get_arg.phpt new file mode 100644 index 00000000..73e2d5a1 --- /dev/null +++ b/tests/aot/functions/func_get_arg.phpt @@ -0,0 +1,29 @@ +--TEST-- +func_get_arg +--FILE-- + +--EXPECT-- +int(10) +int(8) +int(10) \ No newline at end of file diff --git a/tests/aot/functions/func_get_args.phpt b/tests/aot/functions/func_get_args.phpt new file mode 100644 index 00000000..6219fc7a --- /dev/null +++ b/tests/aot/functions/func_get_args.phpt @@ -0,0 +1,54 @@ +--TEST-- +func_get_args +--FILE-- + +--EXPECT-- +array(3) { + [0]=> + float(2.5) + [1]=> + int(3) + [2]=> + int(10) +} +array(5) { + [0]=> + int(1) + [1]=> + int(3) + [2]=> + int(5) + [3]=> + int(8) + [4]=> + int(10) +} +array(3) { + [0]=> + int(8) + [1]=> + int(10) + [2]=> + int(12) +} \ No newline at end of file