diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 86eb3494..7cb3df0a 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -1277,12 +1277,13 @@ class CompilerBase extends \PhpAot\Core\Translator $this->addObject($var, $rightClass); } elseif ($this->isTypedObject($var)) { $leftClass = $this->getObjectType($var); - // 对象的类不一致,不能互相赋值 + // 对象的类不一致,不能互相赋值,必须使用 objval() 对齐类型 + // 注意这里必须使用绝对相等比较,即使存在继承关系,类的方法也可能不一致 if ($leftClass !== $rightClass) { $this->fatalError($left, "Cannot re-assign typed object `\${$var}` from `{$leftClass}` to `{$rightClass}`"); } } else { - $this->fatalError($left, "Cannot re-assign `\${$var}` to object of `{$rightClass}`"); + // TODO 右值是一个类型对象,但左值是一个 var ,许可,但无法标记对象类型 } } else { if ($this->isFuncCallExpr($right) and $this->isNameExpr($right->name)) { @@ -1326,7 +1327,11 @@ class CompilerBase extends \PhpAot\Core\Translator } if (!$this->hasVar($var)) { $this->addLocalVar($var, $type); - } elseif ($this->getVarType($var) !== self::TYPE_VAR and $this->isTypedObject($var) and $type !== self::TYPE_OBJECT) { + } elseif (($this->getVarType($var) !== self::TYPE_VAR and $this->isTypedObject($var) and $type !== self::TYPE_OBJECT) + // 禁止字符串与数组互相转换,其他类型如对象可以使用 __toString() 协议转为字符串,整数和浮点型也可以转为字符串 + or ($this->getVarType($var) === self::TYPE_STR and $type === self::TYPE_ARRAY) + or ($this->getVarType($var) === self::TYPE_ARRAY and $type === self::TYPE_STR) + ) { $this->fatalError($left, "Cannot re-assign variable `\${$var}` from " . $this->getVarType($var) . ' to ' . $type); } } @@ -1491,8 +1496,40 @@ class CompilerBase extends \PhpAot\Core\Translator } if ($this->isFuncCallExpr($expr) and $this->isNameExpr($expr->name)) { $fn = $this->parseIdentifier($expr->name); - if (count($expr->args) === 2 and $fn === 'objval' and $this->isScalarString($expr->args[1]->value)) { - return $this->getNamespacedClassName($this->parseIdentifier($expr->args[1]->value)); + if (count($expr->args) === 2 and $fn === 'objval') { + $argClass = $expr->args[1]->value; + if ($this->isScalarString($argClass)) { + return $this->getNamespacedClassName($this->parseIdentifier($argClass)); + } elseif ($this->isClassConstFetch($argClass)) { + if ($this->isNameExpr($argClass->class) and $this->isIdExpr($argClass->name) and $this->parseIdentifier($argClass->name) === 'class') { + return $this->getNamespacedClassName($this->parseIdentifier($argClass->class)); + } + } else { + $this->fatalError($expr, 'The second parameter of objval() function only supports string literals or `ClassName::class` constant'); + } + } + } + if ($this->isMethodCall($expr) and $this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) { + $object = $this->parseVariable($expr->var); + try { + $nativeFunc = $this->findNativeMethod($expr, $object, $this->parseIdentifier($expr->name)); + if ($nativeFunc) { + return $this->getFunction($nativeFunc)->returnClass; + } + } catch (DynamicCall) {} + } + if ($this->isStaticCall($expr) and $this->isNameExpr($expr->class) and $this->isNamedMethod($expr->name)) { + $class = $this->parseIdentifier($expr->class); + if ($class === 'self') { + $class = $this->class; + } elseif ($class === 'static' or $class === 'parent') { + return ''; + } + $class = $this->getNamespacedClassName($class); + $method = $this->parseIdentifier($expr->name); + $nativeFunc = $this->getNativeMethod($expr, $class, $method); + if ($nativeFunc) { + return $this->getFunction($nativeFunc)->returnClass; } } return ''; @@ -1526,7 +1563,7 @@ class CompilerBase extends \PhpAot\Core\Translator $returnClass = $this->getReturnClass(); if ($returnClass) { if (!$objectClass) { - $this->fatalError($v, 'The return value must be an instance of the `' . $returnClass . '` class'); + // TODO 返回值的类型无法确定,需要插入动态类型检测代码 } elseif (!$this->isInheritedFrom($objectClass, $returnClass)) { $this->fatalError($v, 'The return type is `' . $returnClass . '`, cannot return an instance of `' . $objectClass . '`'); } diff --git a/src/Php/Translator.php b/src/Php/Translator.php index b13d2de6..fbd1a73d 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -567,6 +567,12 @@ class Translator extends Preprocessor $constDef = $expr->getAttribute('nativeConst'); return $this->genCValue($constDef->valueExpr->value); } + if ($this->isInternalClass($class)) { + $constName = $class . '::' . $name; + if (defined($constName)) { + return $this->genCValue(constant($constName)); + } + } throw new \Exception("Class constant `{$class}::{$name}` not found"); } diff --git a/src/gen_stub.php b/src/gen_stub.php index e630193e..a9239092 100755 --- a/src/gen_stub.php +++ b/src/gen_stub.php @@ -2310,8 +2310,9 @@ class EvaluatedValue } } elseif ($expr->name->__toString() === 'class') { return $class; + } else { + return getTranslator()->getClassConstValue($expr, $class, $expr->name->__toString()); } - goto _error; } else { $constName = $expr->name->__toString(); if (strtolower($constName) === "unknown") { diff --git a/tests/aot/type_decl/007.phpt b/tests/aot/type_decl/007.phpt new file mode 100644 index 00000000..e6f5a9d8 --- /dev/null +++ b/tests/aot/type_decl/007.phpt @@ -0,0 +1,34 @@ +--TEST-- +Type Declarations +--FILE-- +name = $name; + } + } +} + +namespace TestApp\Command { + use TestApp\Console\Command; + class Test { + public function callCommand(string $class): Command + { + $array = ['object' => new Command($class)]; + return objval($array['object'], Command::class); + } + } +} +namespace { + function main() { + $o = new TestApp\Command\Test; + $o2 = $o->callCommand('TestApp\Console\Command'); + var_dump($o2->name); + } +} +?> +--EXPECT-- +string(23) "TestApp\Console\Command" \ No newline at end of file