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