PluginProbe
WPIDE – File Manager & Code Editor / 3.5.10
WPIDE – File Manager & Code Editor v3.5.10
3.5.10 3.5.9 3.5.8 3.5.7 2.0.14 2.0.15 2.0.16 2.0.2 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1 2.2 2.3 2.3.1 2.3.2 2.4.0 2.5 2.6 3.0 3.1 3.2 All 56 releases
wpide / vendor / nikic / php-parser / lib / PhpParser / ConstExprEvaluator.php

ConstExprEvaluator.php in WPIDE – File Manager & Code Editor 3.5.10, at vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php

230 lines 9.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace PhpParser;
4
5 use function array_merge;
6 use PhpParser\Node\Expr;
7 use PhpParser\Node\Scalar;
8
9 /**
10 * Evaluates constant expressions.
11 *
12 * This evaluator is able to evaluate all constant expressions (as defined by PHP), which can be
13 * evaluated without further context. If a subexpression is not of this type, a user-provided
14 * fallback evaluator is invoked. To support all constant expressions that are also supported by
15 * PHP (and not already handled by this class), the fallback evaluator must be able to handle the
16 * following node types:
17 *
18 * * All Scalar\MagicConst\* nodes.
19 * * Expr\ConstFetch nodes. Only null/false/true are already handled by this class.
20 * * Expr\ClassConstFetch nodes.
21 *
22 * The fallback evaluator should throw ConstExprEvaluationException for nodes it cannot evaluate.
23 *
24 * The evaluation is dependent on runtime configuration in two respects: Firstly, floating
25 * point to string conversions are affected by the precision ini setting. Secondly, they are also
26 * affected by the LC_NUMERIC locale.
27 */
28 class ConstExprEvaluator
29 {
30 private $fallbackEvaluator;
31
32 /**
33 * Create a constant expression evaluator.
34 *
35 * The provided fallback evaluator is invoked whenever a subexpression cannot be evaluated. See
36 * class doc comment for more information.
37 *
38 * @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
39 */
40 public function __construct(?callable $fallbackEvaluator = null) {
41 $this->fallbackEvaluator = $fallbackEvaluator ?? function(Expr $expr) {
42 throw new ConstExprEvaluationException(
43 "Expression of type {$expr->getType()} cannot be evaluated"
44 );
45 };
46 }
47
48 /**
49 * Silently evaluates a constant expression into a PHP value.
50 *
51 * Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException.
52 * The original source of the exception is available through getPrevious().
53 *
54 * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
55 * constructor will be invoked. By default, if no fallback is provided, an exception of type
56 * ConstExprEvaluationException is thrown.
57 *
58 * See class doc comment for caveats and limitations.
59 *
60 * @param Expr $expr Constant expression to evaluate
61 * @return mixed Result of evaluation
62 *
63 * @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred
64 */
65 public function evaluateSilently(Expr $expr) {
66 set_error_handler(function($num, $str, $file, $line) {
67 throw new \ErrorException($str, 0, $num, $file, $line);
68 });
69
70 try {
71 return $this->evaluate($expr);
72 } catch (\Throwable $e) {
73 if (!$e instanceof ConstExprEvaluationException) {
74 $e = new ConstExprEvaluationException(
75 "An error occurred during constant expression evaluation", 0, $e);
76 }
77 throw $e;
78 } finally {
79 restore_error_handler();
80 }
81 }
82
83 /**
84 * Directly evaluates a constant expression into a PHP value.
85 *
86 * May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these
87 * into a ConstExprEvaluationException.
88 *
89 * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
90 * constructor will be invoked. By default, if no fallback is provided, an exception of type
91 * ConstExprEvaluationException is thrown.
92 *
93 * See class doc comment for caveats and limitations.
94 *
95 * @param Expr $expr Constant expression to evaluate
96 * @return mixed Result of evaluation
97 *
98 * @throws ConstExprEvaluationException if the expression cannot be evaluated
99 */
100 public function evaluateDirectly(Expr $expr) {
101 return $this->evaluate($expr);
102 }
103
104 private function evaluate(Expr $expr) {
105 if ($expr instanceof Scalar\LNumber
106 || $expr instanceof Scalar\DNumber
107 || $expr instanceof Scalar\String_
108 ) {
109 return $expr->value;
110 }
111
112 if ($expr instanceof Expr\Array_) {
113 return $this->evaluateArray($expr);
114 }
115
116 // Unary operators
117 if ($expr instanceof Expr\UnaryPlus) {
118 return +$this->evaluate($expr->expr);
119 }
120 if ($expr instanceof Expr\UnaryMinus) {
121 return -$this->evaluate($expr->expr);
122 }
123 if ($expr instanceof Expr\BooleanNot) {
124 return !$this->evaluate($expr->expr);
125 }
126 if ($expr instanceof Expr\BitwiseNot) {
127 return ~$this->evaluate($expr->expr);
128 }
129
130 if ($expr instanceof Expr\BinaryOp) {
131 return $this->evaluateBinaryOp($expr);
132 }
133
134 if ($expr instanceof Expr\Ternary) {
135 return $this->evaluateTernary($expr);
136 }
137
138 if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) {
139 return $this->evaluate($expr->var)[$this->evaluate($expr->dim)];
140 }
141
142 if ($expr instanceof Expr\ConstFetch) {
143 return $this->evaluateConstFetch($expr);
144 }
145
146 return ($this->fallbackEvaluator)($expr);
147 }
148
149 private function evaluateArray(Expr\Array_ $expr) {
150 $array = [];
151 foreach ($expr->items as $item) {
152 if (null !== $item->key) {
153 $array[$this->evaluate($item->key)] = $this->evaluate($item->value);
154 } elseif ($item->unpack) {
155 $array = array_merge($array, $this->evaluate($item->value));
156 } else {
157 $array[] = $this->evaluate($item->value);
158 }
159 }
160 return $array;
161 }
162
163 private function evaluateTernary(Expr\Ternary $expr) {
164 if (null === $expr->if) {
165 return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
166 }
167
168 return $this->evaluate($expr->cond)
169 ? $this->evaluate($expr->if)
170 : $this->evaluate($expr->else);
171 }
172
173 private function evaluateBinaryOp(Expr\BinaryOp $expr) {
174 if ($expr instanceof Expr\BinaryOp\Coalesce
175 && $expr->left instanceof Expr\ArrayDimFetch
176 ) {
177 // This needs to be special cased to respect BP_VAR_IS fetch semantics
178 return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)]
179 ?? $this->evaluate($expr->right);
180 }
181
182 // The evaluate() calls are repeated in each branch, because some of the operators are
183 // short-circuiting and evaluating the RHS in advance may be illegal in that case
184 $l = $expr->left;
185 $r = $expr->right;
186 switch ($expr->getOperatorSigil()) {
187 case '&': return $this->evaluate($l) & $this->evaluate($r);
188 case '|': return $this->evaluate($l) | $this->evaluate($r);
189 case '^': return $this->evaluate($l) ^ $this->evaluate($r);
190 case '&&': return $this->evaluate($l) && $this->evaluate($r);
191 case '||': return $this->evaluate($l) || $this->evaluate($r);
192 case '??': return $this->evaluate($l) ?? $this->evaluate($r);
193 case '.': return $this->evaluate($l) . $this->evaluate($r);
194 case '/': return $this->evaluate($l) / $this->evaluate($r);
195 case '==': return $this->evaluate($l) == $this->evaluate($r);
196 case '>': return $this->evaluate($l) > $this->evaluate($r);
197 case '>=': return $this->evaluate($l) >= $this->evaluate($r);
198 case '===': return $this->evaluate($l) === $this->evaluate($r);
199 case 'and': return $this->evaluate($l) and $this->evaluate($r);
200 case 'or': return $this->evaluate($l) or $this->evaluate($r);
201 case 'xor': return $this->evaluate($l) xor $this->evaluate($r);
202 case '-': return $this->evaluate($l) - $this->evaluate($r);
203 case '%': return $this->evaluate($l) % $this->evaluate($r);
204 case '*': return $this->evaluate($l) * $this->evaluate($r);
205 case '!=': return $this->evaluate($l) != $this->evaluate($r);
206 case '!==': return $this->evaluate($l) !== $this->evaluate($r);
207 case '+': return $this->evaluate($l) + $this->evaluate($r);
208 case '**': return $this->evaluate($l) ** $this->evaluate($r);
209 case '<<': return $this->evaluate($l) << $this->evaluate($r);
210 case '>>': return $this->evaluate($l) >> $this->evaluate($r);
211 case '<': return $this->evaluate($l) < $this->evaluate($r);
212 case '<=': return $this->evaluate($l) <= $this->evaluate($r);
213 case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
214 }
215
216 throw new \Exception('Should not happen');
217 }
218
219 private function evaluateConstFetch(Expr\ConstFetch $expr) {
220 $name = $expr->name->toLowerString();
221 switch ($name) {
222 case 'null': return null;
223 case 'false': return false;
224 case 'true': return true;
225 }
226
227 return ($this->fallbackEvaluator)($expr);
228 }
229 }
230