From 42dae8ceaa14ccae2772442253166b93ea800789 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sat, 9 May 2026 15:53:46 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=94=E5=9B=9E=E7=B1=BB=E4=B8=8D=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E6=97=B6=EF=BC=8C=E5=BF=85=E9=A1=BB=E6=98=AF=E6=8A=BD?= =?UTF-8?q?=E8=B1=A1=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Php/CompilerBase.php | 20 +++++++++-- src/Php/Entity/ClassDef.php | 6 ++++ src/Php/Reflection.php | 9 +++++ .../dynamic_call/return-abstract-class.phpt | 30 ++++++++++++++++ .../dynamic_call/return-base-class-001.phpt | 34 +++++++++++++++++++ .../dynamic_call/return-base-class-002.phpt | 34 +++++++++++++++++++ 6 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 tests/aot/dynamic_call/return-abstract-class.phpt create mode 100644 tests/aot/dynamic_call/return-base-class-001.phpt create mode 100644 tests/aot/dynamic_call/return-base-class-002.phpt diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index a4829813..10cc1248 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -173,7 +173,7 @@ class CompilerBase extends \PhpAot\Core\Translator protected string $ldflags = ''; protected int $floatPrecision = 17; protected bool $debug = false; - protected bool $formatCode = false; + protected bool $formatCode = true; protected bool $printBacktraceOnError = false; protected bool $noLiteralStrings = false; protected bool $noConsole = false; // Windows: hide console window @@ -1490,7 +1490,7 @@ class CompilerBase extends \PhpAot\Core\Translator if (!$this->hasVar($var)) { $this->addLocalVar($var, self::TYPE_OBJECT); // TODO 返回值类型是一个接口,只能作为 var 变量,无法作为 TypedObject - if (!$this->hasInterface($rightClass)) { + if (!$this->hasInterface($rightClass) and !$this->isAbstractClass($rightClass)) { $this->addObject($var, $rightClass); } } elseif ($this->isTypedObject($var)) { @@ -1616,6 +1616,18 @@ class CompilerBase extends \PhpAot\Core\Translator return Reflection::isInternalClass($name); } + protected function isAbstractClass(string $name): bool + { + if ($this->isInternalClass($name)) { + return Reflection::isAbstractClass($name); + } + if ($this->hasClass($name)) { + $classDef = $this->getClass($name); + return $classDef->isAbstract(); + } + return false; + } + protected function isInternalInterface(string $name): bool { return Reflection::isInternalInterface($name); @@ -1788,6 +1800,10 @@ class CompilerBase extends \PhpAot\Core\Translator } elseif (!$this->isInheritedFrom($objectClass, $returnClass)) { $this->fatalError($v, 'The return type is `' . $returnClass . '`, cannot return an instance of `' . $objectClass . '`'); } + // 把子类当做父类返回时,父类必须是抽象类 + if ($objectClass !== $returnClass and !$this->isAbstractClass($returnClass)) { + $this->fatalError($v, 'When returning a subclass instance as its parent type, the parent class must be abstract'); + } } $exprCode = $this->convertExprType($expr, $returnType, $type); diff --git a/src/Php/Entity/ClassDef.php b/src/Php/Entity/ClassDef.php index 0203bc42..9af717da 100644 --- a/src/Php/Entity/ClassDef.php +++ b/src/Php/Entity/ClassDef.php @@ -9,6 +9,7 @@ namespace PhpAot\Php\Entity; use PhpAot\Php\Context\FunctionContext; +use PhpParser\Modifiers; use PhpParser\Node\Stmt\Trait_; class ClassDef extends ClassLikeDef @@ -91,4 +92,9 @@ class ClassDef extends ClassLikeDef { return $this->constants[$name]; } + + public function isAbstract(): bool + { + return $this->flags & Modifiers::ABSTRACT; + } } diff --git a/src/Php/Reflection.php b/src/Php/Reflection.php index e05dd2c0..863232d1 100644 --- a/src/Php/Reflection.php +++ b/src/Php/Reflection.php @@ -187,4 +187,13 @@ class Reflection $methodDef = $classRef->getMethod($method); return $methodDef->getReturnType() ? $methodDef->getReturnType()->getName() : null; } + + public static function isAbstractClass(string $name): bool + { + $class = self::getClass($name); + if (!$class) { + return false; + } + return $class->isAbstract(); + } } diff --git a/tests/aot/dynamic_call/return-abstract-class.phpt b/tests/aot/dynamic_call/return-abstract-class.phpt new file mode 100644 index 00000000..7a7ac3da --- /dev/null +++ b/tests/aot/dynamic_call/return-abstract-class.phpt @@ -0,0 +1,30 @@ +--TEST-- +return abstract class +--FILE-- +getUser(); + var_dump($user->foo()); +} +?> +--EXPECT-- +string(3) "foo" \ No newline at end of file diff --git a/tests/aot/dynamic_call/return-base-class-001.phpt b/tests/aot/dynamic_call/return-base-class-001.phpt new file mode 100644 index 00000000..81e81fad --- /dev/null +++ b/tests/aot/dynamic_call/return-base-class-001.phpt @@ -0,0 +1,34 @@ +--TEST-- +return abstract class +--SKIPIF-- + +--FILE-- +getUser(); + var_dump($user->foo()); +} +?> +--EXPECT-- +string(3) "foo" \ No newline at end of file diff --git a/tests/aot/dynamic_call/return-base-class-002.phpt b/tests/aot/dynamic_call/return-base-class-002.phpt new file mode 100644 index 00000000..4d6a60d1 --- /dev/null +++ b/tests/aot/dynamic_call/return-base-class-002.phpt @@ -0,0 +1,34 @@ +--TEST-- +return abstract class +--SKIPIF-- + +--FILE-- +getUser(); + var_dump($user->foo()); +} +?> +--EXPECT-- +string(3) "foo" \ No newline at end of file