From 88693dcc21a40a9a37f1ade5aeef690a6f056df4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 14 Apr 2026 16:44:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(aot):=20=E6=B7=BB=E5=8A=A0=E5=AF=B9=20PHP?= =?UTF-8?q?=20Trait=20=E7=89=B9=E6=80=A7=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了 Trait 的基本语法解析和处理功能 - 添加了 trait 属性、方法、常量的继承机制 - 实现了 trait 使用时的命名空间解析 - 添加了类与 trait 同名成员的冲突处理逻辑 - 新增 trait 相关的测试用例 - 重构了类定义结构以支持 trait 类型判断 --- src/Php/Entity/ClassDef.php | 3 ++- src/Php/Preprocessor.php | 2 +- src/Php/Translator.php | 31 ++++++++++++++++++++++++++++ src/gen_stub.php | 5 +++++ tests/aot/trait/001.phpt | 40 +++++++++++++++++++++++++++++++++++++ tests/aot/trait/002.phpt | 17 ++++++++++++++++ 6 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 tests/aot/trait/001.phpt create mode 100644 tests/aot/trait/002.phpt diff --git a/src/Php/Entity/ClassDef.php b/src/Php/Entity/ClassDef.php index a29f2097..eedfa679 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\Node\Stmt\Trait_; class ClassDef extends ClassLikeDef { @@ -30,7 +31,7 @@ class ClassDef extends ClassLikeDef public string $extends = ''; public bool $requireCtor = false; public bool $enum = false; - public bool $trait = false; + public ?Trait_ $trait = null; public int $flags; public bool $inheritedFromInternalClass = false; public string $ctorInit = ''; diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index d358aea2..ba7db888 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -422,7 +422,7 @@ class Preprocessor extends CompilerBase if (!$class instanceof Node\Stmt\Trait_) { $this->classDef->implements = $this->parseImplements($class->implements); } else { - $this->classDef->trait = true; + $this->classDef->trait = $class; } if (isset($this->symbolDeclInFile[$fullClassNameLower])) { $this->fatalError($class, "Duplicate class `{$fullClassName}`"); diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 6cab0104..8c366b55 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -920,6 +920,27 @@ class Translator extends Preprocessor $this->argInfoHeaderFiles[] = $headerFile; } + public function parseTraitUseForStub(Node\Stmt\ClassLike $stmt): void + { + foreach ($stmt->stmts as $classStmt) { + if (!$classStmt instanceof Node\Stmt\TraitUse) { + continue; + } + foreach ($classStmt->traits as $trait) { + $traitFullName = $this->getNamespacedClassName($trait); + if (!$this->hasClass($traitFullName)) { + $this->fatalError($classStmt, "Trait `{$traitFullName}` not found"); + } + $classDef = $this->getClass($traitFullName); + if (!$classDef->trait) { + $this->fatalError($classStmt, "Trait `{$traitFullName}` not found"); + } + // 务必注意顺序,如果类和 Trait 存在同名的方法,那么将使用类定义的方法 + $stmt->stmts = array_merge($classDef->trait->stmts, $stmt->stmts); + } + } + } + protected function parseClass(Node\Stmt\Class_|Node\Stmt\Trait_|Node\Stmt\Enum_ $class): string { $this->class = $this->parseIdentifier($class->name); @@ -1184,12 +1205,22 @@ class Translator extends Preprocessor $traitDef = $this->getClass($traitFullName); // 将 Trait 中定义的 常量、静态常量、属性、方法、静态属性复制到当前类中 foreach ($traitDef->constants as $const) { + if ($classDef->hasConstant($const->name)) { + continue; + } $classDef->constants[$const->name] = $const; } foreach ($traitDef->properties as $prop) { + if ($classDef->hasProperty($prop->name)) { + continue; + } $classDef->properties[$prop->name] = $prop; } foreach ($traitDef->methods as $methodDef) { + // 类中已经有同名方法,则不使用 Trait 中的方法 + if ($classDef->hasMethod($methodDef->name)) { + continue; + } $classDef->methods[$methodDef->name] = $methodDef; $traitMethodNativeName = self::PREFIX . $this->getNativeName($methodDef->name, $traitDef->namespace, $traitDef->name); $classMethodNativeName = self::PREFIX . $this->getNativeName($methodDef->name, $classDef->namespace, $classDef->name); diff --git a/src/gen_stub.php b/src/gen_stub.php index 87b817ea..df061f82 100755 --- a/src/gen_stub.php +++ b/src/gen_stub.php @@ -4437,6 +4437,9 @@ class FileInfo { $propertyInfos = []; $methodInfos = []; $enumCaseInfos = []; + + getTranslator()->parseTraitUseForStub($stmt); + foreach ($stmt->stmts as $classStmt) { $cond = self::handlePreprocessorConditions($conds, $classStmt); if ($classStmt instanceof Stmt\Nop) { @@ -4496,6 +4499,8 @@ class FileInfo { } else if ($classStmt instanceof Stmt\EnumCase) { $enumCaseInfos[] = new EnumCaseInfo( $classStmt->name->toString(), $classStmt->expr); + } else if ($classStmt instanceof Stmt\TraitUse) { + continue; } else { throw new Exception("Not implemented {$classStmt->getType()}"); } diff --git a/tests/aot/trait/001.phpt b/tests/aot/trait/001.phpt new file mode 100644 index 00000000..c8a64340 --- /dev/null +++ b/tests/aot/trait/001.phpt @@ -0,0 +1,40 @@ +--TEST-- +Single Trait with simple trait method +--FILE-- +name . PHP_EOL; + $this->foo(); + } + + public function foo(): void { + $array = [ + 'count' => self::$count, + 'baz' => self::BAZ, + ]; + var_dump($array); + } +} + +class TraitsTest { + use THello; +} + +function main() { + $test = new TraitsTest(); + $test->hello(); +} +?> +--EXPECT-- +Hello world +array(2) { + ["count"]=> + int(0) + ["baz"]=> + string(3) "baz" +} diff --git a/tests/aot/trait/002.phpt b/tests/aot/trait/002.phpt new file mode 100644 index 00000000..d6526880 --- /dev/null +++ b/tests/aot/trait/002.phpt @@ -0,0 +1,17 @@ +--TEST-- +Single Trait with simple trait method +--FILE-- +name . PHP_EOL; + } +} + +function main() { + eval("(new class { use HelloTrait;})->hello();"); +} +?> +--EXPECT-- +Hello world