refactor(translator): extract class constant value handling to dedicated trait

- Move getClassConstValue, resolveInheritedClassConst, evaluateClassConstValue methods to ClassConstantValueTrait
- Extract getDefinedConstants and getConstValue methods to the new trait
- Add ClassConstantValueTrait usage to Translator class
- Remove duplicated method implementations from Translator class
- Maintain same functionality for class constant evaluation and inheritance resolution
- Improve code organization by separating constant-related logic into reusable trait
pull/17/head
韩天峰 2 months ago
parent 11d4c23085
commit 6de4dc42bd
  1. 151
      src/Resolver/ClassConstantValueTrait.php
  2. 161
      src/Translator.php

@ -0,0 +1,151 @@
<?php
/**
* This file is part of TypePHP.
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace TypePhp\Resolver;
use PhpParser\ConstExprEvaluator;
use PhpParser\Node;
use PhpParser\NodeAbstract;
use TypePhp\Entity\ConstantDef;
trait ClassConstantValueTrait
{
public function getDefinedConstants(): array
{
return $this->internalConstants;
}
public function getClassConstValue(NodeAbstract $expr, string $_class, string $name, string $currentClass = ''): mixed
{
$namespace = $this->namespace;
if (!$namespace and $currentClass and !str_contains($_class, '\\')) {
$namespace = $this->getNamespaceOfClass($currentClass);
}
$class = $this->getNamespacedClassName($_class, $namespace);
$nativeConst = $this->findNativeClassConst($expr, $class, $name);
if ($nativeConst and $expr->hasAttribute('nativeConst')) {
$constDef = $expr->getAttribute('nativeConst');
if ($constDef->valueExpr !== null) {
return $this->evaluateClassConstValue($expr, $constDef, $class, $name);
}
if ($constDef->class !== '') {
$refConst = $constDef->class . '::' . $name;
if (defined($refConst)) {
return constant($refConst);
}
}
}
if ($this->isInternalClass($class)) {
$constName = $class . '::' . $name;
if (defined($constName)) {
return constant($constName);
}
}
[$inheritedFound, $inherited] = $this->resolveInheritedClassConst($class, $name);
if ($inheritedFound) {
return $inherited;
}
if ($this->hasClass($class)) {
$classDef = $this->getClass($class);
if ($classDef->enum && array_key_exists($name, $classDef->enumCases)) {
$caseValue = $classDef->enumCases[$name];
return $caseValue ?? $name;
}
}
$this->fatalError($expr, "Class constant `{$class}::{$name}` not found");
}
/** @return array{bool, mixed} */
protected function resolveInheritedClassConst(string $class, string $name): array
{
$current = ltrim($class, '\\');
$visited = [];
while ($current !== '' && $current !== '\\' && !isset($visited[strtolower($current)])) {
$visited[strtolower($current)] = true;
if ($this->hasClass($current)) {
$classDef = $this->getClass($current);
if ($classDef->hasConstant($name)) {
$constDef = $classDef->getConstant($name);
if ($constDef->valueExpr !== null) {
return [true, $this->evaluateClassConstValue(null, $constDef, $current, $name)];
}
if ($constDef->class !== '' && defined($constDef->class . '::' . $name)) {
return [true, constant($constDef->class . '::' . $name)];
}
}
$current = $classDef->extends;
} elseif (($parent = $this->getParentClass($current)) !== '') {
$current = $parent;
} elseif (Reflection::isInternalClass($current)) {
$constName = $current . '::' . $name;
if (defined($constName)) {
return [true, constant($constName)];
}
break;
} else {
break;
}
}
return [false, null];
}
protected function evaluateClassConstValue(?NodeAbstract $origin, ConstantDef $constDef, string $class, string $name): mixed
{
$valueExpr = $constDef->valueExpr;
if (!$valueExpr instanceof Node\Expr) {
$this->fatalError($origin, "Class constant `{$class}::{$name}` has no constant expression");
}
$evaluator = new ConstExprEvaluator(function (Node\Expr $expr) use ($origin, $class) {
if ($expr instanceof Node\Expr\ConstFetch) {
$constName = $expr->name->toString();
return match (strtolower($constName)) {
'true' => true,
'false' => false,
'null' => null,
default => defined($constName)
? constant($constName)
: throw new \RuntimeException("Constant `{$constName}` not found"),
};
}
if ($expr instanceof Node\Expr\ClassConstFetch && $expr->class instanceof Node\Name) {
$constName = $expr->name->toString();
$className = $expr->class->toString();
if (strcasecmp($className, 'self') === 0) {
$className = $class;
} elseif (strcasecmp($className, 'parent') === 0) {
$className = $this->getParentClass($class);
}
return $this->getClassConstValue($origin ?? $expr, $className, $constName, $class);
}
throw new \RuntimeException('Unsupported class constant expression');
});
return $evaluator->evaluateDirectly($valueExpr);
}
public function getConstValue(string $name): mixed
{
if ($this->isInternalConstant($name)) {
$value = $this->internalConstants[$name];
if (is_int($value)) {
$expr = $this->genIntegerLiteral($value);
} elseif (is_float($value)) {
return $value;
} elseif (is_bool($value)) {
return $value ? 1 : 0;
} 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');
}
}

