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