diff --git a/src/CompilerBase.php b/src/CompilerBase.php index 4959a714..04471805 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -41,6 +41,7 @@ use TypePhp\Parser\ArrayExpressionTrait; use TypePhp\Parser\BinaryOpTrait; use TypePhp\Parser\ClassConstantFetchTrait; use TypePhp\Parser\ConditionalControlTrait; +use TypePhp\Parser\ConstantExpressionTrait; use TypePhp\Parser\ExceptionControlFlowTrait; use TypePhp\Parser\ForeachTrait; use TypePhp\Parser\FunctionCallTrait; @@ -79,7 +80,6 @@ use PhpParser\Node\Expr\Variable; use PhpParser\Node\FunctionLike; use PhpParser\Node\IntersectionType; use PhpParser\Node\NullableType; -use PhpParser\Node\Scalar\MagicConst; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\UnionType; use PhpParser\NodeAbstract; @@ -109,6 +109,7 @@ class CompilerBase implements PropertyAccessContext use BinaryOpTrait; use ClassConstantFetchTrait; use ConditionalControlTrait; + use ConstantExpressionTrait; use ExceptionControlFlowTrait; use ForeachTrait; use FunctionCallTrait; @@ -3451,71 +3452,6 @@ class CompilerBase implements PropertyAccessContext return $this->convertObjectExpr($this->parseExprAsValue($node->expr)); } - protected function parseConstFetch(Expr\ConstFetch $expr, bool $scalar = false): string - { - if ($expr->name->getType() != 'Name' and !($expr->name instanceof Node\Name\FullyQualified)) { - abort($expr); - } - $name = $this->parseIdentifier($expr->name); - $name = ltrim($name, '\\'); - if ($this->isNameExpr($expr->name) and $this->hasConstant($name)) { - return $this->getConstant($name); - } - if ($this->namespace and $this->isNameExpr($expr->name) and !$expr->name instanceof Node\Name\FullyQualified) { - $nsName = $this->namespace . '\\' . $name; - if ($this->hasConstant($nsName)) { - return $this->getConstant($nsName); - } - } - if ($this->isNameExpr($expr->name) and isset($this->useConstants[$name])) { - $importedName = $this->useConstants[$name]; - if ($this->hasConstant($importedName)) { - return $this->getConstant($importedName); - } - } - if (strcasecmp($name, 'null') === 0) { - return self::VALUE_NULL; - } - if (strcasecmp($name, 'true') === 0) { - return 'true'; - } - if (strcasecmp($name, 'false') === 0) { - return 'false'; - } - if ($name === 'PHP_EOL') { - return '"' . $this->escapeString(PHP_EOL) . '"'; - } - if ($this->isInternalScalarConstant($name)) { - return $this->getInternalScalarConstantValue($name); - } - if ($scalar) { - return constant($expr->name); - } - if ($this->isNameExpr($expr->name)) { - if (str_contains($name, '::')) { - $ns = explode('::', $name)[0]; - $fullName = $this->getNamespacedClassName($ns[0]); - $ce = $this->getClassEntryPtr($fullName); - return Symbol::constant() . '(' . $ce . ', ' . $this->getLiteralString($ns[1]) . ')'; - } - if ($this->isInternalConstant($name)) { - return Symbol::constant() . '(' . $this->getLiteralString($name) . ')'; - } - if (isset($this->useAliases[$name])) { - $name = $this->useAliases[$name]; - } elseif (isset($this->useConstants[$name])) { - $name = $this->useConstants[$name]; - } else { - $fullName = $this->getNamespacedClassName($name); - if ($fullName) { - $name = $fullName; - } - } - return Symbol::constant() . '(nullptr, ' . $this->getLiteralString($name) . ')'; - } - return Symbol::constant() . '("' . $this->escapeString($name) . '")'; - } - protected function parseUnaryMinus(Expr\UnaryMinus $expr): string { $type = $this->detectTypeOfExpr($expr->expr); @@ -3595,90 +3531,6 @@ class CompilerBase implements PropertyAccessContext return 'php::aotExit(' . $status . ')'; } - protected function parseMagicConst(MagicConst $expr): string - { - $class = ($this->namespace ? $this->namespace . '\\' : '') . $this->class; - $function = ($this->namespace ? $this->namespace . '\\' : '') . $this->function; - switch ($expr->getType()) { - case 'Scalar_MagicConst_Dir': - return '"' . $this->escapeString($this->dir) . '"'; - case 'Scalar_MagicConst_File': - return '"' . $this->escapeString($this->file) . '"'; - case 'Scalar_MagicConst_Line': - return (string) $expr->getStartLine(); - case 'Scalar_MagicConst_Function': - return '"' . $this->escapeString($function) . '"'; - case 'Scalar_MagicConst_Class': - if (!$this->classDef) { - $this->fatalError($expr, 'The magic constant `__CLASS__` is not allowed in global scope'); - } - if ($this->classDef->trait) { - return Symbol::getCalledClass(); - } - return '"' . $this->escapeString($class) . '"'; - case 'Scalar_MagicConst_Trait': - if (!$this->classDef or !$this->classDef->trait) { - $this->fatalError($expr, 'The magic constant `__TRAIT__` is not allowed in global scope'); - } - return '"' . $this->escapeString($class) . '"'; - case 'Scalar_MagicConst_Method': - return '"' . $this->escapeString($class) . '::' . $this->escapeString($this->method) . '"'; - default: - abort($expr); - break; - } - } - - protected function detectConstType($expr): string - { - $name = $this->parseIdentifier($expr->name); - if ($this->hasConstant($name)) { - return $this->getConstantType($name); - } - if ($this->isInternalConstant($name)) { - return $this->getTypeFromZendType(gettype($this->internalConstants[$name])); - } - if (strcasecmp($name, 'true') === 0) { - return self::TYPE_BOOL; - } - if (strcasecmp($name, 'false') === 0) { - return self::TYPE_BOOL; - } - if ($name === 'NAN' or $name === 'INF') { - return self::TYPE_FLOAT; - } - return self::TYPE_VAR; - } - - protected function isInternalScalarConstant(string $name): bool - { - return $this->isInternalConstant($name) && is_scalar($this->internalConstants[$name]); - } - - protected function getInternalScalarConstantValue(string $name): string|int|float - { - $value = $this->internalConstants[$name]; - if (is_int($value)) { - return $this->genIntegerLiteral($value); - } - if (is_float($value)) { - if (is_nan($value)) { - return self::VALUE_NAN; - } - if (is_infinite($value)) { - return $value > 0 ? self::VALUE_INF : '-' . self::VALUE_INF; - } - return $this->genCValue($value); - } - if (is_bool($value)) { - return $value ? 1 : 0; - } - if (is_string($value)) { - return $this->genCharPtr($value, true); - } - $this->error('Unsupported constant type: ' . gettype($value)); - } - protected function parseStatic(Node\Stmt\Static_ $v): string { $list = []; diff --git a/src/Parser/ConstantExpressionTrait.php b/src/Parser/ConstantExpressionTrait.php new file mode 100644 index 00000000..1e1e1c98 --- /dev/null +++ b/src/Parser/ConstantExpressionTrait.php @@ -0,0 +1,165 @@ +name->getType() != 'Name' and !($expr->name instanceof Node\Name\FullyQualified)) { + abort($expr); + } + $name = $this->parseIdentifier($expr->name); + $name = ltrim($name, '\\'); + if ($this->isNameExpr($expr->name) and $this->hasConstant($name)) { + return $this->getConstant($name); + } + if ($this->namespace and $this->isNameExpr($expr->name) and !$expr->name instanceof Node\Name\FullyQualified) { + $nsName = $this->namespace . '\\' . $name; + if ($this->hasConstant($nsName)) { + return $this->getConstant($nsName); + } + } + if ($this->isNameExpr($expr->name) and isset($this->useConstants[$name])) { + $importedName = $this->useConstants[$name]; + if ($this->hasConstant($importedName)) { + return $this->getConstant($importedName); + } + } + if (strcasecmp($name, 'null') === 0) { + return self::VALUE_NULL; + } + if (strcasecmp($name, 'true') === 0) { + return 'true'; + } + if (strcasecmp($name, 'false') === 0) { + return 'false'; + } + if ($name === 'PHP_EOL') { + return '"' . $this->escapeString(PHP_EOL) . '"'; + } + if ($this->isInternalScalarConstant($name)) { + return $this->getInternalScalarConstantValue($name); + } + if ($scalar) { + return constant($expr->name); + } + if ($this->isNameExpr($expr->name)) { + if (str_contains($name, '::')) { + $ns = explode('::', $name)[0]; + $fullName = $this->getNamespacedClassName($ns[0]); + $ce = $this->getClassEntryPtr($fullName); + return Symbol::constant() . '(' . $ce . ', ' . $this->getLiteralString($ns[1]) . ')'; + } + if ($this->isInternalConstant($name)) { + return Symbol::constant() . '(' . $this->getLiteralString($name) . ')'; + } + if (isset($this->useAliases[$name])) { + $name = $this->useAliases[$name]; + } elseif (isset($this->useConstants[$name])) { + $name = $this->useConstants[$name]; + } else { + $fullName = $this->getNamespacedClassName($name); + if ($fullName) { + $name = $fullName; + } + } + return Symbol::constant() . '(nullptr, ' . $this->getLiteralString($name) . ')'; + } + return Symbol::constant() . '("' . $this->escapeString($name) . '")'; + } + + protected function parseMagicConst(MagicConst $expr): string + { + $class = ($this->namespace ? $this->namespace . '\\' : '') . $this->class; + $function = ($this->namespace ? $this->namespace . '\\' : '') . $this->function; + switch ($expr->getType()) { + case 'Scalar_MagicConst_Dir': + return '"' . $this->escapeString($this->dir) . '"'; + case 'Scalar_MagicConst_File': + return '"' . $this->escapeString($this->file) . '"'; + case 'Scalar_MagicConst_Line': + return (string) $expr->getStartLine(); + case 'Scalar_MagicConst_Function': + return '"' . $this->escapeString($function) . '"'; + case 'Scalar_MagicConst_Class': + if (!$this->classDef) { + $this->fatalError($expr, 'The magic constant `__CLASS__` is not allowed in global scope'); + } + if ($this->classDef->trait) { + return Symbol::getCalledClass(); + } + return '"' . $this->escapeString($class) . '"'; + case 'Scalar_MagicConst_Trait': + if (!$this->classDef or !$this->classDef->trait) { + $this->fatalError($expr, 'The magic constant `__TRAIT__` is not allowed in global scope'); + } + return '"' . $this->escapeString($class) . '"'; + case 'Scalar_MagicConst_Method': + return '"' . $this->escapeString($class) . '::' . $this->escapeString($this->method) . '"'; + default: + abort($expr); + } + } + + protected function detectConstType($expr): string + { + $name = $this->parseIdentifier($expr->name); + if ($this->hasConstant($name)) { + return $this->getConstantType($name); + } + if ($this->isInternalConstant($name)) { + return $this->getTypeFromZendType(gettype($this->internalConstants[$name])); + } + if (strcasecmp($name, 'true') === 0) { + return self::TYPE_BOOL; + } + if (strcasecmp($name, 'false') === 0) { + return self::TYPE_BOOL; + } + if ($name === 'NAN' or $name === 'INF') { + return self::TYPE_FLOAT; + } + return self::TYPE_VAR; + } + + protected function isInternalScalarConstant(string $name): bool + { + return $this->isInternalConstant($name) && is_scalar($this->internalConstants[$name]); + } + + protected function getInternalScalarConstantValue(string $name): string|int|float + { + $value = $this->internalConstants[$name]; + if (is_int($value)) { + return $this->genIntegerLiteral($value); + } + if (is_float($value)) { + if (is_nan($value)) { + return self::VALUE_NAN; + } + if (is_infinite($value)) { + return $value > 0 ? self::VALUE_INF : '-' . self::VALUE_INF; + } + return $this->genCValue($value); + } + if (is_bool($value)) { + return $value ? 1 : 0; + } + if (is_string($value)) { + return $this->genCharPtr($value, true); + } + $this->error('Unsupported constant type: ' . gettype($value)); + } +}