From 257616d57183e9f35b36199850e7bed8b5b2b191 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 11 Feb 2026 18:20:19 +0800 Subject: [PATCH] =?UTF-8?q?refactor(compiler):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E8=B0=83=E7=94=A8=E5=92=8C=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除编译器中无用的原生函数检查逻辑 - 更新反射参数检查以支持类方法参数引用 - 添加类型化对象的方法调用处理机制 - 实现匿名类实例继承相关功能 - 修复数组元素递增递减操作处理 - 优化ZipArchive等扩展函数的引用参数处理 - 添加私有属性名称解混淆测试用例 - 扩展反射类以支持类方法参数获取 --- src/Php/AstNodeType.php | 5 ++++ src/Php/CompilerBase.php | 34 ++++++++++++---------- src/Php/Reflection.php | 48 +++++++++++++++++++++++++++---- tests/aot/array-item-dec-inc.phpt | 18 ++++++++++++ tests/aot/ref-call-arg.phpt | 42 +++++++++++++++++++++++++++ tests/zend/anon/011.phpt | 16 +++++++++++ tests/zend/anon/012.phpt | 11 +++++++ 7 files changed, 154 insertions(+), 20 deletions(-) create mode 100644 tests/aot/array-item-dec-inc.phpt create mode 100644 tests/aot/ref-call-arg.phpt create mode 100644 tests/zend/anon/011.phpt create mode 100644 tests/zend/anon/012.phpt diff --git a/src/Php/AstNodeType.php b/src/Php/AstNodeType.php index 241db5db..aee2488e 100644 --- a/src/Php/AstNodeType.php +++ b/src/Php/AstNodeType.php @@ -49,6 +49,11 @@ trait AstNodeType return $expr instanceof Node\Name; } + protected function isNamedMethod(NodeAbstract $expr): bool + { + return $this->isIdExpr($expr); + } + protected function isScalarString(NodeAbstract $expr): bool { return $expr instanceof Node\Scalar\String_; diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 812ab7de..7271af66 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -1948,12 +1948,6 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseCallArgs(array $args, string $funcName = '', string $className = ''): string { - if (!$className) { - if ($this->isNativeFunction($funcName)) { - return $this->parseNativeCallArgs($args, $funcName); - } - } - $list_args = []; foreach ($args as $i => $arg) { if ($arg->name !== null) { @@ -1961,7 +1955,7 @@ class CompilerBase extends \PhpAot\Core\Translator } if ($this->isVarExpr($arg->value)) { $name = $this->parseIdentifier($arg->value); - if ($funcName and Reflection::isReferenceArg($funcName, $i)) { + if ($funcName and Reflection::isReferenceArg($funcName, $className, $i)) { if (!$this->hasVar($name)) { // 若参数是引用类型,可以传入未定义变量,将立即创建变量作为引用 $this->addLocalVar($name, self::TYPE_REF); @@ -1969,10 +1963,10 @@ class CompilerBase extends \PhpAot\Core\Translator // 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数 $tmpVar = $this->genTmpVarName(); $this->addLocalVar($tmpVar, self::TYPE_REF); + $this->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();'; $name = $tmpVar; } - $this->beforeStmtLines[] = $name . ' = ' . $this->parseExpr($arg->value) . '.toReference();'; - $list_args[] = '&' . $name; + $list_args[] = '&' . $name; continue; } if (!$this->hasVar($name)) { @@ -1983,7 +1977,7 @@ class CompilerBase extends \PhpAot\Core\Translator if (!$this->hasVar($obj)) { $this->fatalError($arg, 'Undefined variable `$' . $obj . '`'); } - if ($funcName and Reflection::isReferenceArg($funcName, $i)) { + if ($funcName and Reflection::isReferenceArg($funcName, $className, $i)) { $list_args[] = $obj . '.attrRef(' . $this->identifierToStr($arg->value->name) . ')'; continue; } @@ -1992,7 +1986,7 @@ class CompilerBase extends \PhpAot\Core\Translator if ($this->isVarExpr($arg->value->var) and !$this->hasVar($array)) { $this->fatalError($arg, 'Undefined variable `$' . $array . '`'); } - if ($funcName and Reflection::isReferenceArg($funcName, $i)) { + if ($funcName and Reflection::isReferenceArg($funcName, $className, $i)) { if ($arg->value->dim === null) { $this->fatalError($arg, 'Array dimension must be a constant expression'); } @@ -3122,16 +3116,26 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseMethodCall(Node\Expr\MethodCall $expr): string { - $object = $this->convertToObject($expr->var); - $method = $this->parseIdentifier($expr->name); + $object = $this->convertToObject($expr->var); + if ($this->isTypedObject($object)) { + $class = $this->getObjectType($object); + } else { + $class = ''; + } + $method = $this->identifierToStr($expr->name); $nativeFunc = $this->findNativeMethod($expr, $object, $method); if ($nativeFunc) { return $this->parseNativeMethodCall($object, $nativeFunc, $expr->args); } if (empty($expr->args)) { - return $object . '.exec("' . $method . '")'; + return $object . '.exec(' . $method . ')'; + } + if ($this->isNamedMethod($expr->name)) { + $funcName = $this->parseIdentifier($expr->name); + } else { + $funcName = ''; } - return $object . '.exec("' . $method . '", ' . $this->parseCallArgs($expr->args) . ')'; + return $object . '.exec(' . $method . ', ' . $this->parseCallArgs($expr->args, $funcName, $class) . ')'; } protected function identifierToStr(NodeAbstract $node, bool $require = true): string diff --git a/src/Php/Reflection.php b/src/Php/Reflection.php index efadf004..9dc59717 100644 --- a/src/Php/Reflection.php +++ b/src/Php/Reflection.php @@ -8,15 +8,19 @@ namespace PhpAot\Php; +use ReflectionClass; +use ReflectionFunction; + class Reflection { private static array $functions = []; + private static array $classes = []; - public static function getFunction(string $fn) + public static function getFunction(string $fn): ?ReflectionFunction { if (!isset(self::$functions[$fn])) { try { - $ref = new \ReflectionFunction($fn); + $ref = new ReflectionFunction($fn); } catch (\ReflectionException $e) { return null; } @@ -26,6 +30,20 @@ class Reflection return self::$functions[$fn]; } + public static function getClass(string $className): ?ReflectionClass + { + if (!isset(self::$classes[$className])) { + try { + $ref = new ReflectionClass($className); + } catch (\ReflectionException $e) { + return null; + } + self::$classes[$className] = $ref; + } + + return self::$classes[$className]; + } + public static function getFunctionReturnType(string $fn): ?string { $func = self::getFunction($fn); @@ -57,13 +75,33 @@ class Reflection return $args[$index]; } - public static function isReferenceArg(string $fn, int $index): ?string + public static function getClassMethodParameter(string $className, string $fn, int $index): ?\ReflectionParameter { - $param = self::getFunctionParameter($fn, $index); - if (!$param) { + $classRef = self::getClass($className); + if (!$classRef) { return null; } + $method = $classRef->getMethod($fn); + if (!$method) { + return null; + } + $args = $method->getParameters(); + if ($index >= count($args)) { + return null; + } + return $args[$index]; + } + public static function isReferenceArg(string $fnName, string $className, int $index): ?string + { + if ($className) { + $param = self::getClassMethodParameter($className, $fnName, $index); + } else { + $param = self::getFunctionParameter($fnName, $index); + } + if (!$param) { + return null; + } return $param->isPassedByReference() ? $param->getName() : null; } } diff --git a/tests/aot/array-item-dec-inc.phpt b/tests/aot/array-item-dec-inc.phpt new file mode 100644 index 00000000..ec5a72ec --- /dev/null +++ b/tests/aot/array-item-dec-inc.phpt @@ -0,0 +1,18 @@ +--TEST-- +array item inc/dec +--FILE-- + +--EXPECT-- +int(1000) +int(999) +int(1000) diff --git a/tests/aot/ref-call-arg.phpt b/tests/aot/ref-call-arg.phpt new file mode 100644 index 00000000..8aa044b5 --- /dev/null +++ b/tests/aot/ref-call-arg.phpt @@ -0,0 +1,42 @@ +--TEST-- +ref call arg +--FILE-- +open(__DIR__ . '/../../examples/test.zip') === TRUE) { + for ($idx = 0; $s = $zip->statIndex($idx); $idx++) { + $rs = $zip->getExternalAttributesIndex($idx, $opsys, $attr); + var_dump($rs, $idx, $opsys, $attr); + } + $zip->close(); + echo "OK\n"; + } + + $str = "first=value&arr[]=foo+bar&arr[]=baz"; + parse_str($str, $output); + echo $output['first'], PHP_EOL; // value + echo $output['arr'][0], PHP_EOL; // foo bar + echo $output['arr'][1], PHP_EOL; // baz + echo "DONE\n"; +} +?> +--EXPECT-- +bool(true) +int(0) +int(3) +int(1107099648) +bool(true) +int(1) +int(3) +int(2176057344) +bool(true) +int(2) +int(3) +int(2176057344) +OK +value +foo bar +baz +DONE diff --git a/tests/zend/anon/011.phpt b/tests/zend/anon/011.phpt new file mode 100644 index 00000000..44a0f97c --- /dev/null +++ b/tests/zend/anon/011.phpt @@ -0,0 +1,16 @@ +--TEST-- +Ensure proper inheritance with get_class(anon class instance) used via class_alias (see also bug #70106) +--FILE-- +foo; + } + })->getFoo()); +} +?> +--EXPECT-- +int(1) diff --git a/tests/zend/anon/012.phpt b/tests/zend/anon/012.phpt new file mode 100644 index 00000000..d433a5bb --- /dev/null +++ b/tests/zend/anon/012.phpt @@ -0,0 +1,11 @@ +--TEST-- +Ensure correct unmangling of private property names for anonymous class instances +--FILE-- + +--EXPECTF-- +object(_anon_class_%s)#1 (1) { + ["foo":"_anon_class_%s":private]=> + NULL +}