From dd2c2d0dc764c79ae4518a60ac03e8176e5b22b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Hansl=C3=ADk?= Date: Wed, 28 Feb 2018 23:53:31 +0100 Subject: [PATCH] Simplified CompileNodeToValue --- src/NodeCompiler/CompileNodeToValue.php | 219 ++---------------- .../NodeCompiler/CompileNodeToValueTest.php | 10 +- 2 files changed, 18 insertions(+), 211 deletions(-) diff --git a/src/NodeCompiler/CompileNodeToValue.php b/src/NodeCompiler/CompileNodeToValue.php index d1fa66d89..3cff016f5 100644 --- a/src/NodeCompiler/CompileNodeToValue.php +++ b/src/NodeCompiler/CompileNodeToValue.php @@ -4,12 +4,10 @@ namespace Roave\BetterReflection\NodeCompiler; +use PhpParser\ConstExprEvaluator; use PhpParser\Node; -use ReflectionFunction; use Roave\BetterReflection\Reflection\ReflectionClass; use Roave\BetterReflection\Util\FileHelper; -use function array_combine; -use function array_map; use function constant; use function defined; use function dirname; @@ -20,11 +18,6 @@ class CompileNodeToValue { - /** - * @var callable[]|null indexed by supported expression node class name - */ - private static $nodeEvaluators; - /** * Compile an expression from a node into a value. * @@ -40,63 +33,27 @@ public function __invoke(Node $node, CompilerContext $context) return $this($node->expr, $context); } - if ($node instanceof Node\Scalar\String_ - || $node instanceof Node\Scalar\DNumber - || $node instanceof Node\Scalar\LNumber) { - return $node->value; - } - - // common edge case - negative numbers - if ($node instanceof Node\Expr\UnaryMinus) { - return $this($node->expr, $context) * -1; - } - - if ($node instanceof Node\Expr\Array_) { - return $this->compileArray($node, $context); - } - - if ($node instanceof Node\Expr\ConstFetch) { - return $this->compileConstFetch($node); - } - - if ($node instanceof Node\Expr\ClassConstFetch) { - return $this->compileClassConstFetch($node, $context); - } - - if ($node instanceof Node\Expr\BinaryOp) { - return $this->compileBinaryOperator($node, $context); - } - - if ($node instanceof Node\Scalar\MagicConst\Dir) { - return $this->compileDirConstant($context); - } - - if ($node instanceof Node\Scalar\MagicConst\Class_) { - return $this->compileClassConstant($context); - } + $constExprEvaluator = new ConstExprEvaluator(function (Node\Expr $node) use ($context) { + if ($node instanceof Node\Expr\ConstFetch) { + return $this->compileConstFetch($node); + } - throw new Exception\UnableToCompileNode('Unable to compile expression: ' . get_class($node)); - } + if ($node instanceof Node\Expr\ClassConstFetch) { + return $this->compileClassConstFetch($node, $context); + } - /** - * Compile arrays - * - * @return mixed[] - */ - private function compileArray(Node\Expr\Array_ $arrayNode, CompilerContext $context) : array - { - $compiledArray = []; - foreach ($arrayNode->items as $arrayItem) { - $compiledValue = $this($arrayItem->value, $context); + if ($node instanceof Node\Scalar\MagicConst\Dir) { + return $this->compileDirConstant($context); + } - if ($arrayItem->key === null) { - $compiledArray[] = $compiledValue; - continue; + if ($node instanceof Node\Scalar\MagicConst\Class_) { + return $this->compileClassConstant($context); } - $compiledArray[$this($arrayItem->key, $context)] = $compiledValue; - } - return $compiledArray; + throw new Exception\UnableToCompileNode('Unable to compile expression: ' . get_class($node)); + }); + + return $constExprEvaluator->evaluateDirectly($node); } /** @@ -162,30 +119,6 @@ private function compileClassConstFetch(Node\Expr\ClassConstFetch $node, Compile ); } - /** - * Compile a binary operator node - * - * - * @return mixed - * - * @throws \Roave\BetterReflection\NodeCompiler\Exception\UnableToCompileNode - */ - private function compileBinaryOperator(Node\Expr\BinaryOp $node, CompilerContext $context) - { - $evaluators = self::loadEvaluators(); - $nodeClass = get_class($node); - - if (! isset($evaluators[$nodeClass])) { - throw new Exception\UnableToCompileNode(sprintf( - 'Unable to compile binary operator: %s', - $nodeClass - )); - } - - // Welcome to method overloading implemented PHP-style. Yay? - return $evaluators[$nodeClass]($node, $context, $this); - } - /** * Compile a __DIR__ node */ @@ -212,122 +145,4 @@ private function getConstantDeclaringClass(string $constantName, ReflectionClass return $parentClass ? $this->getConstantDeclaringClass($constantName, $parentClass) : null; } - - /** - * @return callable[] indexed by node class name - */ - private static function loadEvaluators() : array - { - if (self::$nodeEvaluators) { - return self::$nodeEvaluators; - } - - $evaluators = self::makeEvaluators(); - - return self::$nodeEvaluators = array_combine( - array_map(function (callable $nodeEvaluator) : string { - /** @noinspection ExceptionsAnnotatingAndHandlingInspection */ - /** @noinspection NullPointerExceptionInspection */ - return (new ReflectionFunction($nodeEvaluator))->getParameters()[0]->getType()->getName(); - }, $evaluators), - $evaluators - ); - } - - /** - * @return callable[] - */ - private static function makeEvaluators() : array - { - return [ - function (Node\Expr\BinaryOp\Plus $node, CompilerContext $context, self $next) { - return $next($node->left, $context) + $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Mul $node, CompilerContext $context, self $next) { - return $next($node->left, $context) * $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Minus $node, CompilerContext $context, self $next) { - return $next($node->left, $context) - $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Div $node, CompilerContext $context, self $next) { - return $next($node->left, $context) / $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Concat $node, CompilerContext $context, self $next) { - return $next($node->left, $context) . $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\BooleanAnd $node, CompilerContext $context, self $next) { - return $next($node->left, $context) && $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\BooleanOr $node, CompilerContext $context, self $next) { - return $next($node->left, $context) || $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\BitwiseAnd $node, CompilerContext $context, self $next) { - return $next($node->left, $context) & $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\BitwiseOr $node, CompilerContext $context, self $next) { - return $next($node->left, $context) | $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\BitwiseXor $node, CompilerContext $context, self $next) { - return $next($node->left, $context) ^ $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Equal $node, CompilerContext $context, self $next) { - /** @noinspection TypeUnsafeComparisonInspection */ - // phpcs:disable SlevomatCodingStandard.ControlStructures.DisallowEqualOperators - return $next($node->left, $context) == $next($node->right, $context); - // phpcs:enable - }, - function (Node\Expr\BinaryOp\Greater $node, CompilerContext $context, self $next) { - return $next($node->left, $context) > $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\GreaterOrEqual $node, CompilerContext $context, self $next) { - return $next($node->left, $context) >= $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Identical $node, CompilerContext $context, self $next) { - return $next($node->left, $context) === $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\LogicalAnd $node, CompilerContext $context, self $next) { - // phpcs:disable Squiz.Operators.ValidLogicalOperators.NotAllowed - return $next($node->left, $context) and $next($node->right, $context); - // phpcs:enable - }, - function (Node\Expr\BinaryOp\LogicalOr $node, CompilerContext $context, self $next) { - // phpcs:disable Squiz.Operators.ValidLogicalOperators.NotAllowed - return $next($node->left, $context) or $next($node->right, $context); - // phpcs:enable - }, - function (Node\Expr\BinaryOp\LogicalXor $node, CompilerContext $context, self $next) { - return $next($node->left, $context) xor $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Mod $node, CompilerContext $context, self $next) { - return $next($node->left, $context) % $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\NotEqual $node, CompilerContext $context, self $next) { - /** @noinspection TypeUnsafeComparisonInspection */ - // phpcs:disable SlevomatCodingStandard.ControlStructures.DisallowEqualOperators - return $next($node->left, $context) != $next($node->right, $context); - // phpcs:enable - }, - function (Node\Expr\BinaryOp\NotIdentical $node, CompilerContext $context, self $next) { - return $next($node->left, $context) !== $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Pow $node, CompilerContext $context, self $next) { - return $next($node->left, $context) ** $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\ShiftLeft $node, CompilerContext $context, self $next) { - return $next($node->left, $context) << $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\ShiftRight $node, CompilerContext $context, self $next) { - return $next($node->left, $context) >> $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Smaller $node, CompilerContext $context, self $next) { - return $next($node->left, $context) < $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\SmallerOrEqual $node, CompilerContext $context, self $next) { - return $next($node->left, $context) <= $next($node->right, $context); - }, - function (Node\Expr\BinaryOp\Spaceship $node, CompilerContext $context, self $next) { - return $next($node->left, $context) <=> $next($node->right, $context); - }, - ]; - } } diff --git a/test/unit/NodeCompiler/CompileNodeToValueTest.php b/test/unit/NodeCompiler/CompileNodeToValueTest.php index d100aec35..e756713ef 100644 --- a/test/unit/NodeCompiler/CompileNodeToValueTest.php +++ b/test/unit/NodeCompiler/CompileNodeToValueTest.php @@ -5,11 +5,9 @@ namespace Roave\BetterReflectionTest\NodeCompiler; use PhpParser\Node; -use PhpParser\Node\Expr\BinaryOp\Coalesce; use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\Yield_; use PhpParser\Node\Name; -use PhpParser\Node\Scalar\LNumber; use PhpParser\Parser; use PHPUnit\Framework\TestCase; use Roave\BetterReflection\NodeCompiler\CompileNodeToValue; @@ -154,6 +152,7 @@ public function nodeProvider() : array ['1 <=> 4', -1], ['4 <=> 1', 1], ['1 <=> 1', 0], + ['5 ?? 4', 5], ]; } @@ -177,13 +176,6 @@ public function testExceptionThrownWhenInvalidNodeGiven() : void (new CompileNodeToValue())->__invoke(new Yield_(), $this->getDummyContext()); } - public function testExceptionThrownWhenCoalesceOperatorUsed() : void - { - $this->expectException(UnableToCompileNode::class); - $this->expectExceptionMessage('Unable to compile binary operator'); - (new CompileNodeToValue())->__invoke(new Coalesce(new LNumber(5), new LNumber(3)), $this->getDummyContext()); - } - public function testExceptionThrownWhenUndefinedConstUsed() : void { $this->expectException(UnableToCompileNode::class);