feat(compiler): 添加对PHP内置函数func_get_arg和func_get_args的支持

- 在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的功能
- 实现可变参数函数中对这些内置函数的正确处理
pull/1/head
韩天峰 5 months ago
parent 898cbe2553
commit eb2eacc69a
  1. 7
      src/Php/CompilerBase.php
  2. 2
      src/Php/Constants.php
  3. 57
      src/Php/FuncCallOptimizer.php
  4. 29
      tests/aot/functions/func_get_arg.phpt
  5. 54
      tests/aot/functions/func_get_args.phpt

@ -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) {

@ -59,7 +59,5 @@ class Constants
public const UNSUPPORTED_FUNCTIONS = [
'compact',
'extract',
'func_get_arg',
'func_get_args',
];
}

@ -56,10 +56,58 @@ 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 . '.offsetGet(' . ($posInt - $i) . ')';
} elseif ($i == $posInt) {
return $argInfo->name;
}
}
$this->fatalError($expr, 'wrong parameter position `' . $posInt . '`');
} else {
$this->fatalError($expr, 'func_get_arg() only support scalar int');
}
}
protected function genFuncNumArgs(string $name, Node\Expr\FuncCall $expr): string
{
$funcDef = $this->functionDef;
foreach ($funcDef->argInfoList as $i => $argInfo) {
if ($argInfo->variadic) {
@ -68,7 +116,9 @@ trait FuncCallOptimizer
}
return count($funcDef->argInfoList);
}
if ($name === 'function_exists') {
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, '\\')));
@ -76,7 +126,4 @@ trait FuncCallOptimizer
}
return 'php::fn::function_exists(' . $this->parseIdentifier($funcName) . ')';
}
return false;
}
}

@ -0,0 +1,29 @@
--TEST--
func_get_arg
--FILE--
<?php
function foo1($a, $b, $c = 10)
{
var_dump(func_get_arg(2));
}
function foo2(...$args) {
var_dump(func_get_arg(3));
}
function foo3($a, $b, $c, ...$args) {
var_dump(func_get_arg(4));
}
function main()
{
foo1(2.5, 3);
foo2(1, 3, 5, 8, 10);
foo3(1, 3, 5, 8, 10, 12);
}
?>
--EXPECT--
int(10)
int(8)
int(10)

@ -0,0 +1,54 @@
--TEST--
func_get_args
--FILE--
<?php
function foo1($a, $b, $c = 10)
{
var_dump(func_get_args());
}
function foo2(...$args) {
var_dump(func_get_args());
}
function foo3($a, $b, $c, ...$args) {
var_dump(func_get_args());
}
function main()
{
foo1(2.5, 3);
foo2(1, 3, 5, 8, 10);
foo3(1, 3, 5, 8, 10, 12);
}
?>
--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)
}
Loading…
Cancel
Save