PluginProbe
Elementor Website Builder – more than just a page builder / 3.28.0-dev2
Elementor Website Builder – more than just a page builder v3.28.0-dev2
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / vendor_prefixed / twig / src / Environment.php

Environment.php in Elementor Website Builder – more than just a page builder 3.28.0-dev2, at vendor_prefixed/twig/src/Environment.php

762 lines 25.8 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 Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 namespace ElementorDeps\Twig;
12
13 use ElementorDeps\Twig\Cache\CacheInterface;
14 use ElementorDeps\Twig\Cache\FilesystemCache;
15 use ElementorDeps\Twig\Cache\NullCache;
16 use ElementorDeps\Twig\Error\Error;
17 use ElementorDeps\Twig\Error\LoaderError;
18 use ElementorDeps\Twig\Error\RuntimeError;
19 use ElementorDeps\Twig\Error\SyntaxError;
20 use ElementorDeps\Twig\Extension\CoreExtension;
21 use ElementorDeps\Twig\Extension\EscaperExtension;
22 use ElementorDeps\Twig\Extension\ExtensionInterface;
23 use ElementorDeps\Twig\Extension\OptimizerExtension;
24 use ElementorDeps\Twig\Extension\YieldNotReadyExtension;
25 use ElementorDeps\Twig\Loader\ArrayLoader;
26 use ElementorDeps\Twig\Loader\ChainLoader;
27 use ElementorDeps\Twig\Loader\LoaderInterface;
28 use ElementorDeps\Twig\Node\Expression\Binary\AbstractBinary;
29 use ElementorDeps\Twig\Node\Expression\Unary\AbstractUnary;
30 use ElementorDeps\Twig\Node\ModuleNode;
31 use ElementorDeps\Twig\Node\Node;
32 use ElementorDeps\Twig\NodeVisitor\NodeVisitorInterface;
33 use ElementorDeps\Twig\Runtime\EscaperRuntime;
34 use ElementorDeps\Twig\RuntimeLoader\FactoryRuntimeLoader;
35 use ElementorDeps\Twig\RuntimeLoader\RuntimeLoaderInterface;
36 use ElementorDeps\Twig\TokenParser\TokenParserInterface;
37 /**
38 * Stores the Twig configuration and renders templates.
39 *
40 * @author Fabien Potencier <fabien@symfony.com>
41 */
42 class Environment
43 {
44 public const VERSION = '3.11.3';
45 public const VERSION_ID = 301103;
46 public const MAJOR_VERSION = 4;
47 public const MINOR_VERSION = 11;
48 public const RELEASE_VERSION = 3;
49 public const EXTRA_VERSION = '';
50 private $charset;
51 private $loader;
52 private $debug;
53 private $autoReload;
54 private $cache;
55 private $lexer;
56 private $parser;
57 private $compiler;
58 /** @var array<string, mixed> */
59 private $globals = [];
60 private $resolvedGlobals;
61 private $loadedTemplates;
62 private $strictVariables;
63 private $originalCache;
64 private $extensionSet;
65 private $runtimeLoaders = [];
66 private $runtimes = [];
67 private $optionsHash;
68 /** @var bool */
69 private $useYield;
70 private $defaultRuntimeLoader;
71 /**
72 * Constructor.
73 *
74 * Available options:
75 *
76 * * debug: When set to true, it automatically set "auto_reload" to true as
77 * well (default to false).
78 *
79 * * charset: The charset used by the templates (default to UTF-8).
80 *
81 * * cache: An absolute path where to store the compiled templates,
82 * a \Twig\Cache\CacheInterface implementation,
83 * or false to disable compilation cache (default).
84 *
85 * * auto_reload: Whether to reload the template if the original source changed.
86 * If you don't provide the auto_reload option, it will be
87 * determined automatically based on the debug value.
88 *
89 * * strict_variables: Whether to ignore invalid variables in templates
90 * (default to false).
91 *
92 * * autoescape: Whether to enable auto-escaping (default to html):
93 * * false: disable auto-escaping
94 * * html, js: set the autoescaping to one of the supported strategies
95 * * name: set the autoescaping strategy based on the template name extension
96 * * PHP callback: a PHP callback that returns an escaping strategy based on the template "name"
97 *
98 * * optimizations: A flag that indicates which optimizations to apply
99 * (default to -1 which means that all optimizations are enabled;
100 * set it to 0 to disable).
101 *
102 * * use_yield: Enable templates to exclusively use "yield" instead of "echo"
103 * (default to "false", but switch it to "true" when possible
104 * as this will be the only supported mode in Twig 4.0)
105 */
106 public function __construct(LoaderInterface $loader, $options = [])
107 {
108 $this->setLoader($loader);
109 $options = \array_merge(['debug' => \false, 'charset' => 'UTF-8', 'strict_variables' => \false, 'autoescape' => 'html', 'cache' => \false, 'auto_reload' => null, 'optimizations' => -1, 'use_yield' => \false], $options);
110 $this->useYield = (bool) $options['use_yield'];
111 $this->debug = (bool) $options['debug'];
112 $this->setCharset($options['charset'] ?? 'UTF-8');
113 $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
114 $this->strictVariables = (bool) $options['strict_variables'];
115 $this->setCache($options['cache']);
116 $this->extensionSet = new ExtensionSet();
117 $this->defaultRuntimeLoader = new FactoryRuntimeLoader([EscaperRuntime::class => function () {
118 return new EscaperRuntime($this->charset);
119 }]);
120 $this->addExtension(new CoreExtension());
121 $escaperExt = new EscaperExtension($options['autoescape']);
122 $escaperExt->setEnvironment($this, \false);
123 $this->addExtension($escaperExt);
124 if (\PHP_VERSION_ID >= 80000) {
125 $this->addExtension(new YieldNotReadyExtension($this->useYield));
126 }
127 $this->addExtension(new OptimizerExtension($options['optimizations']));
128 }
129 /**
130 * @internal
131 */
132 public function useYield() : bool
133 {
134 return $this->useYield;
135 }
136 /**
137 * Enables debugging mode.
138 */
139 public function enableDebug()
140 {
141 $this->debug = \true;
142 $this->updateOptionsHash();
143 }
144 /**
145 * Disables debugging mode.
146 */
147 public function disableDebug()
148 {
149 $this->debug = \false;
150 $this->updateOptionsHash();
151 }
152 /**
153 * Checks if debug mode is enabled.
154 *
155 * @return bool true if debug mode is enabled, false otherwise
156 */
157 public function isDebug()
158 {
159 return $this->debug;
160 }
161 /**
162 * Enables the auto_reload option.
163 */
164 public function enableAutoReload()
165 {
166 $this->autoReload = \true;
167 }
168 /**
169 * Disables the auto_reload option.
170 */
171 public function disableAutoReload()
172 {
173 $this->autoReload = \false;
174 }
175 /**
176 * Checks if the auto_reload option is enabled.
177 *
178 * @return bool true if auto_reload is enabled, false otherwise
179 */
180 public function isAutoReload()
181 {
182 return $this->autoReload;
183 }
184 /**
185 * Enables the strict_variables option.
186 */
187 public function enableStrictVariables()
188 {
189 $this->strictVariables = \true;
190 $this->updateOptionsHash();
191 }
192 /**
193 * Disables the strict_variables option.
194 */
195 public function disableStrictVariables()
196 {
197 $this->strictVariables = \false;
198 $this->updateOptionsHash();
199 }
200 /**
201 * Checks if the strict_variables option is enabled.
202 *
203 * @return bool true if strict_variables is enabled, false otherwise
204 */
205 public function isStrictVariables()
206 {
207 return $this->strictVariables;
208 }
209 /**
210 * Gets the current cache implementation.
211 *
212 * @param bool $original Whether to return the original cache option or the real cache instance
213 *
214 * @return CacheInterface|string|false A Twig\Cache\CacheInterface implementation,
215 * an absolute path to the compiled templates,
216 * or false to disable cache
217 */
218 public function getCache($original = \true)
219 {
220 return $original ? $this->originalCache : $this->cache;
221 }
222 /**
223 * Sets the current cache implementation.
224 *
225 * @param CacheInterface|string|false $cache A Twig\Cache\CacheInterface implementation,
226 * an absolute path to the compiled templates,
227 * or false to disable cache
228 */
229 public function setCache($cache)
230 {
231 if (\is_string($cache)) {
232 $this->originalCache = $cache;
233 $this->cache = new FilesystemCache($cache, $this->autoReload ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0);
234 } elseif (\false === $cache) {
235 $this->originalCache = $cache;
236 $this->cache = new NullCache();
237 } elseif ($cache instanceof CacheInterface) {
238 $this->originalCache = $this->cache = $cache;
239 } else {
240 throw new \LogicException('Cache can only be a string, false, or a \\Twig\\Cache\\CacheInterface implementation.');
241 }
242 }
243 /**
244 * Gets the template class associated with the given string.
245 *
246 * The generated template class is based on the following parameters:
247 *
248 * * The cache key for the given template;
249 * * The currently enabled extensions;
250 * * PHP version;
251 * * Twig version;
252 * * Options with what environment was created.
253 *
254 * @param string $name The name for which to calculate the template class name
255 * @param int|null $index The index if it is an embedded template
256 *
257 * @internal
258 */
259 public function getTemplateClass(string $name, ?int $index = null) : string
260 {
261 $key = $this->getLoader()->getCacheKey($name) . $this->optionsHash;
262 return '__TwigTemplate_' . \hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $key) . (null === $index ? '' : '___' . $index);
263 }
264 /**
265 * Renders a template.
266 *
267 * @param string|TemplateWrapper $name The template name
268 *
269 * @throws LoaderError When the template cannot be found
270 * @throws SyntaxError When an error occurred during compilation
271 * @throws RuntimeError When an error occurred during rendering
272 */
273 public function render($name, array $context = []) : string
274 {
275 return $this->load($name)->render($context);
276 }
277 /**
278 * Displays a template.
279 *
280 * @param string|TemplateWrapper $name The template name
281 *
282 * @throws LoaderError When the template cannot be found
283 * @throws SyntaxError When an error occurred during compilation
284 * @throws RuntimeError When an error occurred during rendering
285 */
286 public function display($name, array $context = []) : void
287 {
288 $this->load($name)->display($context);
289 }
290 /**
291 * Loads a template.
292 *
293 * @param string|TemplateWrapper $name The template name
294 *
295 * @throws LoaderError When the template cannot be found
296 * @throws RuntimeError When a previously generated cache is corrupted
297 * @throws SyntaxError When an error occurred during compilation
298 */
299 public function load($name) : TemplateWrapper
300 {
301 if ($name instanceof TemplateWrapper) {
302 return $name;
303 }
304 if ($name instanceof Template) {
305 trigger_deprecation('twig/twig', '3.9', 'Passing a "%s" instance to "%s" is deprecated.', self::class, __METHOD__);
306 return $name;
307 }
308 return new TemplateWrapper($this, $this->loadTemplate($this->getTemplateClass($name), $name));
309 }
310 /**
311 * Loads a template internal representation.
312 *
313 * This method is for internal use only and should never be called
314 * directly.
315 *
316 * @param string $name The template name
317 * @param int|null $index The index if it is an embedded template
318 *
319 * @throws LoaderError When the template cannot be found
320 * @throws RuntimeError When a previously generated cache is corrupted
321 * @throws SyntaxError When an error occurred during compilation
322 *
323 * @internal
324 */
325 public function loadTemplate(string $cls, string $name, ?int $index = null) : Template
326 {
327 $mainCls = $cls;
328 if (null !== $index) {
329 $cls .= '___' . $index;
330 }
331 if (isset($this->loadedTemplates[$cls])) {
332 return $this->loadedTemplates[$cls];
333 }
334 if (!\class_exists($cls, \false)) {
335 $key = $this->cache->generateKey($name, $mainCls);
336 if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) {
337 $this->cache->load($key);
338 }
339 if (!\class_exists($cls, \false)) {
340 $source = $this->getLoader()->getSourceContext($name);
341 $content = $this->compileSource($source);
342 $this->cache->write($key, $content);
343 $this->cache->load($key);
344 if (!\class_exists($mainCls, \false)) {
345 /* Last line of defense if either $this->bcWriteCacheFile was used,
346 * $this->cache is implemented as a no-op or we have a race condition
347 * where the cache was cleared between the above calls to write to and load from
348 * the cache.
349 */
350 eval('?>' . $content);
351 }
352 if (!\class_exists($cls, \false)) {
353 throw new RuntimeError(\sprintf('Failed to load Twig template "%s", index "%s": cache might be corrupted.', $name, $index), -1, $source);
354 }
355 }
356 }
357 $this->extensionSet->initRuntime();
358 return $this->loadedTemplates[$cls] = new $cls($this);
359 }
360 /**
361 * Creates a template from source.
362 *
363 * This method should not be used as a generic way to load templates.
364 *
365 * @param string $template The template source
366 * @param string|null $name An optional name of the template to be used in error messages
367 *
368 * @throws LoaderError When the template cannot be found
369 * @throws SyntaxError When an error occurred during compilation
370 */
371 public function createTemplate(string $template, ?string $name = null) : TemplateWrapper
372 {
373 $hash = \hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $template, \false);
374 if (null !== $name) {
375 $name = \sprintf('%s (string template %s)', $name, $hash);
376 } else {
377 $name = \sprintf('__string_template__%s', $hash);
378 }
379 $loader = new ChainLoader([new ArrayLoader([$name => $template]), $current = $this->getLoader()]);
380 $this->setLoader($loader);
381 try {
382 return new TemplateWrapper($this, $this->loadTemplate($this->getTemplateClass($name), $name));
383 } finally {
384 $this->setLoader($current);
385 }
386 }
387 /**
388 * Returns true if the template is still fresh.
389 *
390 * Besides checking the loader for freshness information,
391 * this method also checks if the enabled extensions have
392 * not changed.
393 *
394 * @param int $time The last modification time of the cached template
395 */
396 public function isTemplateFresh(string $name, int $time) : bool
397 {
398 return $this->extensionSet->getLastModified() <= $time && $this->getLoader()->isFresh($name, $time);
399 }
400 /**
401 * Tries to load a template consecutively from an array.
402 *
403 * Similar to load() but it also accepts instances of \Twig\TemplateWrapper
404 * and an array of templates where each is tried to be loaded.
405 *
406 * @param string|TemplateWrapper|array<string|TemplateWrapper> $names A template or an array of templates to try consecutively
407 *
408 * @throws LoaderError When none of the templates can be found
409 * @throws SyntaxError When an error occurred during compilation
410 */
411 public function resolveTemplate($names) : TemplateWrapper
412 {
413 if (!\is_array($names)) {
414 return $this->load($names);
415 }
416 $count = \count($names);
417 foreach ($names as $name) {
418 if ($name instanceof Template) {
419 trigger_deprecation('twig/twig', '3.9', 'Passing a "%s" instance to "%s" is deprecated.', Template::class, __METHOD__);
420 return new TemplateWrapper($this, $name);
421 }
422 if ($name instanceof TemplateWrapper) {
423 return $name;
424 }
425 if (1 !== $count && !$this->getLoader()->exists($name)) {
426 continue;
427 }
428 return $this->load($name);
429 }
430 throw new LoaderError(\sprintf('Unable to find one of the following templates: "%s".', \implode('", "', $names)));
431 }
432 public function setLexer(Lexer $lexer)
433 {
434 $this->lexer = $lexer;
435 }
436 /**
437 * @throws SyntaxError When the code is syntactically wrong
438 */
439 public function tokenize(Source $source) : TokenStream
440 {
441 if (null === $this->lexer) {
442 $this->lexer = new Lexer($this);
443 }
444 return $this->lexer->tokenize($source);
445 }
446 public function setParser(Parser $parser)
447 {
448 $this->parser = $parser;
449 }
450 /**
451 * Converts a token stream to a node tree.
452 *
453 * @throws SyntaxError When the token stream is syntactically or semantically wrong
454 */
455 public function parse(TokenStream $stream) : ModuleNode
456 {
457 if (null === $this->parser) {
458 $this->parser = new Parser($this);
459 }
460 return $this->parser->parse($stream);
461 }
462 public function setCompiler(Compiler $compiler)
463 {
464 $this->compiler = $compiler;
465 }
466 /**
467 * Compiles a node and returns the PHP code.
468 */
469 public function compile(Node $node) : string
470 {
471 if (null === $this->compiler) {
472 $this->compiler = new Compiler($this);
473 }
474 return $this->compiler->compile($node)->getSource();
475 }
476 /**
477 * Compiles a template source code.
478 *
479 * @throws SyntaxError When there was an error during tokenizing, parsing or compiling
480 */
481 public function compileSource(Source $source) : string
482 {
483 try {
484 return $this->compile($this->parse($this->tokenize($source)));
485 } catch (Error $e) {
486 $e->setSourceContext($source);
487 throw $e;
488 } catch (\Exception $e) {
489 throw new SyntaxError(\sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e);
490 }
491 }
492 public function setLoader(LoaderInterface $loader)
493 {
494 $this->loader = $loader;
495 }
496 public function getLoader() : LoaderInterface
497 {
498 return $this->loader;
499 }
500 public function setCharset(string $charset)
501 {
502 if ('UTF8' === ($charset = \strtoupper($charset ?: ''))) {
503 // iconv on Windows requires "UTF-8" instead of "UTF8"
504 $charset = 'UTF-8';
505 }
506 $this->charset = $charset;
507 }
508 public function getCharset() : string
509 {
510 return $this->charset;
511 }
512 public function hasExtension(string $class) : bool
513 {
514 return $this->extensionSet->hasExtension($class);
515 }
516 public function addRuntimeLoader(RuntimeLoaderInterface $loader)
517 {
518 $this->runtimeLoaders[] = $loader;
519 }
520 /**
521 * @template TExtension of ExtensionInterface
522 *
523 * @param class-string<TExtension> $class
524 *
525 * @return TExtension
526 */
527 public function getExtension(string $class) : ExtensionInterface
528 {
529 return $this->extensionSet->getExtension($class);
530 }
531 /**
532 * Returns the runtime implementation of a Twig element (filter/function/tag/test).
533 *
534 * @template TRuntime of object
535 *
536 * @param class-string<TRuntime> $class A runtime class name
537 *
538 * @return TRuntime The runtime implementation
539 *
540 * @throws RuntimeError When the template cannot be found
541 */
542 public function getRuntime(string $class)
543 {
544 if (isset($this->runtimes[$class])) {
545 return $this->runtimes[$class];
546 }
547 foreach ($this->runtimeLoaders as $loader) {
548 if (null !== ($runtime = $loader->load($class))) {
549 return $this->runtimes[$class] = $runtime;
550 }
551 }
552 if (null !== ($runtime = $this->defaultRuntimeLoader->load($class))) {
553 return $this->runtimes[$class] = $runtime;
554 }
555 throw new RuntimeError(\sprintf('Unable to load the "%s" runtime.', $class));
556 }
557 public function addExtension(ExtensionInterface $extension)
558 {
559 $this->extensionSet->addExtension($extension);
560 $this->updateOptionsHash();
561 }
562 /**
563 * @param ExtensionInterface[] $extensions An array of extensions
564 */
565 public function setExtensions(array $extensions)
566 {
567 $this->extensionSet->setExtensions($extensions);
568 $this->updateOptionsHash();
569 }
570 /**
571 * @return ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on)
572 */
573 public function getExtensions() : array
574 {
575 return $this->extensionSet->getExtensions();
576 }
577 public function addTokenParser(TokenParserInterface $parser)
578 {
579 $this->extensionSet->addTokenParser($parser);
580 }
581 /**
582 * @return TokenParserInterface[]
583 *
584 * @internal
585 */
586 public function getTokenParsers() : array
587 {
588 return $this->extensionSet->getTokenParsers();
589 }
590 /**
591 * @internal
592 */
593 public function getTokenParser(string $name) : ?TokenParserInterface
594 {
595 return $this->extensionSet->getTokenParser($name);
596 }
597 public function registerUndefinedTokenParserCallback(callable $callable) : void
598 {
599 $this->extensionSet->registerUndefinedTokenParserCallback($callable);
600 }
601 public function addNodeVisitor(NodeVisitorInterface $visitor)
602 {
603 $this->extensionSet->addNodeVisitor($visitor);
604 }
605 /**
606 * @return NodeVisitorInterface[]
607 *
608 * @internal
609 */
610 public function getNodeVisitors() : array
611 {
612 return $this->extensionSet->getNodeVisitors();
613 }
614 public function addFilter(TwigFilter $filter)
615 {
616 $this->extensionSet->addFilter($filter);
617 }
618 /**
619 * @internal
620 */
621 public function getFilter(string $name) : ?TwigFilter
622 {
623 return $this->extensionSet->getFilter($name);
624 }
625 public function registerUndefinedFilterCallback(callable $callable) : void
626 {
627 $this->extensionSet->registerUndefinedFilterCallback($callable);
628 }
629 /**
630 * Gets the registered Filters.
631 *
632 * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
633 *
634 * @return TwigFilter[]
635 *
636 * @see registerUndefinedFilterCallback
637 *
638 * @internal
639 */
640 public function getFilters() : array
641 {
642 return $this->extensionSet->getFilters();
643 }
644 public function addTest(TwigTest $test)
645 {
646 $this->extensionSet->addTest($test);
647 }
648 /**
649 * @return TwigTest[]
650 *
651 * @internal
652 */
653 public function getTests() : array
654 {
655 return $this->extensionSet->getTests();
656 }
657 /**
658 * @internal
659 */
660 public function getTest(string $name) : ?TwigTest
661 {
662 return $this->extensionSet->getTest($name);
663 }
664 public function addFunction(TwigFunction $function)
665 {
666 $this->extensionSet->addFunction($function);
667 }
668 /**
669 * @internal
670 */
671 public function getFunction(string $name) : ?TwigFunction
672 {
673 return $this->extensionSet->getFunction($name);
674 }
675 public function registerUndefinedFunctionCallback(callable $callable) : void
676 {
677 $this->extensionSet->registerUndefinedFunctionCallback($callable);
678 }
679 /**
680 * Gets registered functions.
681 *
682 * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
683 *
684 * @return TwigFunction[]
685 *
686 * @see registerUndefinedFunctionCallback
687 *
688 * @internal
689 */
690 public function getFunctions() : array
691 {
692 return $this->extensionSet->getFunctions();
693 }
694 /**
695 * Registers a Global.
696 *
697 * New globals can be added before compiling or rendering a template;
698 * but after, you can only update existing globals.
699 *
700 * @param mixed $value The global value
701 */
702 public function addGlobal(string $name, $value)
703 {
704 if ($this->extensionSet->isInitialized() && !\array_key_exists($name, $this->getGlobals())) {
705 throw new \LogicException(\sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
706 }
707 if (null !== $this->resolvedGlobals) {
708 $this->resolvedGlobals[$name] = $value;
709 } else {
710 $this->globals[$name] = $value;
711 }
712 }
713 /**
714 * @internal
715 *
716 * @return array<string, mixed>
717 */
718 public function getGlobals() : array
719 {
720 if ($this->extensionSet->isInitialized()) {
721 if (null === $this->resolvedGlobals) {
722 $this->resolvedGlobals = \array_merge($this->extensionSet->getGlobals(), $this->globals);
723 }
724 return $this->resolvedGlobals;
725 }
726 return \array_merge($this->extensionSet->getGlobals(), $this->globals);
727 }
728 public function mergeGlobals(array $context) : array
729 {
730 // we don't use array_merge as the context being generally
731 // bigger than globals, this code is faster.
732 foreach ($this->getGlobals() as $key => $value) {
733 if (!\array_key_exists($key, $context)) {
734 $context[$key] = $value;
735 }
736 }
737 return $context;
738 }
739 /**
740 * @internal
741 *
742 * @return array<string, array{precedence: int, class: class-string<AbstractUnary>}>
743 */
744 public function getUnaryOperators() : array
745 {
746 return $this->extensionSet->getUnaryOperators();
747 }
748 /**
749 * @internal
750 *
751 * @return array<string, array{precedence: int, class: class-string<AbstractBinary>, associativity: ExpressionParser::OPERATOR_*}>
752 */
753 public function getBinaryOperators() : array
754 {
755 return $this->extensionSet->getBinaryOperators();
756 }
757 private function updateOptionsHash() : void
758 {
759 $this->optionsHash = \implode(':', [$this->extensionSet->getSignature(), \PHP_MAJOR_VERSION, \PHP_MINOR_VERSION, self::VERSION, (int) $this->debug, (int) $this->strictVariables, $this->useYield ? '1' : '0']);
760 }
761 }
762