PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.25
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.25
1.10.25 1.10.24 1.10.23 1.10.22 1.10.21 1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 All 169 releases
woocommerce-pos / vendor / mustache / mustache / src / Context.php

Context.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.25, at vendor/mustache/mustache/src/Context.php

428 lines 13.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of Mustache.php.
5 *
6 * (c) 2010-2026 Justin Hileman
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Mustache;
13
14 use Mustache\Exception\InvalidArgumentException;
15 use Mustache\Exception\UnknownVariableException;
16
17 /**
18 * Mustache Template rendering Context.
19 */
20 class Context
21 {
22 private $strictTags = Engine::STRICT_NONE;
23 private $stack = [];
24 private $stackSize = 0;
25 private $blockScopes = [[]];
26 private $blockScopeIndex = 0;
27 private $renderingStack = [];
28 private $renderingStackSize = 0;
29 private static $publicMethods = [];
30
31 private $buggyPropertyShadowing = false;
32
33 /**
34 * Mustache rendering Context constructor.
35 *
36 * @param mixed $context Default rendering context (default: null)
37 * @param bool $buggyPropertyShadowing See Engine::getBuggyPropertyShadowing (default: false)
38 * @param int $strictTags Strict tag bitmask (default: Engine::STRICT_NONE)
39 */
40 public function __construct($context = null, $buggyPropertyShadowing = false, $strictTags = Engine::STRICT_NONE)
41 {
42 if ($context !== null) {
43 $this->stack = [$context];
44 $this->stackSize = 1;
45 }
46
47 $this->buggyPropertyShadowing = $buggyPropertyShadowing;
48 $this->strictTags = $strictTags;
49 }
50
51 /**
52 * Push a new Context frame onto the stack.
53 *
54 * @param mixed $value Object or array to use for context
55 */
56 public function push($value)
57 {
58 $this->stack[$this->stackSize++] = $value;
59 }
60
61 /**
62 * Push a new Context frame onto the block context stack.
63 *
64 * @param mixed $value Object or array to use for block context
65 */
66 public function pushBlockContext($value)
67 {
68 $this->blockScopes[$this->blockScopeIndex][] = $value;
69 }
70
71 /**
72 * Pop the last Context frame from the stack.
73 *
74 * @return mixed Last Context frame (object or array)
75 */
76 public function pop()
77 {
78 if ($this->stackSize === 0) {
79 return null;
80 }
81
82 $index = --$this->stackSize;
83 $value = $this->stack[$index];
84 unset($this->stack[$index]);
85
86 return $value;
87 }
88
89 /**
90 * Pop the last block Context frame from the stack.
91 *
92 * @return mixed Last block Context frame (object or array)
93 */
94 public function popBlockContext()
95 {
96 return array_pop($this->blockScopes[$this->blockScopeIndex]);
97 }
98
99 /**
100 * Push a rendering debug frame onto the stack.
101 *
102 * This is used by debug-compiled templates to preserve the current Mustache
103 * tag context if rendering fails.
104 */
105 public function pushRenderingFrame(array $frame)
106 {
107 $this->renderingStack[$this->renderingStackSize++] = $frame;
108 }
109
110 /**
111 * Pop the last rendering debug frame from the stack.
112 */
113 public function popRenderingFrame()
114 {
115 if ($this->renderingStackSize === 0) {
116 return null;
117 }
118
119 $index = --$this->renderingStackSize;
120 $value = $this->renderingStack[$index];
121 unset($this->renderingStack[$index]);
122
123 return $value;
124 }
125
126 /**
127 * Get the current rendering debug stack.
128 *
129 * @return array
130 */
131 public function getRenderingStack()
132 {
133 return $this->renderingStack;
134 }
135
136 /**
137 * Clear the rendering debug stack.
138 */
139 public function clearRenderingStack()
140 {
141 $this->renderingStack = [];
142 $this->renderingStackSize = 0;
143 }
144
145 /**
146 * Get the last Context frame.
147 *
148 * @return mixed Last Context frame (object or array)
149 */
150 public function last()
151 {
152 return $this->stackSize === 0 ? false : $this->stack[$this->stackSize - 1];
153 }
154
155 /**
156 * Find a variable in the Context stack.
157 *
158 * Starting with the last Context frame (the context of the innermost section), and working back to the top-level
159 * rendering context, look for a variable with the given name:
160 *
161 * * If the Context frame is an associative array which contains the key $id, returns the value of that element.
162 * * If the Context frame is an object, this will check first for a public method, then a public property named
163 * $id. Failing both of these, it will try `__isset` and `__get` magic methods.
164 * * If a value named $id is not found in any Context frame, returns an empty string (or throws an
165 * UnknownVariableException if the relevant strict tag is enabled).
166 *
167 * @param string $id Variable name
168 * @param int $strictTag Strict tag responsible for this lookup (default: Engine::STRICT_INTERPOLATION)
169 *
170 * @return mixed Variable value, or '' if not found
171 *
172 * @throws UnknownVariableException if the relevant strict tag is enabled and the variable is not found
173 */
174 public function find($id, $strictTag = Engine::STRICT_INTERPOLATION)
175 {
176 return $this->findVariableInStack($id, $this->stack, $this->stackSize, $strictTag);
177 }
178
179 /**
180 * Find a 'dot notation' variable in the Context stack.
181 *
182 * Note that dot notation traversal bubbles through scope differently than the regular find method. After finding
183 * the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous
184 * result. For example, given the following context stack:
185 *
186 * $data = [
187 * 'name' => 'Fred',
188 * 'child' => [
189 * 'name' => 'Bob'
190 * ],
191 * ];
192 *
193 * ... and the Mustache following template:
194 *
195 * {{ child.name }}
196 *
197 * ... the `name` value is only searched for within the `child` value of the global Context, not within parent
198 * Context frames.
199 *
200 * @param string $id Dotted variable selector
201 * @param bool $strictCallables (default: false)
202 * @param int $strictTag Strict tag responsible for this lookup (default: Engine::STRICT_INTERPOLATION)
203 *
204 * @return mixed Variable value, or '' if not found
205 *
206 * @throws UnknownVariableException if the relevant strict tag is enabled and the variable is not found
207 */
208 public function findDot($id, $strictCallables = false, $strictTag = Engine::STRICT_INTERPOLATION)
209 {
210 $chunks = explode('.', $id);
211 $chunkCount = count($chunks);
212 $value = $this->findVariableInStack($chunks[0], $this->stack, $this->stackSize, $strictTag);
213
214 // This wasn't really a dotted name, so we can just return the value.
215 if ($chunkCount === 1) {
216 return $value;
217 }
218
219 for ($i = 1; $i < $chunkCount; $i++) {
220 $isCallable = $strictCallables ? (is_object($value) && is_callable($value)) : (!is_string($value) && is_callable($value));
221
222 if ($isCallable) {
223 $value = $value();
224 } elseif ($value === '') {
225 return $value;
226 }
227
228 $value = $this->findVariableInStack($chunks[$i], [$value], 1, $strictTag);
229 }
230
231 return $value;
232 }
233
234 /**
235 * Find an 'anchored dot notation' variable in the Context stack.
236 *
237 * This is the same as findDot(), except it looks in the top of the context
238 * stack for the first value, rather than searching the whole context stack
239 * and starting from there.
240 *
241 * @see Mustache\Context::findDot
242 *
243 * @throws InvalidArgumentException if given an invalid anchored dot $id
244 *
245 * @param string $id Dotted variable selector
246 * @param bool $strictCallables (default: false)
247 * @param int $strictTag Strict tag responsible for this lookup (default: Engine::STRICT_INTERPOLATION)
248 *
249 * @return mixed Variable value, or '' if not found
250 *
251 * @throws UnknownVariableException if the relevant strict tag is enabled and the variable is not found
252 */
253 public function findAnchoredDot($id, $strictCallables = false, $strictTag = Engine::STRICT_INTERPOLATION)
254 {
255 $chunks = explode('.', $id);
256 if ($chunks[0] !== '') {
257 throw new InvalidArgumentException(sprintf('Unexpected id for findAnchoredDot: %s', $id));
258 }
259
260 $value = $this->last();
261 $chunkCount = count($chunks);
262
263 for ($i = 1; $i < $chunkCount; $i++) {
264 if ($value === '') {
265 return $value;
266 }
267
268 $value = $this->findVariableInStack($chunks[$i], [$value], 1, $strictTag);
269 }
270
271 return $value;
272 }
273
274 /**
275 * Find an argument in the block context stack.
276 *
277 * @param string $id
278 *
279 * @return mixed Variable value, or '' if not found
280 */
281 public function findInBlock($id)
282 {
283 foreach ($this->blockScopes[$this->blockScopeIndex] as $context) {
284 if (array_key_exists($id, $context)) {
285 return $context[$id];
286 }
287 }
288
289 return '';
290 }
291
292 /**
293 * Get the block override names visible in the current block context scope,
294 * keyed by name for cheap lookup.
295 *
296 * @return bool[]
297 */
298 public function getBlockContextNames()
299 {
300 $names = [];
301 foreach ($this->blockScopes[$this->blockScopeIndex] as $context) {
302 foreach ($context as $id => $value) {
303 $names[$id] = true;
304 }
305 }
306
307 return $names;
308 }
309
310 /**
311 * Start an isolated block context scope.
312 *
313 * Block lookups inside a nested parent partial should not resolve against
314 * block contexts pushed by the surrounding block argument.
315 */
316 public function pushBlockContextScope()
317 {
318 $this->blockScopes[++$this->blockScopeIndex] = [];
319 }
320
321 /**
322 * End the current isolated block context scope, restoring visibility into
323 * the surrounding block context scope.
324 */
325 public function popBlockContextScope()
326 {
327 // The root scope is always kept; popping it would leave findInBlock
328 // with no scope to read.
329 if ($this->blockScopeIndex === 0) {
330 return;
331 }
332
333 unset($this->blockScopes[$this->blockScopeIndex]);
334 $this->blockScopeIndex--;
335 }
336
337 /**
338 * Helper function to find a variable in the Context stack.
339 *
340 * @see Mustache\Context::find
341 *
342 * @param string $id Variable name
343 * @param array $stack Context stack
344 * @param int $stackSize Number of frames in $stack
345 * @param int $strictTag Strict tag responsible for this lookup
346 *
347 * @return mixed Variable value, or '' if not found
348 *
349 * @throws UnknownVariableException if the relevant strict tag is enabled and the variable is not found
350 */
351 private function findVariableInStack($id, array $stack, $stackSize, $strictTag)
352 {
353 for ($i = $stackSize - 1; $i >= 0; $i--) {
354 $frame = $stack[$i];
355
356 if (is_array($frame)) {
357 if (array_key_exists($id, $frame)) {
358 return $frame[$id];
359 }
360
361 continue;
362 }
363
364 if (!is_object($frame) || $frame instanceof \Closure) {
365 continue;
366 }
367
368 if ($this->hasPublicMethod($frame, $id)) {
369 return $frame->$id();
370 }
371
372 if (isset($frame->$id)) {
373 return $frame->$id;
374 }
375
376 // Preserve backwards compatibility with a property shadowing bug in
377 // Mustache.php <= 2.14.2
378 // See https://github.com/bobthecow/mustache.php/pull/410
379 if ($this->buggyPropertyShadowing) {
380 if ($frame instanceof \ArrayAccess && isset($frame[$id])) {
381 return $frame[$id];
382 }
383 } else {
384 if (property_exists($frame, $id)) {
385 $rp = new \ReflectionProperty($frame, $id);
386 if ($rp->isPublic()) {
387 return $frame->$id;
388 }
389 }
390
391 if ($frame instanceof \ArrayAccess && $frame->offsetExists($id)) {
392 return $frame[$id];
393 }
394 }
395 }
396
397 if (($this->strictTags & $strictTag) !== 0) {
398 throw new UnknownVariableException($id);
399 }
400
401 return '';
402 }
403
404 /**
405 * Check whether an object exposes a named public method.
406 *
407 * This intentionally uses get_class_methods() rather than is_callable() so
408 * magic __call handlers do not change Mustache lookup semantics.
409 *
410 * @see https://github.com/bobthecow/mustache.php/wiki/Magic-Methods
411 *
412 * @param object $object
413 * @param string $method
414 *
415 * @return bool
416 */
417 private function hasPublicMethod($object, $method)
418 {
419 $class = get_class($object);
420
421 if (!isset(self::$publicMethods[$class])) {
422 self::$publicMethods[$class] = array_fill_keys(array_map('strtolower', get_class_methods($object)), true);
423 }
424
425 return isset(self::$publicMethods[$class][strtolower($method)]);
426 }
427 }
428