ConstExprEvaluatorTest.php 4.4 KB

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