refactor(parser): move constant expression parsing to dedicated trait

- Extract const fetch logic from CompilerBase to ConstantExpressionTrait
- Remove unused MagicConst import from CompilerBase
- Add new ConstantExpressionTrait with complete constant handling implementation
- Maintain all original constant parsing functionality in new trait structure
- Update CompilerBase to use the new ConstantExpressionTrait
- Remove duplicate constant-related methods from CompilerBase
pull/17/head
韩天峰 2 months ago
parent 7fdf7213ce
commit 742820dc0f
  1. 152
      src/CompilerBase.php
  2. 165
      src/Parser/ConstantExpressionTrait.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 = [];

@ -0,0 +1,165 @@
<?php
/**
* This file is part of TypePHP.
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace TypePhp\Parser;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar\MagicConst;
use TypePhp\Symbol;
trait ConstantExpressionTrait
{
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 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));
}
}
Loading…
Cancel
Save