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 +}