mirror of
https://github.com/danog/PHP-Parser.git
synced 2024-12-02 09:17:58 +01:00
Add constant expression evaluator (#402)
This commit is contained in:
parent
a02990a39a
commit
4b1d9667af
@ -24,6 +24,8 @@ Version 4.0.0-dev
|
|||||||
* Added `getComments()`, `getStartLine()`, `getEndLine()`, `getStartTokenPos()`, `getEndTokenPos()`,
|
* Added `getComments()`, `getStartLine()`, `getEndLine()`, `getStartTokenPos()`, `getEndTokenPos()`,
|
||||||
`getStartFilePos()` and `getEndFilePos()` methods to `Node`. These provide a more obvious access
|
`getStartFilePos()` and `getEndFilePos()` methods to `Node`. These provide a more obvious access
|
||||||
point for the already existing attributes of the same name.
|
point for the already existing attributes of the same name.
|
||||||
|
* Added `ConstExprEvaluator` to evaluate constant expressions to PHP values.
|
||||||
|
* Added `Expr\BinaryOp::getOperatorSigil()`, returning `+` for `Expr\BinaryOp\Plus`, etc.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
5
lib/PhpParser/ConstExprEvaluationException.php
Normal file
5
lib/PhpParser/ConstExprEvaluationException.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpParser;
|
||||||
|
|
||||||
|
class ConstExprEvaluationException extends \Exception {}
|
186
lib/PhpParser/ConstExprEvaluator.php
Normal file
186
lib/PhpParser/ConstExprEvaluator.php
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpParser;
|
||||||
|
|
||||||
|
use PhpParser\Node\Expr;
|
||||||
|
use PhpParser\Node\Scalar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates constant expressions.
|
||||||
|
*
|
||||||
|
* This evaluator is able to evaluate all constant expressions (as defined by PHP), which can be
|
||||||
|
* evaluated without further context. If a subexpression is not of this type, a user-provided
|
||||||
|
* fallback evaluator is invoked. To support all constant expressions that are also supported by
|
||||||
|
* PHP (and not already handled by this class), the fallback evaluator must be able to handle the
|
||||||
|
* following node types:
|
||||||
|
*
|
||||||
|
* * All Scalar\MagicConst\* nodes.
|
||||||
|
* * Expr\ConstFetch nodes. Only null/false/true are already handled by this class.
|
||||||
|
* * Expr\ClassConstFetch nodes.
|
||||||
|
*
|
||||||
|
* The fallback evaluator should throw ConstExprEvaluationException for nodes it cannot evaluate.
|
||||||
|
*
|
||||||
|
* The evaluation is performed as PHP would perform it, and as such may generate notices, warnings
|
||||||
|
* or Errors. For example, if the expression `1%0` is evaluated, an ArithmeticError is thrown. It is
|
||||||
|
* left to the consumer to handle these as appropriate.
|
||||||
|
*
|
||||||
|
* The evaluation is also dependent on runtime configuration in two respects: Firstly, floating
|
||||||
|
* point to string conversions are affected by the precision ini setting. Secondly, they are also
|
||||||
|
* affected by the LC_NUMERIC locale.
|
||||||
|
*/
|
||||||
|
class ConstExprEvaluator {
|
||||||
|
private $fallbackEvaluator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a constant expression evaluator.
|
||||||
|
*
|
||||||
|
* The provided fallback evaluator is invoked whenever a subexpression cannot be evaluated. See
|
||||||
|
* class doc comment for more information.
|
||||||
|
*
|
||||||
|
* @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
|
||||||
|
*/
|
||||||
|
public function __construct(callable $fallbackEvaluator = null) {
|
||||||
|
$this->fallbackEvaluator = $fallbackEvaluator ?? function(Expr $expr) {
|
||||||
|
throw new ConstExprEvaluationException(
|
||||||
|
"Expression of type {$expr->getType()} cannot be evaluated"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates a constant expression into a PHP value.
|
||||||
|
*
|
||||||
|
* If some part of the expression cannot be evaluated, the fallback evaluator passed to the
|
||||||
|
* constructor will be invoked. By default, if no fallback is provided, an exception of type
|
||||||
|
* ConstExprEvaluationException is thrown.
|
||||||
|
*
|
||||||
|
* See class doc comment for caveats and limitations.
|
||||||
|
*
|
||||||
|
* @param Expr $expr Constant expression to evaluate
|
||||||
|
* @return mixed Result of evaluation
|
||||||
|
* @throws ConstExprEvaluationException if the expression cannot be evaluated
|
||||||
|
*/
|
||||||
|
public function evaluate(Expr $expr) {
|
||||||
|
if ($expr instanceof Scalar\LNumber
|
||||||
|
|| $expr instanceof Scalar\DNumber
|
||||||
|
|| $expr instanceof Scalar\String_
|
||||||
|
) {
|
||||||
|
return $expr->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expr instanceof Expr\Array_) {
|
||||||
|
return $this->evaluateArray($expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unary operators
|
||||||
|
if ($expr instanceof Expr\UnaryPlus) {
|
||||||
|
return +$this->evaluate($expr->expr);
|
||||||
|
}
|
||||||
|
if ($expr instanceof Expr\UnaryMinus) {
|
||||||
|
return -$this->evaluate($expr->expr);
|
||||||
|
}
|
||||||
|
if ($expr instanceof Expr\BooleanNot) {
|
||||||
|
return !$this->evaluate($expr->expr);
|
||||||
|
}
|
||||||
|
if ($expr instanceof Expr\BitwiseNot) {
|
||||||
|
return ~$this->evaluate($expr->expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expr instanceof Expr\BinaryOp) {
|
||||||
|
return $this->evaluateBinaryOp($expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expr instanceof Expr\Ternary) {
|
||||||
|
return $this->evaluateTernary($expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) {
|
||||||
|
return $this->evaluate($expr->var)[$this->evaluate($expr->dim)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expr instanceof Expr\ConstFetch) {
|
||||||
|
return $this->evaluateConstFetch($expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($this->fallbackEvaluator)($expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function evaluateArray(Expr\Array_ $expr) {
|
||||||
|
$array = [];
|
||||||
|
foreach ($expr->items as $item) {
|
||||||
|
if (null !== $item->key) {
|
||||||
|
$array[$this->evaluate($item->key)] = $this->evaluate($item->value);
|
||||||
|
} else {
|
||||||
|
$array[] = $this->evaluate($item->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function evaluateTernary(Expr\Ternary $expr) {
|
||||||
|
if (null === $expr->if) {
|
||||||
|
return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->evaluate($expr->cond)
|
||||||
|
? $this->evaluate($expr->if)
|
||||||
|
: $this->evaluate($expr->else);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function evaluateBinaryOp(Expr\BinaryOp $expr) {
|
||||||
|
if ($expr instanceof Expr\BinaryOp\Coalesce
|
||||||
|
&& $expr->left instanceof Expr\ArrayDimFetch
|
||||||
|
) {
|
||||||
|
// This needs to be special cased to respect BP_VAR_IS fetch semantics
|
||||||
|
return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)]
|
||||||
|
?? $this->evaluate($expr->right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The evaluate() calls are repeated in each branch, because some of the operators are
|
||||||
|
// short-circuiting and evaluating the RHS in advance may be illegal in that case
|
||||||
|
$l = $expr->left;
|
||||||
|
$r = $expr->right;
|
||||||
|
switch ($expr->getOperatorSigil()) {
|
||||||
|
case '&': return $this->evaluate($l) & $this->evaluate($r);
|
||||||
|
case '|': return $this->evaluate($l) | $this->evaluate($r);
|
||||||
|
case '^': return $this->evaluate($l) ^ $this->evaluate($r);
|
||||||
|
case '&&': return $this->evaluate($l) && $this->evaluate($r);
|
||||||
|
case '||': return $this->evaluate($l) || $this->evaluate($r);
|
||||||
|
case '??': return $this->evaluate($l) ?? $this->evaluate($r);
|
||||||
|
case '.': return $this->evaluate($l) . $this->evaluate($r);
|
||||||
|
case '/': return $this->evaluate($l) / $this->evaluate($r);
|
||||||
|
case '==': return $this->evaluate($l) == $this->evaluate($r);
|
||||||
|
case '>': return $this->evaluate($l) > $this->evaluate($r);
|
||||||
|
case '>=': return $this->evaluate($l) >= $this->evaluate($r);
|
||||||
|
case '===': return $this->evaluate($l) === $this->evaluate($r);
|
||||||
|
case 'and': return $this->evaluate($l) and $this->evaluate($r);
|
||||||
|
case 'or': return $this->evaluate($l) or $this->evaluate($r);
|
||||||
|
case 'xor': return $this->evaluate($l) xor $this->evaluate($r);
|
||||||
|
case '-': return $this->evaluate($l) - $this->evaluate($r);
|
||||||
|
case '%': return $this->evaluate($l) % $this->evaluate($r);
|
||||||
|
case '*': return $this->evaluate($l) * $this->evaluate($r);
|
||||||
|
case '!=': return $this->evaluate($l) != $this->evaluate($r);
|
||||||
|
case '!==': return $this->evaluate($l) !== $this->evaluate($r);
|
||||||
|
case '+': return $this->evaluate($l) + $this->evaluate($r);
|
||||||
|
case '**': return $this->evaluate($l) ** $this->evaluate($r);
|
||||||
|
case '<<': return $this->evaluate($l) << $this->evaluate($r);
|
||||||
|
case '>>': return $this->evaluate($l) >> $this->evaluate($r);
|
||||||
|
case '<': return $this->evaluate($l) < $this->evaluate($r);
|
||||||
|
case '<=': return $this->evaluate($l) <= $this->evaluate($r);
|
||||||
|
case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \Exception('Should not happen');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function evaluateConstFetch(Expr\ConstFetch $expr) {
|
||||||
|
$name = $expr->name->toLowerString();
|
||||||
|
switch ($name) {
|
||||||
|
case 'null': return null;
|
||||||
|
case 'false': return false;
|
||||||
|
case 'true': return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($this->fallbackEvaluator)($expr);
|
||||||
|
}
|
||||||
|
}
|
@ -27,4 +27,14 @@ abstract class BinaryOp extends Expr
|
|||||||
public function getSubNodeNames() : array {
|
public function getSubNodeNames() : array {
|
||||||
return ['left', 'right'];
|
return ['left', 'right'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the operator sigil for this binary operation.
|
||||||
|
*
|
||||||
|
* In the case there are multiple possible sigils for an operator, this method does not
|
||||||
|
* necessarily return the one used in the parsed code.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract public function getOperatorSigil() : string;
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class BitwiseAnd extends BinaryOp
|
class BitwiseAnd extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '&';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class BitwiseOr extends BinaryOp
|
class BitwiseOr extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '|';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class BitwiseXor extends BinaryOp
|
class BitwiseXor extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '^';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class BooleanAnd extends BinaryOp
|
class BooleanAnd extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '&&';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class BooleanOr extends BinaryOp
|
class BooleanOr extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '||';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Coalesce extends BinaryOp
|
class Coalesce extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '??';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Concat extends BinaryOp
|
class Concat extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '.';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Div extends BinaryOp
|
class Div extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Equal extends BinaryOp
|
class Equal extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '==';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Greater extends BinaryOp
|
class Greater extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '>';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class GreaterOrEqual extends BinaryOp
|
class GreaterOrEqual extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '>=';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Identical extends BinaryOp
|
class Identical extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '===';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class LogicalAnd extends BinaryOp
|
class LogicalAnd extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return 'and';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class LogicalOr extends BinaryOp
|
class LogicalOr extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return 'or';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class LogicalXor extends BinaryOp
|
class LogicalXor extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return 'xor';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Minus extends BinaryOp
|
class Minus extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Mod extends BinaryOp
|
class Mod extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '%';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Mul extends BinaryOp
|
class Mul extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '*';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class NotEqual extends BinaryOp
|
class NotEqual extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '!=';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class NotIdentical extends BinaryOp
|
class NotIdentical extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '!==';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Plus extends BinaryOp
|
class Plus extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '+';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Pow extends BinaryOp
|
class Pow extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '**';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class ShiftLeft extends BinaryOp
|
class ShiftLeft extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '<<';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class ShiftRight extends BinaryOp
|
class ShiftRight extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '>>';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Smaller extends BinaryOp
|
class Smaller extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '<';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class SmallerOrEqual extends BinaryOp
|
class SmallerOrEqual extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '<=';
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,4 +6,7 @@ use PhpParser\Node\Expr\BinaryOp;
|
|||||||
|
|
||||||
class Spaceship extends BinaryOp
|
class Spaceship extends BinaryOp
|
||||||
{
|
{
|
||||||
|
public function getOperatorSigil() : string {
|
||||||
|
return '<=>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
97
test/PhpParser/ConstExprEvaluatorTest.php
Normal file
97
test/PhpParser/ConstExprEvaluatorTest.php
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpParser;
|
||||||
|
|
||||||
|
use PhpParser\Node\Expr;
|
||||||
|
use PhpParser\Node\Scalar;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ConstExprEvaluatorTest extends TestCase {
|
||||||
|
/** @dataProvider provideTestEvaluate */
|
||||||
|
public function testEvaluate($exprString, $expected) {
|
||||||
|
$parser = new Parser\Php7(new Lexer());
|
||||||
|
$expr = $parser->parse('<?php ' . $exprString . ';')[0]->expr;
|
||||||
|
$evaluator = new ConstExprEvaluator();
|
||||||
|
$this->assertSame($expected, $evaluator->evaluate($expr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideTestEvaluate() {
|
||||||
|
return [
|
||||||
|
['1', 1],
|
||||||
|
['1.0', 1.0],
|
||||||
|
['"foo"', "foo"],
|
||||||
|
['[0, 1]', [0, 1]],
|
||||||
|
['["foo" => "bar"]', ["foo" => "bar"]],
|
||||||
|
['NULL', null],
|
||||||
|
['False', false],
|
||||||
|
['true', true],
|
||||||
|
['+1', 1],
|
||||||
|
['-1', -1],
|
||||||
|
['~0', -1],
|
||||||
|
['!true', false],
|
||||||
|
['[0][0]', 0],
|
||||||
|
['"a"[0]', "a"],
|
||||||
|
['true ? 1 : (1/0)', 1],
|
||||||
|
['false ? (1/0) : 1', 1],
|
||||||
|
['42 ?: (1/0)', 42],
|
||||||
|
['false ?: 42', 42],
|
||||||
|
['false ?? 42', false],
|
||||||
|
['null ?? 42', 42],
|
||||||
|
['[0][0] ?? 42', 0],
|
||||||
|
['[][0] ?? 42', 42],
|
||||||
|
['0b11 & 0b10', 0b10],
|
||||||
|
['0b11 | 0b10', 0b11],
|
||||||
|
['0b11 ^ 0b10', 0b01],
|
||||||
|
['1 << 2', 4],
|
||||||
|
['4 >> 2', 1],
|
||||||
|
['"a" . "b"', "ab"],
|
||||||
|
['4 + 2', 6],
|
||||||
|
['4 - 2', 2],
|
||||||
|
['4 * 2', 8],
|
||||||
|
['4 / 2', 2],
|
||||||
|
['4 % 2', 0],
|
||||||
|
['4 ** 2', 16],
|
||||||
|
['1 == 1.0', true],
|
||||||
|
['1 != 1.0', false],
|
||||||
|
['1 < 2.0', true],
|
||||||
|
['1 <= 2.0', true],
|
||||||
|
['1 > 2.0', false],
|
||||||
|
['1 >= 2.0', false],
|
||||||
|
['1 <=> 2.0', -1],
|
||||||
|
['1 === 1.0', false],
|
||||||
|
['1 !== 1.0', true],
|
||||||
|
['true && true', true],
|
||||||
|
['true and true', true],
|
||||||
|
['false && (1/0)', false],
|
||||||
|
['false and (1/0)', false],
|
||||||
|
['false || false', false],
|
||||||
|
['false or false', false],
|
||||||
|
['true || (1/0)', true],
|
||||||
|
['true or (1/0)', true],
|
||||||
|
['true xor false', true],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \PhpParser\ConstExprEvaluationException
|
||||||
|
* @expectedExceptionMessage Expression of type Expr_Variable cannot be evaluated
|
||||||
|
*/
|
||||||
|
public function testEvaluateFails() {
|
||||||
|
$evaluator = new ConstExprEvaluator();
|
||||||
|
$evaluator->evaluate(new Expr\Variable('a'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEvaluateFallback() {
|
||||||
|
$evaluator = new ConstExprEvaluator(function(Expr $expr) {
|
||||||
|
if ($expr instanceof Scalar\MagicConst\Line) {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
throw new ConstExprEvaluationException();
|
||||||
|
});
|
||||||
|
$expr = new Expr\BinaryOp\Plus(
|
||||||
|
new Scalar\LNumber(8),
|
||||||
|
new Scalar\MagicConst\Line()
|
||||||
|
);
|
||||||
|
$this->assertSame(50, $evaluator->evaluate($expr));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user