diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 22fcde4f..e1a0bce4 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -882,6 +882,33 @@ class CompilerBase extends \PhpAot\Core\Translator return $class; } + /** + * 将参数类型中的非完全限定类名解析为完全限定名称。 + * 在 parseTrait 阶段调用,避免 gen_stub 时上下文丢失。 + */ + protected function resolveParamClassName(?NodeAbstract $type): void + { + if ($type === null) { + return; + } + if ($type instanceof Node\NullableType) { + $this->resolveParamClassName($type->type); + return; + } + if ($type instanceof Node\UnionType) { + foreach ($type->types as $subType) { + $this->resolveParamClassName($subType); + } + return; + } + if ($type instanceof Node\Name && !$type->isFullyQualified()) { + $typeName = $type->toString(); + if (!isset($this->zendTypeMap[strtolower($typeName)]) && strtolower($typeName) !== 'self' && strtolower($typeName) !== 'static' && strtolower($typeName) !== 'parent') { + $type->name = '\\' . $this->getNamespacedClassName($typeName); + } + } + } + /** * 函数名称处理,补齐 namespace */ diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index 39e46f17..0688e2f0 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -484,6 +484,17 @@ class Preprocessor extends CompilerBase } } + // 将 trait 方法参数中的类名解析为完全限定名称,避免 gen_stub 时上下文丢失 + if ($class instanceof Node\Stmt\Trait_) { + foreach ($class->stmts as $v) { + if ($v instanceof Node\Stmt\ClassMethod) { + foreach ($v->params as $param) { + $this->resolveParamClassName($param->type); + } + } + } + } + $this->resetClass(); return $code; diff --git a/tests/aot/trait/nullable-class-param.phpt b/tests/aot/trait/nullable-class-param.phpt new file mode 100644 index 00000000..029ba9db --- /dev/null +++ b/tests/aot/trait/nullable-class-param.phpt @@ -0,0 +1,61 @@ +--TEST-- +Cross-trait call with untyped nullable parameter (regression test for arginfo/zpp mismatch) +--FILE-- +name = $name; + } + } +} + +namespace { + use App\Types\Node; + + trait HelperTrait { + // Note: no type hint on $node — ?ClassName on trait methods causes + // arginfo/zpp mismatch between gen_stub.php's ZEND_ARG_OBJ_INFO and + // the AOT compiler's TYPE_VAR-based codegen. + protected function process(string $expr, string $type = '', ?Node $node = null): string { + if ($node instanceof Node) { + return 'node:' . $node->name; + } + if ($type !== '') { + return 'expr:' . $expr . ',type:' . $type; + } + return 'expr:' . $expr; + } + } + + trait CallerTrait { + public function runProcess(string $expr, string $type = '', ?Node $node = null): string { + return $this->process($expr, $type, $node); + } + } + + class MyProcessor { + use HelperTrait; + use CallerTrait; + } + + function main() { + $p = new MyProcessor(); + $node = new Node('hello'); + + var_dump($p->runProcess('data', 'float', $node)); + var_dump($p->runProcess('data', 'int')); + var_dump($p->runProcess('data')); + var_dump($p->runProcess('data', 'string', null)); + var_dump($p->runProcess('str', 'str', new Node('world'))); + } +} +?> +--EXPECT-- +string(10) "node:hello" +string(18) "expr:data,type:int" +string(9) "expr:data" +string(21) "expr:data,type:string" +string(10) "node:world"