feat(php): 添加对 trait 的支持并重构符号引用方式

- 在 ClassDef 中添加 trait 属性标识类是否为 trait
- 新增 Symbol 类统一管理 PHP 符号的生成
- 将编译器中的硬编码符号调用替换为 Symbol 类方法
- 实现 Trait 使用功能,支持将 Trait 中的常量、属性和方法复制到目标类
- 添加对 trait 内属性的动态访问支持
- 重构静态属性访问逻辑以兼容 trait 使用场景
- 移动并优化常量值获取相关方法到 Translator 类
pull/1/head
韩天峰 4 months ago
parent d85353f8eb
commit f4b8d6c1f3
  1. 65
      src/Php/CompilerBase.php
  2. 1
      src/Php/Entity/ClassDef.php
  3. 3
      src/Php/Generator/Utils.php
  4. 2
      src/Php/Preprocessor.php
  5. 37
      src/Php/Symbol.php
  6. 147
      src/Php/Translator.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

@ -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 = '';

@ -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));

@ -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}`");

@ -0,0 +1,37 @@
<?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace PhpAot\Php;
class Symbol
{
public static function getStaticProperty(): string
{
return 'php::getStaticProperty';
}
public static function setStaticProperty(): string
{
return 'php::setStaticProperty';
}
public static function getCalledCe(): string
{
return CompilerBase::PREFIX . 'get_called_ce(this_)';
}
public static function getCalledClass(): string
{
return CompilerBase::PREFIX . 'get_called_class(this_)';
}
public static function constant(): string
{
return 'php::constant';
}
}

@ -545,6 +545,51 @@ class Translator extends Preprocessor
return $code;
}
public function getDefinedConstants(): array
{
return $this->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');
}
}

Loading…
Cancel
Save