ConstExprEvaluatorTest.php 4.4 KB

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