annotate vendor/nikic/php-parser/doc/component/Constant_expression_evaluation.markdown @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 5fb285c0d0e3
children
rev   line source
Chris@13 1 Constant expression evaluation
Chris@13 2 ==============================
Chris@13 3
Chris@13 4 Initializers for constants, properties, parameters, etc. have limited support for expressions. For
Chris@13 5 example:
Chris@13 6
Chris@13 7 ```php
Chris@13 8 <?php
Chris@13 9 class Test {
Chris@13 10 const SECONDS_IN_HOUR = 60 * 60;
Chris@13 11 const SECONDS_IN_DAY = 24 * self::SECONDS_IN_HOUR;
Chris@13 12 }
Chris@13 13 ```
Chris@13 14
Chris@13 15 PHP-Parser supports evaluation of such constant expressions through the `ConstExprEvaluator` class:
Chris@13 16
Chris@13 17 ```php
Chris@13 18 <?php
Chris@13 19
Chris@13 20 use PhpParser\{ConstExprEvaluator, ConstExprEvaluationException};
Chris@13 21
Chris@13 22 $evalutator = new ConstExprEvaluator();
Chris@13 23 try {
Chris@13 24 $value = $evalutator->evaluateSilently($someExpr);
Chris@13 25 } catch (ConstExprEvaluationException $e) {
Chris@13 26 // Either the expression contains unsupported expression types,
Chris@13 27 // or an error occurred during evaluation
Chris@13 28 }
Chris@13 29 ```
Chris@13 30
Chris@13 31 Error handling
Chris@13 32 --------------
Chris@13 33
Chris@13 34 The constant evaluator provides two methods, `evaluateDirectly()` and `evaluateSilently()`, which
Chris@13 35 differ in error behavior. `evaluateDirectly()` will evaluate the expression as PHP would, including
Chris@13 36 any generated warnings or Errors. `evaluateSilently()` will instead convert warnings and Errors into
Chris@13 37 a `ConstExprEvaluationException`. For example:
Chris@13 38
Chris@13 39 ```php
Chris@13 40 <?php
Chris@13 41
Chris@13 42 use PhpParser\{ConstExprEvaluator, ConstExprEvaluationException};
Chris@13 43 use PhpParser\Node\{Expr, Scalar};
Chris@13 44
Chris@13 45 $evaluator = new ConstExprEvaluator();
Chris@13 46
Chris@13 47 // 10 / 0
Chris@13 48 $expr = new Expr\BinaryOp\Div(new Scalar\LNumber(10), new Scalar\LNumber(0));
Chris@13 49
Chris@13 50 var_dump($evaluator->evaluateDirectly($expr)); // float(INF)
Chris@13 51 // Warning: Division by zero
Chris@13 52
Chris@13 53 try {
Chris@13 54 $evaluator->evaluateSilently($expr);
Chris@13 55 } catch (ConstExprEvaluationException $e) {
Chris@13 56 var_dump($e->getPrevious()->getMessage()); // Division by zero
Chris@13 57 }
Chris@13 58 ```
Chris@13 59
Chris@13 60 For the purposes of static analysis, you will likely want to use `evaluateSilently()` and leave
Chris@13 61 erroring expressions unevaluated.
Chris@13 62
Chris@13 63 Unsupported expressions and evaluator fallback
Chris@13 64 ----------------------------------------------
Chris@13 65
Chris@13 66 The constant expression evaluator supports all expression types that are permitted in constant
Chris@13 67 expressions, apart from the following:
Chris@13 68
Chris@13 69 * `Scalar\MagicConst\*`
Chris@13 70 * `Expr\ConstFetch` (only null/false/true are handled)
Chris@13 71 * `Expr\ClassConstFetch`
Chris@13 72
Chris@13 73 Handling these expression types requires non-local information, such as which global constants are
Chris@13 74 defined. By default, the evaluator will throw a `ConstExprEvaluationException` when it encounters
Chris@13 75 an unsupported expression type.
Chris@13 76
Chris@13 77 It is possible to override this behavior and support resolution for these expression types by
Chris@13 78 specifying an evaluation fallback function:
Chris@13 79
Chris@13 80 ```php
Chris@13 81 <?php
Chris@13 82
Chris@13 83 use PhpParser\{ConstExprEvaluator, ConstExprEvaluationException};
Chris@13 84 use PhpParser\Node\Expr;
Chris@13 85
Chris@13 86 $evalutator = new ConstExprEvaluator(function(Expr $expr) {
Chris@13 87 if ($expr instanceof Expr\ConstFetch) {
Chris@13 88 return fetchConstantSomehow($expr);
Chris@13 89 }
Chris@13 90 if ($expr instanceof Expr\ClassConstFetch) {
Chris@13 91 return fetchClassConstantSomehow($expr);
Chris@13 92 }
Chris@13 93 // etc.
Chris@13 94 throw new ConstExprEvaluationException(
Chris@13 95 "Expression of type {$expr->getType()} cannot be evaluated");
Chris@13 96 });
Chris@13 97
Chris@13 98 try {
Chris@13 99 $evalutator->evaluateSilently($someExpr);
Chris@13 100 } catch (ConstExprEvaluationException $e) {
Chris@13 101 // Handle exception
Chris@13 102 }
Chris@13 103 ```
Chris@13 104
Chris@13 105 Implementers are advised to ensure that evaluation of indirect constant references cannot lead to
Chris@13 106 infinite recursion. For example, the following code could lead to infinite recursion if constant
Chris@13 107 lookup is implemented naively.
Chris@13 108
Chris@13 109 ```php
Chris@13 110 <?php
Chris@13 111 class Test {
Chris@13 112 const A = self::B;
Chris@13 113 const B = self::A;
Chris@13 114 }
Chris@13 115 ```