From 6de4dc42bd1c9d5e35ded14f2810c285b5f6d43c Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sat, 11 Jul 2026 17:29:26 +0800 Subject: [PATCH] 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 --- src/Resolver/ClassConstantValueTrait.php | 151 +++++++++++++++++++++ src/Translator.php | 161 +---------------------- 2 files changed, 153 insertions(+), 159 deletions(-) create mode 100644 src/Resolver/ClassConstantValueTrait.php diff --git a/src/Resolver/ClassConstantValueTrait.php b/src/Resolver/ClassConstantValueTrait.php new file mode 100644 index 00000000..0853e993 --- /dev/null +++ b/src/Resolver/ClassConstantValueTrait.php @@ -0,0 +1,151 @@ +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'); + } +} diff --git a/src/Translator.php b/src/Translator.php index 5f35b144..7e99e41b 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -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;