Chris@13
|
1 <?php declare(strict_types=1);
|
Chris@13
|
2
|
Chris@13
|
3 namespace PhpParser;
|
Chris@13
|
4
|
Chris@13
|
5 use PhpParser\Node\Expr;
|
Chris@13
|
6 use PhpParser\Node\Scalar;
|
Chris@13
|
7
|
Chris@17
|
8 class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase
|
Chris@13
|
9 {
|
Chris@13
|
10 /** @dataProvider provideTestEvaluate */
|
Chris@13
|
11 public function testEvaluate($exprString, $expected) {
|
Chris@13
|
12 $parser = new Parser\Php7(new Lexer());
|
Chris@13
|
13 $expr = $parser->parse('<?php ' . $exprString . ';')[0]->expr;
|
Chris@13
|
14 $evaluator = new ConstExprEvaluator();
|
Chris@13
|
15 $this->assertSame($expected, $evaluator->evaluateDirectly($expr));
|
Chris@13
|
16 }
|
Chris@13
|
17
|
Chris@13
|
18 public function provideTestEvaluate() {
|
Chris@13
|
19 return [
|
Chris@13
|
20 ['1', 1],
|
Chris@13
|
21 ['1.0', 1.0],
|
Chris@13
|
22 ['"foo"', "foo"],
|
Chris@13
|
23 ['[0, 1]', [0, 1]],
|
Chris@13
|
24 ['["foo" => "bar"]', ["foo" => "bar"]],
|
Chris@13
|
25 ['NULL', null],
|
Chris@13
|
26 ['False', false],
|
Chris@13
|
27 ['true', true],
|
Chris@13
|
28 ['+1', 1],
|
Chris@13
|
29 ['-1', -1],
|
Chris@13
|
30 ['~0', -1],
|
Chris@13
|
31 ['!true', false],
|
Chris@13
|
32 ['[0][0]', 0],
|
Chris@13
|
33 ['"a"[0]', "a"],
|
Chris@13
|
34 ['true ? 1 : (1/0)', 1],
|
Chris@13
|
35 ['false ? (1/0) : 1', 1],
|
Chris@13
|
36 ['42 ?: (1/0)', 42],
|
Chris@13
|
37 ['false ?: 42', 42],
|
Chris@13
|
38 ['false ?? 42', false],
|
Chris@13
|
39 ['null ?? 42', 42],
|
Chris@13
|
40 ['[0][0] ?? 42', 0],
|
Chris@13
|
41 ['[][0] ?? 42', 42],
|
Chris@13
|
42 ['0b11 & 0b10', 0b10],
|
Chris@13
|
43 ['0b11 | 0b10', 0b11],
|
Chris@13
|
44 ['0b11 ^ 0b10', 0b01],
|
Chris@13
|
45 ['1 << 2', 4],
|
Chris@13
|
46 ['4 >> 2', 1],
|
Chris@13
|
47 ['"a" . "b"', "ab"],
|
Chris@13
|
48 ['4 + 2', 6],
|
Chris@13
|
49 ['4 - 2', 2],
|
Chris@13
|
50 ['4 * 2', 8],
|
Chris@13
|
51 ['4 / 2', 2],
|
Chris@13
|
52 ['4 % 2', 0],
|
Chris@13
|
53 ['4 ** 2', 16],
|
Chris@13
|
54 ['1 == 1.0', true],
|
Chris@13
|
55 ['1 != 1.0', false],
|
Chris@13
|
56 ['1 < 2.0', true],
|
Chris@13
|
57 ['1 <= 2.0', true],
|
Chris@13
|
58 ['1 > 2.0', false],
|
Chris@13
|
59 ['1 >= 2.0', false],
|
Chris@13
|
60 ['1 <=> 2.0', -1],
|
Chris@13
|
61 ['1 === 1.0', false],
|
Chris@13
|
62 ['1 !== 1.0', true],
|
Chris@13
|
63 ['true && true', true],
|
Chris@13
|
64 ['true and true', true],
|
Chris@13
|
65 ['false && (1/0)', false],
|
Chris@13
|
66 ['false and (1/0)', false],
|
Chris@13
|
67 ['false || false', false],
|
Chris@13
|
68 ['false or false', false],
|
Chris@13
|
69 ['true || (1/0)', true],
|
Chris@13
|
70 ['true or (1/0)', true],
|
Chris@13
|
71 ['true xor false', true],
|
Chris@13
|
72 ];
|
Chris@13
|
73 }
|
Chris@13
|
74
|
Chris@13
|
75 public function testEvaluateFails() {
|
Chris@17
|
76 $this->expectException(ConstExprEvaluationException::class);
|
Chris@17
|
77 $this->expectExceptionMessage('Expression of type Expr_Variable cannot be evaluated');
|
Chris@13
|
78 $evaluator = new ConstExprEvaluator();
|
Chris@13
|
79 $evaluator->evaluateDirectly(new Expr\Variable('a'));
|
Chris@13
|
80 }
|
Chris@13
|
81
|
Chris@13
|
82 public function testEvaluateFallback() {
|
Chris@13
|
83 $evaluator = new ConstExprEvaluator(function(Expr $expr) {
|
Chris@13
|
84 if ($expr instanceof Scalar\MagicConst\Line) {
|
Chris@13
|
85 return 42;
|
Chris@13
|
86 }
|
Chris@13
|
87 throw new ConstExprEvaluationException();
|
Chris@13
|
88 });
|
Chris@13
|
89 $expr = new Expr\BinaryOp\Plus(
|
Chris@13
|
90 new Scalar\LNumber(8),
|
Chris@13
|
91 new Scalar\MagicConst\Line()
|
Chris@13
|
92 );
|
Chris@13
|
93 $this->assertSame(50, $evaluator->evaluateDirectly($expr));
|
Chris@13
|
94 }
|
Chris@13
|
95
|
Chris@13
|
96 /**
|
Chris@13
|
97 * @dataProvider provideTestEvaluateSilently
|
Chris@13
|
98 */
|
Chris@13
|
99 public function testEvaluateSilently($expr, $exception, $msg) {
|
Chris@13
|
100 $evaluator = new ConstExprEvaluator();
|
Chris@13
|
101
|
Chris@13
|
102 try {
|
Chris@13
|
103 $evaluator->evaluateSilently($expr);
|
Chris@13
|
104 } catch (ConstExprEvaluationException $e) {
|
Chris@13
|
105 $this->assertSame(
|
Chris@13
|
106 'An error occurred during constant expression evaluation',
|
Chris@13
|
107 $e->getMessage()
|
Chris@13
|
108 );
|
Chris@13
|
109
|
Chris@13
|
110 $prev = $e->getPrevious();
|
Chris@13
|
111 $this->assertInstanceOf($exception, $prev);
|
Chris@13
|
112 $this->assertSame($msg, $prev->getMessage());
|
Chris@13
|
113 }
|
Chris@13
|
114 }
|
Chris@13
|
115
|
Chris@13
|
116 public function provideTestEvaluateSilently() {
|
Chris@13
|
117 return [
|
Chris@13
|
118 [
|
Chris@13
|
119 new Expr\BinaryOp\Mod(new Scalar\LNumber(42), new Scalar\LNumber(0)),
|
Chris@13
|
120 \Error::class,
|
Chris@13
|
121 'Modulo by zero'
|
Chris@13
|
122 ],
|
Chris@13
|
123 [
|
Chris@13
|
124 new Expr\BinaryOp\Div(new Scalar\LNumber(42), new Scalar\LNumber(0)),
|
Chris@13
|
125 \ErrorException::class,
|
Chris@13
|
126 'Division by zero'
|
Chris@13
|
127 ],
|
Chris@13
|
128 ];
|
Chris@13
|
129 }
|
Chris@13
|
130 }
|