From f4b8d6c1f3b2182ca78d63502cc7ab8c07184c4f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 14 Apr 2026 15:12:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(php):=20=E6=B7=BB=E5=8A=A0=E5=AF=B9=20trai?= =?UTF-8?q?t=20=E7=9A=84=E6=94=AF=E6=8C=81=E5=B9=B6=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7=E5=BC=95=E7=94=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ClassDef 中添加 trait 属性标识类是否为 trait - 新增 Symbol 类统一管理 PHP 符号的生成 - 将编译器中的硬编码符号调用替换为 Symbol 类方法 - 实现 Trait 使用功能,支持将 Trait 中的常量、属性和方法复制到目标类 - 添加对 trait 内属性的动态访问支持 - 重构静态属性访问逻辑以兼容 trait 使用场景 - 移动并优化常量值获取相关方法到 Translator 类 --- src/Php/CompilerBase.php | 65 +++++++++------- src/Php/Entity/ClassDef.php | 1 + src/Php/Generator/Utils.php | 3 +- src/Php/Preprocessor.php | 2 + src/Php/Symbol.php | 37 +++++++++ src/Php/Translator.php | 147 ++++++++++++++++++++++++------------ 6 files changed, 178 insertions(+), 77 deletions(-) create mode 100644 src/Php/Symbol.php diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 5dd96b79..b21aa500 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -554,6 +554,12 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->buildDir; } + public function getRelativePath($path, $cwd = ''): string + { + $cwd = $cwd ?: getcwd(); + return ltrim($this->removeCommonPrefix($cwd, $path), '/'); + } + protected function isSuperGlobal(string $var): bool { if (isset($this->superGlobalVars[$var])) { @@ -562,12 +568,6 @@ class CompilerBase extends \PhpAot\Core\Translator return false; } - public function getRelativePath($path, $cwd = ''): string - { - $cwd = $cwd ?: getcwd(); - return ltrim($this->removeCommonPrefix($cwd, $path), '/'); - } - protected function removeCommonPrefix(string $short, string $long): string { $len = min(strlen($short), strlen($long)); @@ -1187,9 +1187,9 @@ class CompilerBase extends \PhpAot\Core\Translator if ($native) { return $native . ' = ' . $value; } - $class = $this->identifierToStr($left->class); + $class = $this->identifierToStr($left->class); $propName = $this->identifierToStr($left->name); - return "php::setStaticProperty({$class}, {$propName}, {$value})"; + return Symbol::setStaticProperty() . "({$class}, {$propName}, {$value})"; } protected function parseAssign(Expr\Assign $v): string @@ -2584,12 +2584,12 @@ class CompilerBase extends \PhpAot\Core\Translator return $native . str_repeat($op, 2); } - $class = $this->identifierToStr($expr->var->class); - $prop = $this->identifierToStr($expr->var->name); + $class = $this->identifierToStr($expr->var->class); + $prop = $this->identifierToStr($expr->var->name); $tmpVar = $this->genTmpVarName(); $this->addLocalVar($tmpVar, self::TYPE_VAR); - $this->context->beforeStmtLines[] = $tmpVar . ' = php::getStaticProperty(' . $class . ', ' . $prop . ');'; - $this->context->afterStmtLines[] = 'php::setStaticProperty(' . $class . ', ' . $prop . ', ' . $tmpVar . ' ' . $op . ' 1);'; + $this->context->beforeStmtLines[] = $tmpVar . ' = ' . Symbol::getStaticProperty() . '(' . $class . ', ' . $prop . ');'; + $this->context->afterStmtLines[] = Symbol::setStaticProperty() . '(' . $class . ', ' . $prop . ', ' . $tmpVar . ' ' . $op . ' 1);'; return $tmpVar; } @@ -2964,7 +2964,7 @@ class CompilerBase extends \PhpAot\Core\Translator $className = $this->parseIdentifier($expr->class); if ($this->isNameExpr($expr->class)) { if ($className === 'static') { - $cePtr = 'php_get_called_ce(this_)'; + $cePtr = Symbol::getCalledCe(); } else { $className = $this->getNamespacedClassName($className); if ($this->hasClass($className)) { @@ -3052,10 +3052,10 @@ class CompilerBase extends \PhpAot\Core\Translator $ns = explode('::', $name)[0]; $fullName = $this->getNamespacedClassName($ns[0]); $ce = $this->getClassEntryPtr($fullName); - return 'php::constant(' . $ce . ', ' . $this->getLiteralString($ns[1]) . ')'; + return Symbol::constant() . '(' . $ce . ', ' . $this->getLiteralString($ns[1]) . ')'; } if ($this->isInternalConstant($name)) { - return 'php::constant(' . $this->getLiteralString($name) . ')'; + return Symbol::constant() . '(' . $this->getLiteralString($name) . ')'; } if (isset($this->useAliases[$name])) { $name = $this->useAliases[$name]; @@ -3065,9 +3065,9 @@ class CompilerBase extends \PhpAot\Core\Translator $name = $fullName; } } - return 'php::constant(nullptr, ' . $this->getLiteralString($name) . ')'; + return Symbol::constant() . '(nullptr, ' . $this->getLiteralString($name) . ')'; } - return 'php::constant("' . $this->escapeString($name) . '")'; + return Symbol::constant() . '("' . $this->escapeString($name) . '")'; } protected function parseUnaryMinus(Expr\UnaryMinus $expr): string @@ -3250,6 +3250,9 @@ class CompilerBase extends \PhpAot\Core\Translator $propertyName = $this->parseIdentifier($property); $nativeProperty = null; if ($objectName === 'this_') { + if ($this->classDef->trait) { + goto _dynamic_attr; + } $nativeProperty = $this->findNativeProperty($object, $propertyName, $this->class, $this->namespace); } elseif ($this->isTypedObject($objectName)) { $className = $this->getObjectType($objectName); @@ -3260,6 +3263,7 @@ class CompilerBase extends \PhpAot\Core\Translator return $nativeProperty; } } + _dynamic_attr: return $this->identifierToStr($property, literal: true); } @@ -3906,7 +3910,7 @@ class CompilerBase extends \PhpAot\Core\Translator if ($id === 'self') { $id = $this->getNamespacedClassName($this->class); } elseif ($id === 'static') { - return 'php_get_called_class(this_)'; + return Symbol::getCalledClass(); } if ($this->isNameExpr($node) or $this->isIdExpr($node)) { return $literal ? $this->getLiteralString($id) : $this->genCharPtr($id, true); @@ -3941,9 +3945,9 @@ class CompilerBase extends \PhpAot\Core\Translator $placeHolder = $fn; } elseif ($class === 'static') { $methodPtr = $this->identifierToStr($expr->name, literal: true); - $fn = 'php_get_called_ce(this_), php::getMethod(php_get_called_ce(this_), ' . $methodPtr . ')'; + $fn = Symbol::getCalledCe() . ', php::getMethod(' . Symbol::getCalledCe() . ', ' . $methodPtr . ')'; $this->context->beforeStmtLines[] = '// Static Method Call: static::' . $this->parseIdentifier($expr->name) . '()'; - $placeHolder = $this->genArray(['php_get_called_class(this_)', $methodPtr]); + $placeHolder = $this->genArray([Symbol::getCalledCe(), $methodPtr]); } elseif ($this->isNameExpr($expr->class)) { if ($class === 'self') { $class = $this->class; @@ -4020,6 +4024,9 @@ class CompilerBase extends \PhpAot\Core\Translator $class = $this->parseIdentifier($expr->class); $propertyName = $this->parseIdentifier($expr->name); if ($class === 'self') { + if ($this->classDef->trait) { + return Symbol::getStaticProperty() . '(' . Symbol::getCalledCe() . ', ' . $this->getLiteralString($propertyName) . ')'; + } $class = $this->getFullClassName(); } elseif ($class === 'parent') { if (!$this->classDef->extends) { @@ -4084,8 +4091,12 @@ class CompilerBase extends \PhpAot\Core\Translator { $nativeProp = $this->findNativeStaticProperty($expr, $class); if ($nativeProp) { - $classPtr = $this->getClassEntryPtr($class); - return 'php::getStaticProperty(' . $classPtr . ', ' . $nativeProp . ')'; + if ($expr->hasAttribute('nativeProperty')) { + $classPtr = $this->getClassEntryPtr($class); + return Symbol::getStaticProperty() . '(' . $classPtr . ', ' . $nativeProp . ')'; + } else { + return $nativeProp; + } } return false; } @@ -4096,7 +4107,7 @@ class CompilerBase extends \PhpAot\Core\Translator if ($native) { return $native; } - return 'php::getStaticProperty(' . $this->identifierToStr($expr->class) . ', ' . $this->identifierToStr($expr->name) . ')'; + return Symbol::getStaticProperty() . '(' . $this->identifierToStr($expr->class) . ', ' . $this->identifierToStr($expr->name) . ')'; } protected function parseClassConstFetch(Expr\ClassConstFetch $expr): string @@ -4114,9 +4125,9 @@ class CompilerBase extends \PhpAot\Core\Translator $this->fatalError($expr, "The 'static' keyword can only be used as the class name in class methods"); } if ($const === 'class') { - return 'php_get_called_class(this_)'; + return Symbol::getCalledClass(); } else { - return 'php::constant(php_get_called_ce(this_), ' . $this->getLiteralString($const) . ')'; + return Symbol::constant() . '(' . Symbol::getCalledCe() . ', ' . $this->getLiteralString($const) . ')'; } } @@ -4137,11 +4148,11 @@ class CompilerBase extends \PhpAot\Core\Translator } } $ce = $this->getClassEntryPtr($class); - return 'php::constant(' . $ce . ', ' . $this->getLiteralString($const) . ')'; + return Symbol::constant() . '(' . $ce . ', ' . $this->getLiteralString($const) . ')'; } $name = $class . '::' . $const; $name = $this->getLiteralString($name); - return 'php::constant(' . $name . ')'; + return Symbol::constant() . '(' . $name . ')'; } protected function parseThrow(mixed $expr): string diff --git a/src/Php/Entity/ClassDef.php b/src/Php/Entity/ClassDef.php index a6dd8412..a29f2097 100644 --- a/src/Php/Entity/ClassDef.php +++ b/src/Php/Entity/ClassDef.php @@ -30,6 +30,7 @@ class ClassDef extends ClassLikeDef public string $extends = ''; public bool $requireCtor = false; public bool $enum = false; + public bool $trait = false; public int $flags; public bool $inheritedFromInternalClass = false; public string $ctorInit = ''; diff --git a/src/Php/Generator/Utils.php b/src/Php/Generator/Utils.php index 13c72fa0..4d2752ee 100644 --- a/src/Php/Generator/Utils.php +++ b/src/Php/Generator/Utils.php @@ -17,7 +17,8 @@ trait Utils { if (is_int($value) or is_float($value)) { return $value; - } elseif (is_string($value)) { + } + if (is_string($value)) { return $this->genCharPtr($value); } else { $this->error('Unsupported constant type: ' . gettype($value)); diff --git a/src/Php/Preprocessor.php b/src/Php/Preprocessor.php index 1032a45d..d358aea2 100644 --- a/src/Php/Preprocessor.php +++ b/src/Php/Preprocessor.php @@ -421,6 +421,8 @@ class Preprocessor extends CompilerBase } if (!$class instanceof Node\Stmt\Trait_) { $this->classDef->implements = $this->parseImplements($class->implements); + } else { + $this->classDef->trait = true; } if (isset($this->symbolDeclInFile[$fullClassNameLower])) { $this->fatalError($class, "Duplicate class `{$fullClassName}`"); diff --git a/src/Php/Symbol.php b/src/Php/Symbol.php new file mode 100644 index 00000000..d23c03d7 --- /dev/null +++ b/src/Php/Symbol.php @@ -0,0 +1,37 @@ +internalConstants; + } + + /** + * 仅用于 gen_stub 脚本 + * @throws \Exception + */ + public function getClassConstValue(NodeAbstract $expr, string $class, string $name): mixed + { + $class = $this->getNamespacedClassName($class); + $nativeConst = $this->findNativeClassConst($expr, $class, $name); + if ($nativeConst and $expr->hasAttribute('nativeConst')) { + $constDef = $expr->getAttribute('nativeConst'); + return $this->genCValue($constDef->valueExpr->value); + } + throw new \Exception("Class constant `{$class}::{$name}` not found"); + } + + public function getConstValue(string $name): mixed + { + if ($this->isInternalConstant($name)) { + $value = $this->internalConstants[$name]; + if (is_int($value)) { + $expr = strval($value); + if ($value === PHP_INT_MIN) { + $expr = 'LONG_MIN'; + } elseif ($value === PHP_INT_MAX) { + $expr = 'LONG_MAX'; + } else { + $expr = $expr . 'L'; + } + } elseif (is_float($value)) { + return $value; + } elseif (is_string($value)) { + return $this->genCharPtr($value); + } else { + $this->error('Unsupported constant type: ' . gettype($value)); + } + return $expr; + } + throw new \Exception('Constant ' . $name . ' not found'); + } + protected function getRegisterClassFunction(string $name): string { return self::PREFIX . 'register_class_' . $name; @@ -647,11 +692,6 @@ class Translator extends Preprocessor return $list; } - public function getDefinedConstants(): array - { - return $this->internalConstants; - } - protected function getInternalCeInfo(string $ce): array { return [ @@ -920,6 +960,9 @@ class Translator extends Preprocessor case 'Stmt_ClassMethod': $this->parseClassMethod($v, $methodCodes); break; + case 'Stmt_TraitUse': + $this->parseTraitUse($v, $methodCodes); + break; default: abort($v); } @@ -1129,6 +1172,56 @@ class Translator extends Preprocessor $this->resetMethod(); } + protected function parseTraitUse(Node\Stmt\TraitUse $v, array &$methodCodes): void + { + $classDef = $this->classDef; + foreach ($v->traits as $trait) { + $traitName = $this->parseIdentifier($trait); + $traitFullName = $this->getNamespacedClassName($traitName); + if (!$this->hasClass($traitFullName)) { + $this->fatalError($v, $traitFullName . ' not found'); + } + $traitDef = $this->getClass($traitFullName); + // 将 Trait 中定义的 常量、静态常量、属性、方法、静态属性复制到当前类中 + foreach ($traitDef->constants as $const) { + $classDef->constants[$const->name] = $const; + } + foreach ($traitDef->properties as $prop) { + $classDef->properties[$prop->name] = $prop; + } + foreach ($traitDef->methods as $methodDef) { + $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); + $argList = ['this_']; + foreach ($methodDef->functionDef->argInfoList as $argInfo) { + $argList[] = $argInfo->name; + } + $argv = implode(', ', $argList); + + $code = $methodDef->getReturnType() . ' ' . $classMethodNativeName . '('; + if ($this->class) { + $code .= self::TYPE_OBJECT . ' &this_'; + if ($methodDef->functionDef->params) { + $code .= ', '; + } + } + + $code .= $methodDef->functionDef->params . ')'; + $code .= '{' . PHP_EOL; + $this->indentLevel++; + $methodCall = $traitMethodNativeName . '(' . $argv . ')'; + if ($methodDef->getReturnType() !== self::TYPE_VOID) { + $methodCall = 'return ' . $methodCall; + } + $code .= $this->getIndent() . $methodCall . ';' . PHP_EOL; + $this->indentLevel--; + $code .= $this->getIndent() . '}' . PHP_EOL; + $methodCodes[$methodDef->name] = $code; + } + } + } + protected function parseInterface(Node\Stmt\Interface_ $v): void { $name = $this->parseIdentifier($v->name); @@ -1306,48 +1399,4 @@ class Translator extends Preprocessor return $code; } - - /** - * 仅用于 gen_stub 脚本 - * @param NodeAbstract $expr - * @param string $class - * @param string $name - * @return mixed - * @throws \Exception - */ - public function getClassConstValue(NodeAbstract $expr, string $class, string $name): mixed - { - $class = $this->getNamespacedClassName($class); - $nativeConst = $this->findNativeClassConst($expr, $class, $name); - if ($nativeConst and $expr->hasAttribute('nativeConst')) { - $constDef = $expr->getAttribute('nativeConst'); - return $this->genCValue($constDef->valueExpr->value); - } - throw new \Exception("Class constant `$class::$name` not found"); - } - - public function getConstValue(string $name): mixed - { - if ($this->isInternalConstant($name)) { - $value = $this->internalConstants[$name]; - if (is_int($value)) { - $expr = strval($value); - if ($value === PHP_INT_MIN) { - $expr = 'LONG_MIN'; - } elseif ($value === PHP_INT_MAX) { - $expr = 'LONG_MAX'; - } else { - $expr = $expr . 'L'; - } - } elseif (is_float($value)) { - return $value; - } elseif (is_string($value)) { - return $this->genCharPtr($value); - } else { - $this->error('Unsupported constant type: ' . gettype($value)); - } - return $expr; - } - throw new \Exception('Constant ' . $name . ' not found'); - } }