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