TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
5.9 KiB
151 lines
5.9 KiB
<?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');
|
|
}
|
|
}
|
|
|