From 26dfc9909421267fc14088c797c95f85f320b4aa Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 17 Apr 2026 11:03:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(aot):=20=E5=A2=9E=E5=BC=BA=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E6=8E=A8=E6=96=AD=E6=94=AF=E6=8C=81=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加接口类型检测功能,实现 hasInterface 方法 - 修改对象赋值逻辑,对接口类型变量进行特殊处理 - 增强返回值类型检查,支持接口类型的动态类型检测 - 更新类继承关系检查,支持接口实现关系验证 - 添加测试用例验证接口类型推断功能 - 修复接口名称存储时的转义问题 - 默认开启错误回溯打印功能便于调试 --- src/Php/CompilerBase.php | 49 +++++++++++++++++++++++++----------- src/Php/Translator.php | 10 ++++---- tests/aot/type_hits/004.phpt | 33 ++++++++++++++++++++++++ tests/aot/type_hits/005.phpt | 49 ++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 tests/aot/type_hits/004.phpt create mode 100644 tests/aot/type_hits/005.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index df5f3753..5b164d95 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -164,7 +164,7 @@ class CompilerBase extends \PhpAot\Core\Translator protected string $ldflags = ''; protected int $floatPrecision = 17; protected bool $debugInfo = true; - protected bool $printBacktraceOnError = false; + protected bool $printBacktraceOnError = true; protected bool $noLiteralStrings = false; protected string $file; protected string $dir; @@ -1289,7 +1289,10 @@ class CompilerBase extends \PhpAot\Core\Translator if ($rightClass) { if (!$this->hasVar($var)) { $this->addLocalVar($var, self::TYPE_OBJECT); - $this->addObject($var, $rightClass); + // TODO 返回值类型是一个接口,只能作为 var 变量,无法作为 TypedObject + if (!$this->hasInterface($rightClass)) { + $this->addObject($var, $rightClass); + } } elseif ($this->isTypedObject($var)) { $leftClass = $this->getObjectType($var); // 对象的类不一致,不能互相赋值,必须使用 objval() 对齐类型 @@ -1579,8 +1582,8 @@ class CompilerBase extends \PhpAot\Core\Translator $objectClass = $this->detectClassOfExpr($v->expr); $returnClass = $this->getReturnClass(); if ($returnClass) { - if (!$objectClass) { - // TODO 返回值的类型无法确定,需要插入动态类型检测代码 + if (!$objectClass or $this->hasInterface($objectClass)) { + // TODO 返回值的类型无法确定,或者是一个接口,无法继承关系,需要插入动态类型检测代码 } elseif (!$this->isInheritedFrom($objectClass, $returnClass)) { $this->fatalError($v, 'The return type is `' . $returnClass . '`, cannot return an instance of `' . $objectClass . '`'); } @@ -1709,6 +1712,11 @@ class CompilerBase extends \PhpAot\Core\Translator return array_key_exists($this->escapeClass($name), $this->classes); } + protected function hasInterface(string $name): bool + { + return array_key_exists($this->escapeClass($name), $this->interfaces); + } + protected function checkFunction(string $name): void { // 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误 @@ -3259,24 +3267,35 @@ class CompilerBase extends \PhpAot\Core\Translator protected function isInheritedFrom(string $class, string $expected): bool { $internal = ($this->isInternalClass($expected) or $this->isInternalInterface($expected)); - + $isInterface = $this->hasInterface($expected); + // 类不存在,说明这是一个动态类,跳过静态检查,需要运行时检查 + if (!$this->hasClass($class)) { + return true; + } + $classDef = $this->getClass($class); while (true) { - if (strcasecmp($class, $expected) === 0) { - return true; - } - if (!$this->hasClass($class)) { - // 原生类继承自一个内置类,例如: UserError extends Exception ,然后 $expected 预期是 Throwable - // 这种情况,需要使用 ZendVM 获取继承关系 - if ($this->isInternalClass($class) and $internal) { - return $class === $expected or is_subclass_of($class, $expected); + if ($isInterface) { + if ($classDef->implements and in_array($expected, $classDef->implements)) { + return true; + } + } else { + if (strcasecmp($class, $expected) === 0) { + return true; + } + if (!$this->hasClass($class)) { + // 原生类继承自一个内置类,例如: UserError extends Exception ,然后 $expected 预期是 Throwable + // 这种情况,需要使用 ZendVM 获取继承关系 + if ($this->isInternalClass($class) and $internal) { + return $class === $expected or is_subclass_of($class, $expected); + } + return false; } - return false; } - $classDef = $this->getClass($class); if (!$classDef->extends) { return false; } $class = $classDef->extends; + $classDef = $this->getClass($class); } } diff --git a/src/Php/Translator.php b/src/Php/Translator.php index f91099ea..7cf693fe 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -1450,11 +1450,11 @@ class Translator extends Preprocessor protected function parseInterface(Node\Stmt\Interface_ $v): void { - $name = $this->parseIdentifier($v->name); - $this->interface = $name; - $this->interfaceDef = new InterfaceDef($name, $this->namespace); - $interfaceName = $this->interfaceDef->getNamespacedName(); - $this->interfaces[$interfaceName] = $this->interfaceDef; + $name = $this->parseIdentifier($v->name); + $this->interface = $name; + $this->interfaceDef = new InterfaceDef($name, $this->namespace); + $interfaceName = $this->interfaceDef->getNamespacedName(); + $this->interfaces[$this->escapeClass($interfaceName)] = $this->interfaceDef; $this->interfacesDefineInFile[$interfaceName] = $this->interfaceDef; } diff --git a/tests/aot/type_hits/004.phpt b/tests/aot/type_hits/004.phpt new file mode 100644 index 00000000..50cab4fa --- /dev/null +++ b/tests/aot/type_hits/004.phpt @@ -0,0 +1,33 @@ +--TEST-- +type hits +--FILE-- +send('Hello World', 'foo@bar'); +} + +function main() +{ + $obj = create_transport(); + configure_transport($obj); +} +?> +--EXPECT-- +string(11) "Hello World" +string(7) "foo@bar" diff --git a/tests/aot/type_hits/005.phpt b/tests/aot/type_hits/005.phpt new file mode 100644 index 00000000..a1f8faff --- /dev/null +++ b/tests/aot/type_hits/005.phpt @@ -0,0 +1,49 @@ +--TEST-- +type hits +--FILE-- +send('Hello World', 'foo@bar'); + } +} + +function main() +{ + $baz = new Baz; + $obj = $baz->create_transport(); + $foo = new FooB; + $foo->configure($obj); +} +} +?> +--EXPECT-- +string(11) "Hello World" +string(7) "foo@bar"