@ -33,6 +33,7 @@ use TypePhp\Metadata\Constants;
use TypePhp\Platform\PlatformFactory;
use TypePhp\Platform\Windows;
use TypePhp\Resolver\Reflection;
use TypePhp\Resolver\ClassConstantValueTrait;
use TypePhp\Transform\Visitor;
use PhpParser\Modifiers;
use PhpParser\Node;
@ -50,6 +51,7 @@ class Translator extends Preprocessor
use NativeCommandOptionsTrait;
use SourcePipelineTrait;
use ResourceCompilationTrait;
use ClassConstantValueTrait;
public const string VERSION = '0.3.0';
public const string APP_NAME = 'TypePHP Compiler (AOT)';
@ -1679,165 +1681,6 @@ CODE;
return $code;
}
public function getDefinedConstants(): array
{
return $this->internalConstants;
}
/**
* 仅用于 gen_stub 脚本
* @throws \Exception
*/
public function getClassConstValue(NodeAbstract $expr, string $_class, string $name, string $currentClass = ''): mixed
{
$namespace = $this->namespace;
if (!$namespace and $currentClass and !str_contains($_class, '\\')) {
$namespace = $this->getNamespaceOfClass($currentClass);
}
$class = $this->getNamespacedClassName($_class, $namespace);
$nativeConst = $this->findNativeClassConst($expr, $class, $name);
if ($nativeConst and $expr->hasAttribute('nativeConst')) {
$constDef = $expr->getAttribute('nativeConst');
if ($constDef->valueExpr !== null) {
return $this->evaluateClassConstValue($expr, $constDef, $class, $name);
}
// 内部类的常量没有 valueExpr(值来自 PHP 反射),用“定义该常量的类”
// 通过反射取回标量值。继承自有内部父类(如 ArrayObject)的常量也走这里。
if ($constDef->class !== '') {
$refConst = $constDef->class . '::' . $name;
if (defined($refConst)) {
return constant($refConst);
}
}
}
if ($this->isInternalClass($class)) {
$constName = $class . '::' . $name;
if (defined($constName)) {
return constant($constName);
}
}
// findNativeClassConst 不会遍历到内部父类(如 \ArrayObject),
// 因此继承自有内部父类的常量(例如 self::ARRAY_AS_PROPS,常量定义于
// 内部父类)在此沿继承链解析(内部类在运行时已加载,可用反射取值)。
[$inheritedFound, $inherited] = $this->resolveInheritedClassConst($class, $name);
if ($inheritedFound) {
return $inherited;
}
// Resolve enum case references. Enum cases are runtime objects; their
// actual values in class constant arrays are set at runtime by
// genClassArrayConstants() via php::getEnumCase(). Return the backing
// value as a placeholder so gen_stub.php's ConstExprEvaluator can complete.
if ($this->hasClass($class)) {
$classDef = $this->getClass($class);
if ($classDef->enum && array_key_exists($name, $classDef->enumCases)) {
$caseValue = $classDef->enumCases[$name];
return $caseValue ?? $name;
}
}
$this->fatalError($expr, "Class constant `{$class}::{$name}` not found");
}
/**
* 沿类继承链解析类常量,支持继承自自定义父类或内部父类
* (如 LazyArrayObject 继承自内部类 \ArrayObject,其常量 ARRAY_AS_PROPS
* 定义于内部父类中;内部类在运行时已加载,可用 PHP 反射取值)。
*
* @return array{bool, mixed} [是否找到, 常量值]
*/
protected function resolveInheritedClassConst(string $class, string $name): array
{
$current = ltrim($class, '\\');
$visited = [];
while ($current !== '' && $current !== '\\' && !isset($visited[strtolower($current)])) {
$visited[strtolower($current)] = true;
if ($this->hasClass($current)) {
$classDef = $this->getClass($current);
if ($classDef->hasConstant($name)) {
$constDef = $classDef->getConstant($name);
if ($constDef->valueExpr !== null) {
return [true, $this->evaluateClassConstValue(null, $constDef, $current, $name)];
}
if ($constDef->class !== '' && defined($constDef->class . '::' . $name)) {
return [true, constant($constDef->class . '::' . $name)];
}
}
$current = $classDef->extends;
} elseif (($parent = $this->getParentClass($current)) !== '') {
// 生成 stub 时,当前文件的类尚未注册到 $this->classes,
// 但预处理阶段已经记录了其父类关系。
$current = $parent;
} elseif (Reflection::isInternalClass($current)) {
$constName = $current . '::' . $name;
if (defined($constName)) {
return [true, constant($constName)];
}
break;
} else {
break;
}
}
return [false, null];
}
/**
* Evaluates a parsed class-constant expression for arginfo generation.
* ConstantDef::value is C++ code, so it must not be used as the PHP value.
*/
protected function evaluateClassConstValue(?NodeAbstract $origin, ConstantDef $constDef, string $class, string $name): mixed
{
$valueExpr = $constDef->valueExpr;
if (!$valueExpr instanceof Node\Expr) {
$this->fatalError($origin, "Class constant `{$class}::{$name}` has no constant expression");
}
$evaluator = new \PhpParser\ConstExprEvaluator(function (Node\Expr $expr) use ($origin, $class) {
if ($expr instanceof Node\Expr\ConstFetch) {
$constName = $expr->name->toString();
return match (strtolower($constName)) {
'true' => true,
'false' => false,
'null' => null,
default => defined($constName)
? constant($constName)
: throw new \RuntimeException("Constant `{$constName}` not found"),
};
}
if ($expr instanceof Node\Expr\ClassConstFetch && $expr->class instanceof Node\Name) {
$constName = $expr->name->toString();
$className = $expr->class->toString();
if (strcasecmp($className, 'self') === 0) {
$className = $class;
} elseif (strcasecmp($className, 'parent') === 0) {
$className = $this->getParentClass($class);
}
return $this->getClassConstValue($origin ?? $expr, $className, $constName, $class);
}
throw new \RuntimeException('Unsupported class constant expression');
});
return $evaluator->evaluateDirectly($valueExpr);
}
public function getConstValue(string $name): mixed
{
if ($this->isInternalConstant($name)) {
$value = $this->internalConstants[$name];
if (is_int($value)) {
$expr = $this->genIntegerLiteral($value);
} elseif (is_float($value)) {
return $value;
} elseif (is_bool($value)) {
return $value ? 1 : 0;
} 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;

Loading…
Cancel
Save