返回类不匹配时,必须是抽象类

pull/1/head
韩天峰 4 months ago
parent 954c0f399b
commit 42dae8ceaa
  1. 20
      src/Php/CompilerBase.php
  2. 6
      src/Php/Entity/ClassDef.php
  3. 9
      src/Php/Reflection.php
  4. 30
      tests/aot/dynamic_call/return-abstract-class.phpt
  5. 34
      tests/aot/dynamic_call/return-base-class-001.phpt
  6. 34
      tests/aot/dynamic_call/return-base-class-002.phpt

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

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

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

@ -0,0 +1,30 @@
--TEST--
return abstract class
--FILE--
<?php
abstract class Base {
abstract public function foo();
}
class User extends Base {
public function foo()
{
return 'foo';
}
}
class Bar {
public function getUser() : Base {
return new User();
}
}
function main()
{
$bar = new Bar();
$user = $bar->getUser();
var_dump($user->foo());
}
?>
--EXPECT--
string(3) "foo"

@ -0,0 +1,34 @@
--TEST--
return abstract class
--SKIPIF--
<?php die("skip"); ?>
--FILE--
<?php
class Base {
public function foo() {
return 'base';
}
}
class User extends Base {
public function foo()
{
return 'user';
}
}
class Bar {
public function getUser() : Base {
return new User();
}
}
function main()
{
$bar = new Bar();
$user = $bar->getUser();
var_dump($user->foo());
}
?>
--EXPECT--
string(3) "foo"

@ -0,0 +1,34 @@
--TEST--
return abstract class
--SKIPIF--
<?php die("skip"); ?>
--FILE--
<?php
class Base {
public function test() {
return 'base';
}
}
class User extends Base {
public function foo()
{
return 'user';
}
}
class Bar {
public function getUser() : Base {
return new User();
}
}
function main()
{
$bar = new Bar();
$user = $bar->getUser();
var_dump($user->foo());
}
?>
--EXPECT--
string(3) "foo"
Loading…
Cancel
Save