feat(aot): 增强类型推断支持接口类型

- 添加接口类型检测功能,实现 hasInterface 方法
- 修改对象赋值逻辑,对接口类型变量进行特殊处理
- 增强返回值类型检查,支持接口类型的动态类型检测
- 更新类继承关系检查,支持接口实现关系验证
- 添加测试用例验证接口类型推断功能
- 修复接口名称存储时的转义问题
- 默认开启错误回溯打印功能便于调试
pull/1/head
韩天峰 4 months ago
parent 45a4582d03
commit 26dfc99094
  1. 49
      src/Php/CompilerBase.php
  2. 10
      src/Php/Translator.php
  3. 33
      tests/aot/type_hits/004.phpt
  4. 49
      tests/aot/type_hits/005.phpt

@ -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);
}
}

@ -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;
}

@ -0,0 +1,33 @@
--TEST--
type hits
--FILE--
<?php
interface TransportInterface
{
public function send(string $message, ?string $envelope = null);
}
class FooA implements TransportInterface {
public function send(string $message, ?string $envelope = null)
{
var_dump($message, $envelope);
}
}
function create_transport() : TransportInterface {
return new FooA;
}
function configure_transport(FooA $o) {
$o->send('Hello World', 'foo@bar');
}
function main()
{
$obj = create_transport();
configure_transport($obj);
}
?>
--EXPECT--
string(11) "Hello World"
string(7) "foo@bar"

@ -0,0 +1,49 @@
--TEST--
type hits
--FILE--
<?php
namespace Symfony\Component\Mailer\Transport {
interface TransportInterface
{
public function send(string $message, ?string $envelope = null);
}
}
namespace Symfony\Component\Mailer\Transport\Smtp {
use Symfony\Component\Mailer\Transport\TransportInterface;
class EsmtpTransport implements TransportInterface {
public function send(string $message, ?string $envelope = null)
{
var_dump($message, $envelope);
}
}
}
namespace {
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
class Baz {
function create_transport() : TransportInterface {
return new EsmtpTransport;
}
}
class FooB {
function configure(FooA $o) {
$o->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"
Loading…
Cancel
Save