feat(php): 完善函数返回值类型推断和解析完成状态跟踪

- 添加函数解析完成状态标识符 completed 属性
- 在函数解析完成后设置 completed 标识为 true
- 改进返回值类型变更时的日志输出,显示原类型到新类型的转换
- 优化方法调用返回值检测逻辑,仅在函数已完成解析时使用其返回值类型
- 修复内部函数返回值类型检测,排除 void 类型的干扰
- 更新对象表达式转换逻辑,确保表达式预处理正确执行
- 改进变量赋值错误提示信息,明确显示变量名称
- 添加返回值类型测试用例,验证 void 函数的赋值行为
- 修复命名空间类名获取逻辑,确保类名格式正确
pull/1/head
韩天峰 5 months ago
parent c0e224e4db
commit a5f397efa5
  1. 30
      src/Php/CompilerBase.php
  2. 1
      src/Php/Entity/FunctionDef.php
  3. 2
      src/Php/Translator.php
  4. 13
      tests/aot/return-value.phpt

@ -849,6 +849,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if (!$this->isReturnStmtInLastLine($v->stmts)) {
$stmts .= $this->genReturnCode();
}
$this->functionDef->completed = true;
} catch (Skip) {
$this->climate->cyan('Skip function ' . $name);
}
@ -1719,9 +1720,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function resetReturnType(Node\Stmt\Return_ $node, string $type): void
{
$oriType = $this->functionDef->returnType;
$this->functionDef->returnType = $type;
// 返回值变更,需要重新解析
$this->climate->cyan('Return type changed at line ' . $node->getLine() . ', retrying...');
$this->climate->cyan("Return type changed ($oriType -> $type) at line {$node->getLine()} retrying...");
throw new Redo();
}
@ -1792,9 +1794,12 @@ class CompilerBase extends \PhpAot\Core\Translator
$method = $this->parseIdentifier($expr->name);
$nativeFunc = $this->findNativeMethod($expr, $object, $method);
if ($nativeFunc) {
return $this->nativeFunctions[$nativeFunc]->returnType;
}
if ($this->isTypedObject($object)) {
$funcDef = $this->nativeFunctions[$nativeFunc];
// 此函数尚未完成解析,仅经过初步的预处理,无法获得准确的返回值类型
if ($funcDef->completed) {
return $funcDef->returnType;
}
} elseif ($this->isTypedObject($object)) {
return $this->detectMethodCallReturnType($this->getObjectType($object), $method);
}
}
@ -3579,9 +3584,13 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function detectFuncCallReturnType(string $name): string
{
$returnType = Reflection::getFunctionReturnType($name);
if ($returnType) {
return $this->getTypeFromZendType($returnType);
if ($this->isInternalFunction($name)) {
$returnType = Reflection::getFunctionReturnType($name);
// void 类型将被忽略,类型推测仅用于赋值操作的右值,即使返回值为 void , 赋值操作也应该继续运行,右值会被当做 null
// 例如 $a = var_dump('hello'); 虽然 var_dump 返回值为 void ,但是 $a 的类型是 mixed,值为 null
if ($returnType and $returnType !== 'void') {
return $this->getTypeFromZendType($returnType);
}
}
return self::TYPE_VAR;
}
@ -3589,7 +3598,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function detectMethodCallReturnType(string $class, string $method): string
{
$returnType = Reflection::getMethodReturnType($class, $method);
if ($returnType) {
if ($returnType and $returnType !== 'void') {
return $this->getTypeFromZendType($returnType);
}
return self::TYPE_VAR;
@ -3978,8 +3987,9 @@ class CompilerBase extends \PhpAot\Core\Translator
goto _to_object;
}
} else {
$ex = $this->parseIdentifier($expr->expr);
_to_object:
$ex = $this->convertObjectExpr($expr->expr);
$ex = $this->convertObjectExpr($ex);
}
return 'php::throwException(' . $ex . ')';
}
@ -4208,7 +4218,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->addLocalVar($name, self::TYPE_VAR);
} else {
if ($this->getVarType($name) !== self::TYPE_VAR) {
$this->fatalError($node, 'Cannot assign value to variable of type ' . $this->getVarType($name));
$this->fatalError($node, 'Cannot assign value to variable $' . $name . ' of type ' . $this->getVarType($name));
}
}
}

@ -23,6 +23,7 @@ class FunctionDef
public string $params = '';
public string $namespace = '';
public bool $method = false;
public bool $completed = false;
public function __construct(string $name, string $returnType, string $namespace = '')
{

@ -865,7 +865,7 @@ class Translator extends Preprocessor
$extends = $class->extends;
}
$fullName = $this->getNamespacedClassName($this->class);
$fullName = ltrim($this->namespace . '\\' . $this->class, '\\');
if ($this->hasNativeClass($fullName)) {
$this->classDef = $this->getClassDef($fullName);
} else {

@ -0,0 +1,13 @@
--TEST--
object link operator
--FILE--
<?php
function main()
{
$a = var_dump('foo');
var_dump($a);
}
?>
--EXPECT--
string(3) "foo"
NULL
Loading…
Cancel
Save