ConstExprEvaluatorTest.php 4.1 KB

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