Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Environment.php
001 <?php
002
003 /*
004 * This file is part of Twig.
005 *
006 * (c) Fabien Potencier
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Twig;
013
014 use Twig\Cache\CacheInterface;
015 use Twig\Cache\FilesystemCache;
016 use Twig\Cache\NullCache;
017 use Twig\Error\Error;
018 use Twig\Error\LoaderError;
019 use Twig\Error\RuntimeError;
020 use Twig\Error\SyntaxError;
021 use Twig\Extension\CoreExtension;
022 use Twig\Extension\EscaperExtension;
023 use Twig\Extension\ExtensionInterface;
024 use Twig\Extension\OptimizerExtension;
025 use Twig\Loader\ArrayLoader;
026 use Twig\Loader\ChainLoader;
027 use Twig\Loader\LoaderInterface;
028 use Twig\Node\ModuleNode;
029 use Twig\Node\Node;
030 use Twig\NodeVisitor\NodeVisitorInterface;
031 use Twig\RuntimeLoader\RuntimeLoaderInterface;
032 use Twig\TokenParser\TokenParserInterface;
033
034 /**
035 * Stores the Twig configuration and renders templates.
036 *
037 * @author Fabien Potencier <fabien@symfony.com>
038 */
039 class Environment
040 {
041 public const VERSION = '2.16.0';
042 public const VERSION_ID = 21600;
043 public const MAJOR_VERSION = 2;
044 public const MINOR_VERSION = 16;
045 public const RELEASE_VERSION = 0;
046 public const EXTRA_VERSION = '';
047
048 private $charset;
049 private $loader;
050 private $debug;
051 private $autoReload;
052 private $cache;
053 private $lexer;
054 private $parser;
055 private $compiler;
056 private $baseTemplateClass;
057 private $globals = [];
058 private $resolvedGlobals;
059 private $loadedTemplates;
060 private $strictVariables;
061 private $templateClassPrefix = '__TwigTemplate_';
062 private $originalCache;
063 private $extensionSet;
064 private $runtimeLoaders = [];
065 private $runtimes = [];
066 private $optionsHash;
067
068 /**
069 * Constructor.
070 *
071 * Available options:
072 *
073 * * debug: When set to true, it automatically set "auto_reload" to true as
074 * well (default to false).
075 *
076 * * charset: The charset used by the templates (default to UTF-8).
077 *
078 * * base_template_class: The base template class to use for generated
079 * templates (default to \Twig\Template).
080 *
081 * * cache: An absolute path where to store the compiled templates,
082 * a \Twig\Cache\CacheInterface implementation,
083 * or false to disable compilation cache (default).
084 *
085 * * auto_reload: Whether to reload the template if the original source changed.
086 * If you don't provide the auto_reload option, it will be
087 * determined automatically based on the debug value.
088 *
089 * * strict_variables: Whether to ignore invalid variables in templates
090 * (default to false).
091 *
092 * * autoescape: Whether to enable auto-escaping (default to html):
093 * * false: disable auto-escaping
094 * * html, js: set the autoescaping to one of the supported strategies
095 * * name: set the autoescaping strategy based on the template name extension
096 * * PHP callback: a PHP callback that returns an escaping strategy based on the template "name"
097 *
098 * * optimizations: A flag that indicates which optimizations to apply
099 * (default to -1 which means that all optimizations are enabled;
100 * set it to 0 to disable).
101 */
102 public function __construct(LoaderInterface $loader, $options = [])
103 {
104 $this->setLoader($loader);
105
106 $options = array_merge([
107 'debug' => false,
108 'charset' => 'UTF-8',
109 'base_template_class' => Template::class,
110 'strict_variables' => false,
111 'autoescape' => 'html',
112 'cache' => false,
113 'auto_reload' => null,
114 'optimizations' => -1,
115 ], $options);
116
117 $this->debug = (bool) $options['debug'];
118 $this->setCharset($options['charset']);
119 $this->baseTemplateClass = '\\'.ltrim($options['base_template_class'], '\\');
120 if ('\\'.Template::class !== $this->baseTemplateClass && '\Twig_Template' !== $this->baseTemplateClass) {
121 @trigger_error('The "base_template_class" option on '.__CLASS__.' is deprecated since Twig 2.7.0.', \E_USER_DEPRECATED);
122 }
123 $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
124 $this->strictVariables = (bool) $options['strict_variables'];
125 $this->setCache($options['cache']);
126 $this->extensionSet = new ExtensionSet();
127
128 $this->addExtension(new CoreExtension());
129 $this->addExtension(new EscaperExtension($options['autoescape']));
130 $this->addExtension(new OptimizerExtension($options['optimizations']));
131 }
132
133 /**
134 * Gets the base template class for compiled templates.
135 *
136 * @return string The base template class name
137 */
138 public function getBaseTemplateClass()
139 {
140 if (1 > \func_num_args() || \func_get_arg(0)) {
141 @trigger_error('The '.__METHOD__.' is deprecated since Twig 2.7.0.', \E_USER_DEPRECATED);
142 }
143
144 return $this->baseTemplateClass;
145 }
146
147 /**
148 * Sets the base template class for compiled templates.
149 *
150 * @param string $class The base template class name
151 */
152 public function setBaseTemplateClass($class)
153 {
154 @trigger_error('The '.__METHOD__.' is deprecated since Twig 2.7.0.', \E_USER_DEPRECATED);
155
156 $this->baseTemplateClass = $class;
157 $this->updateOptionsHash();
158 }
159
160 /**
161 * Enables debugging mode.
162 */
163 public function enableDebug()
164 {
165 $this->debug = true;
166 $this->updateOptionsHash();
167 }
168
169 /**
170 * Disables debugging mode.
171 */
172 public function disableDebug()
173 {
174 $this->debug = false;
175 $this->updateOptionsHash();
176 }
177
178 /**
179 * Checks if debug mode is enabled.
180 *
181 * @return bool true if debug mode is enabled, false otherwise
182 */
183 public function isDebug()
184 {
185 return $this->debug;
186 }
187
188 /**
189 * Enables the auto_reload option.
190 */
191 public function enableAutoReload()
192 {
193 $this->autoReload = true;
194 }
195
196 /**
197 * Disables the auto_reload option.
198 */
199 public function disableAutoReload()
200 {
201 $this->autoReload = false;
202 }
203
204 /**
205 * Checks if the auto_reload option is enabled.
206 *
207 * @return bool true if auto_reload is enabled, false otherwise
208 */
209 public function isAutoReload()
210 {
211 return $this->autoReload;
212 }
213
214 /**
215 * Enables the strict_variables option.
216 */
217 public function enableStrictVariables()
218 {
219 $this->strictVariables = true;
220 $this->updateOptionsHash();
221 }
222
223 /**
224 * Disables the strict_variables option.
225 */
226 public function disableStrictVariables()
227 {
228 $this->strictVariables = false;
229 $this->updateOptionsHash();
230 }
231
232 /**
233 * Checks if the strict_variables option is enabled.
234 *
235 * @return bool true if strict_variables is enabled, false otherwise
236 */
237 public function isStrictVariables()
238 {
239 return $this->strictVariables;
240 }
241
242 /**
243 * Gets the current cache implementation.
244 *
245 * @param bool $original Whether to return the original cache option or the real cache instance
246 *
247 * @return CacheInterface|string|false A Twig\Cache\CacheInterface implementation,
248 * an absolute path to the compiled templates,
249 * or false to disable cache
250 */
251 public function getCache($original = true)
252 {
253 return $original ? $this->originalCache : $this->cache;
254 }
255
256 /**
257 * Sets the current cache implementation.
258 *
259 * @param CacheInterface|string|false $cache A Twig\Cache\CacheInterface implementation,
260 * an absolute path to the compiled templates,
261 * or false to disable cache
262 */
263 public function setCache($cache)
264 {
265 if (\is_string($cache)) {
266 $this->originalCache = $cache;
267 $this->cache = new FilesystemCache($cache, $this->autoReload ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0);
268 } elseif (false === $cache) {
269 $this->originalCache = $cache;
270 $this->cache = new NullCache();
271 } elseif ($cache instanceof CacheInterface) {
272 $this->originalCache = $this->cache = $cache;
273 } else {
274 throw new \LogicException('Cache can only be a string, false, or a \Twig\Cache\CacheInterface implementation.');
275 }
276 }
277
278 /**
279 * Gets the template class associated with the given string.
280 *
281 * The generated template class is based on the following parameters:
282 *
283 * * The cache key for the given template;
284 * * The currently enabled extensions;
285 * * Whether the Twig C extension is available or not;
286 * * PHP version;
287 * * Twig version;
288 * * Options with what environment was created.
289 *
290 * @param string $name The name for which to calculate the template class name
291 * @param int|null $index The index if it is an embedded template
292 *
293 * @return string The template class name
294 *
295 * @internal
296 */
297 public function getTemplateClass($name, $index = null)
298 {
299 $key = $this->getLoader()->getCacheKey($name).$this->optionsHash;
300
301 return $this->templateClassPrefix.hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $key).(null === $index ? '' : '___'.$index);
302 }
303
304 /**
305 * Renders a template.
306 *
307 * @param string|TemplateWrapper $name The template name
308 * @param array $context An array of parameters to pass to the template
309 *
310 * @return string The rendered template
311 *
312 * @throws LoaderError When the template cannot be found
313 * @throws SyntaxError When an error occurred during compilation
314 * @throws RuntimeError When an error occurred during rendering
315 */
316 public function render($name, array $context = [])
317 {
318 return $this->load($name)->render($context);
319 }
320
321 /**
322 * Displays a template.
323 *
324 * @param string|TemplateWrapper $name The template name
325 * @param array $context An array of parameters to pass to the template
326 *
327 * @throws LoaderError When the template cannot be found
328 * @throws SyntaxError When an error occurred during compilation
329 * @throws RuntimeError When an error occurred during rendering
330 */
331 public function display($name, array $context = [])
332 {
333 $this->load($name)->display($context);
334 }
335
336 /**
337 * Loads a template.
338 *
339 * @param string|TemplateWrapper $name The template name
340 *
341 * @throws LoaderError When the template cannot be found
342 * @throws RuntimeError When a previously generated cache is corrupted
343 * @throws SyntaxError When an error occurred during compilation
344 *
345 * @return TemplateWrapper
346 */
347 public function load($name)
348 {
349 if ($name instanceof TemplateWrapper) {
350 return $name;
351 }
352
353 if ($name instanceof Template) {
354 @trigger_error('Passing a \Twig\Template instance to '.__METHOD__.' is deprecated since Twig 2.7.0, use \Twig\TemplateWrapper instead.', \E_USER_DEPRECATED);
355
356 return new TemplateWrapper($this, $name);
357 }
358
359 return new TemplateWrapper($this, $this->loadTemplate($name));
360 }
361
362 /**
363 * Loads a template internal representation.
364 *
365 * This method is for internal use only and should never be called
366 * directly.
367 *
368 * @param string $name The template name
369 * @param int $index The index if it is an embedded template
370 *
371 * @return Template A template instance representing the given template name
372 *
373 * @throws LoaderError When the template cannot be found
374 * @throws RuntimeError When a previously generated cache is corrupted
375 * @throws SyntaxError When an error occurred during compilation
376 *
377 * @internal
378 */
379 public function loadTemplate($name, $index = null)
380 {
381 return $this->loadClass($this->getTemplateClass($name), $name, $index);
382 }
383
384 /**
385 * @internal
386 */
387 public function loadClass($cls, $name, $index = null)
388 {
389 $mainCls = $cls;
390 if (null !== $index) {
391 $cls .= '___'.$index;
392 }
393
394 if (isset($this->loadedTemplates[$cls])) {
395 return $this->loadedTemplates[$cls];
396 }
397
398 if (!class_exists($cls, false)) {
399 $key = $this->cache->generateKey($name, $mainCls);
400
401 if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) {
402 $this->cache->load($key);
403 }
404
405 $source = null;
406 if (!class_exists($cls, false)) {
407 $source = $this->getLoader()->getSourceContext($name);
408 $content = $this->compileSource($source);
409 $this->cache->write($key, $content);
410 $this->cache->load($key);
411
412 if (!class_exists($mainCls, false)) {
413 /* Last line of defense if either $this->bcWriteCacheFile was used,
414 * $this->cache is implemented as a no-op or we have a race condition
415 * where the cache was cleared between the above calls to write to and load from
416 * the cache.
417 */
418 eval('?>'.$content);
419 }
420
421 if (!class_exists($cls, false)) {
422 throw new RuntimeError(sprintf('Failed to load Twig template "%s", index "%s": cache might be corrupted.', $name, $index), -1, $source);
423 }
424 }
425 }
426
427 // to be removed in 3.0
428 $this->extensionSet->initRuntime($this);
429
430 return $this->loadedTemplates[$cls] = new $cls($this);
431 }
432
433 /**
434 * Creates a template from source.
435 *
436 * This method should not be used as a generic way to load templates.
437 *
438 * @param string $template The template source
439 * @param string $name An optional name of the template to be used in error messages
440 *
441 * @return TemplateWrapper A template instance representing the given template name
442 *
443 * @throws LoaderError When the template cannot be found
444 * @throws SyntaxError When an error occurred during compilation
445 */
446 public function createTemplate($template, string $name = null)
447 {
448 $hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $template, false);
449 if (null !== $name) {
450 $name = sprintf('%s (string template %s)', $name, $hash);
451 } else {
452 $name = sprintf('__string_template__%s', $hash);
453 }
454
455 $loader = new ChainLoader([
456 new ArrayLoader([$name => $template]),
457 $current = $this->getLoader(),
458 ]);
459
460 $this->setLoader($loader);
461 try {
462 return new TemplateWrapper($this, $this->loadTemplate($name));
463 } finally {
464 $this->setLoader($current);
465 }
466 }
467
468 /**
469 * Returns true if the template is still fresh.
470 *
471 * Besides checking the loader for freshness information,
472 * this method also checks if the enabled extensions have
473 * not changed.
474 *
475 * @param string $name The template name
476 * @param int $time The last modification time of the cached template
477 *
478 * @return bool true if the template is fresh, false otherwise
479 */
480 public function isTemplateFresh($name, $time)
481 {
482 return $this->extensionSet->getLastModified() <= $time && $this->getLoader()->isFresh($name, $time);
483 }
484
485 /**
486 * Tries to load a template consecutively from an array.
487 *
488 * Similar to load() but it also accepts instances of \Twig\Template and
489 * \Twig\TemplateWrapper, and an array of templates where each is tried to be loaded.
490 *
491 * @param string|TemplateWrapper|array $names A template or an array of templates to try consecutively
492 *
493 * @return TemplateWrapper|Template
494 *
495 * @throws LoaderError When none of the templates can be found
496 * @throws SyntaxError When an error occurred during compilation
497 */
498 public function resolveTemplate($names)
499 {
500 if (!\is_array($names)) {
501 $names = [$names];
502 }
503
504 $count = \count($names);
505 foreach ($names as $name) {
506 if ($name instanceof Template) {
507 return $name;
508 }
509 if ($name instanceof TemplateWrapper) {
510 return $name;
511 }
512
513 if (1 !== $count && !$this->getLoader()->exists($name)) {
514 continue;
515 }
516
517 return $this->loadTemplate($name);
518 }
519
520 throw new LoaderError(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
521 }
522
523 public function setLexer(Lexer $lexer)
524 {
525 $this->lexer = $lexer;
526 }
527
528 /**
529 * Tokenizes a source code.
530 *
531 * @return TokenStream
532 *
533 * @throws SyntaxError When the code is syntactically wrong
534 */
535 public function tokenize(Source $source)
536 {
537 if (null === $this->lexer) {
538 $this->lexer = new Lexer($this);
539 }
540
541 return $this->lexer->tokenize($source);
542 }
543
544 public function setParser(Parser $parser)
545 {
546 $this->parser = $parser;
547 }
548
549 /**
550 * Converts a token stream to a node tree.
551 *
552 * @return ModuleNode
553 *
554 * @throws SyntaxError When the token stream is syntactically or semantically wrong
555 */
556 public function parse(TokenStream $stream)
557 {
558 if (null === $this->parser) {
559 $this->parser = new Parser($this);
560 }
561
562 return $this->parser->parse($stream);
563 }
564
565 public function setCompiler(Compiler $compiler)
566 {
567 $this->compiler = $compiler;
568 }
569
570 /**
571 * Compiles a node and returns the PHP code.
572 *
573 * @return string The compiled PHP source code
574 */
575 public function compile(Node $node)
576 {
577 if (null === $this->compiler) {
578 $this->compiler = new Compiler($this);
579 }
580
581 return $this->compiler->compile($node)->getSource();
582 }
583
584 /**
585 * Compiles a template source code.
586 *
587 * @return string The compiled PHP source code
588 *
589 * @throws SyntaxError When there was an error during tokenizing, parsing or compiling
590 */
591 public function compileSource(Source $source)
592 {
593 try {
594 return $this->compile($this->parse($this->tokenize($source)));
595 } catch (Error $e) {
596 $e->setSourceContext($source);
597 throw $e;
598 } catch (\Exception $e) {
599 throw new SyntaxError(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e);
600 }
601 }
602
603 public function setLoader(LoaderInterface $loader)
604 {
605 $this->loader = $loader;
606 }
607
608 /**
609 * Gets the Loader instance.
610 *
611 * @return LoaderInterface
612 */
613 public function getLoader()
614 {
615 return $this->loader;
616 }
617
618 /**
619 * Sets the default template charset.
620 *
621 * @param string $charset The default charset
622 */
623 public function setCharset($charset)
624 {
625 if ('UTF8' === $charset = null === $charset ? null : strtoupper($charset)) {
626 // iconv on Windows requires "UTF-8" instead of "UTF8"
627 $charset = 'UTF-8';
628 }
629
630 $this->charset = $charset;
631 }
632
633 /**
634 * Gets the default template charset.
635 *
636 * @return string The default charset
637 */
638 public function getCharset()
639 {
640 return $this->charset;
641 }
642
643 /**
644 * Returns true if the given extension is registered.
645 *
646 * @param string $class The extension class name
647 *
648 * @return bool Whether the extension is registered or not
649 */
650 public function hasExtension($class)
651 {
652 return $this->extensionSet->hasExtension($class);
653 }
654
655 /**
656 * Adds a runtime loader.
657 */
658 public function addRuntimeLoader(RuntimeLoaderInterface $loader)
659 {
660 $this->runtimeLoaders[] = $loader;
661 }
662
663 /**
664 * Gets an extension by class name.
665 *
666 * @param string $class The extension class name
667 *
668 * @return ExtensionInterface
669 */
670 public function getExtension($class)
671 {
672 return $this->extensionSet->getExtension($class);
673 }
674
675 /**
676 * Returns the runtime implementation of a Twig element (filter/function/test).
677 *
678 * @param string $class A runtime class name
679 *
680 * @return object The runtime implementation
681 *
682 * @throws RuntimeError When the template cannot be found
683 */
684 public function getRuntime($class)
685 {
686 if (isset($this->runtimes[$class])) {
687 return $this->runtimes[$class];
688 }
689
690 foreach ($this->runtimeLoaders as $loader) {
691 if (null !== $runtime = $loader->load($class)) {
692 return $this->runtimes[$class] = $runtime;
693 }
694 }
695
696 throw new RuntimeError(sprintf('Unable to load the "%s" runtime.', $class));
697 }
698
699 public function addExtension(ExtensionInterface $extension)
700 {
701 $this->extensionSet->addExtension($extension);
702 $this->updateOptionsHash();
703 }
704
705 /**
706 * Registers an array of extensions.
707 *
708 * @param array $extensions An array of extensions
709 */
710 public function setExtensions(array $extensions)
711 {
712 $this->extensionSet->setExtensions($extensions);
713 $this->updateOptionsHash();
714 }
715
716 /**
717 * Returns all registered extensions.
718 *
719 * @return ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on)
720 */
721 public function getExtensions()
722 {
723 return $this->extensionSet->getExtensions();
724 }
725
726 public function addTokenParser(TokenParserInterface $parser)
727 {
728 $this->extensionSet->addTokenParser($parser);
729 }
730
731 /**
732 * Gets the registered Token Parsers.
733 *
734 * @return TokenParserInterface[]
735 *
736 * @internal
737 */
738 public function getTokenParsers()
739 {
740 return $this->extensionSet->getTokenParsers();
741 }
742
743 /**
744 * Gets registered tags.
745 *
746 * @return TokenParserInterface[]
747 *
748 * @internal
749 */
750 public function getTags()
751 {
752 $tags = [];
753 foreach ($this->getTokenParsers() as $parser) {
754 $tags[$parser->getTag()] = $parser;
755 }
756
757 return $tags;
758 }
759
760 public function addNodeVisitor(NodeVisitorInterface $visitor)
761 {
762 $this->extensionSet->addNodeVisitor($visitor);
763 }
764
765 /**
766 * Gets the registered Node Visitors.
767 *
768 * @return NodeVisitorInterface[]
769 *
770 * @internal
771 */
772 public function getNodeVisitors()
773 {
774 return $this->extensionSet->getNodeVisitors();
775 }
776
777 public function addFilter(TwigFilter $filter)
778 {
779 $this->extensionSet->addFilter($filter);
780 }
781
782 /**
783 * Get a filter by name.
784 *
785 * Subclasses may override this method and load filters differently;
786 * so no list of filters is available.
787 *
788 * @param string $name The filter name
789 *
790 * @return TwigFilter|false
791 *
792 * @internal
793 */
794 public function getFilter($name)
795 {
796 return $this->extensionSet->getFilter($name);
797 }
798
799 public function registerUndefinedFilterCallback(callable $callable)
800 {
801 $this->extensionSet->registerUndefinedFilterCallback($callable);
802 }
803
804 /**
805 * Gets the registered Filters.
806 *
807 * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
808 *
809 * @return TwigFilter[]
810 *
811 * @see registerUndefinedFilterCallback
812 *
813 * @internal
814 */
815 public function getFilters()
816 {
817 return $this->extensionSet->getFilters();
818 }
819
820 public function addTest(TwigTest $test)
821 {
822 $this->extensionSet->addTest($test);
823 }
824
825 /**
826 * Gets the registered Tests.
827 *
828 * @return TwigTest[]
829 *
830 * @internal
831 */
832 public function getTests()
833 {
834 return $this->extensionSet->getTests();
835 }
836
837 /**
838 * Gets a test by name.
839 *
840 * @param string $name The test name
841 *
842 * @return TwigTest|false
843 *
844 * @internal
845 */
846 public function getTest($name)
847 {
848 return $this->extensionSet->getTest($name);
849 }
850
851 public function addFunction(TwigFunction $function)
852 {
853 $this->extensionSet->addFunction($function);
854 }
855
856 /**
857 * Get a function by name.
858 *
859 * Subclasses may override this method and load functions differently;
860 * so no list of functions is available.
861 *
862 * @param string $name function name
863 *
864 * @return TwigFunction|false
865 *
866 * @internal
867 */
868 public function getFunction($name)
869 {
870 return $this->extensionSet->getFunction($name);
871 }
872
873 public function registerUndefinedFunctionCallback(callable $callable)
874 {
875 $this->extensionSet->registerUndefinedFunctionCallback($callable);
876 }
877
878 /**
879 * Gets registered functions.
880 *
881 * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
882 *
883 * @return TwigFunction[]
884 *
885 * @see registerUndefinedFunctionCallback
886 *
887 * @internal
888 */
889 public function getFunctions()
890 {
891 return $this->extensionSet->getFunctions();
892 }
893
894 /**
895 * Registers a Global.
896 *
897 * New globals can be added before compiling or rendering a template;
898 * but after, you can only update existing globals.
899 *
900 * @param string $name The global name
901 * @param mixed $value The global value
902 */
903 public function addGlobal($name, $value)
904 {
905 if ($this->extensionSet->isInitialized() && !\array_key_exists($name, $this->getGlobals())) {
906 throw new \LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
907 }
908
909 if (null !== $this->resolvedGlobals) {
910 $this->resolvedGlobals[$name] = $value;
911 } else {
912 $this->globals[$name] = $value;
913 }
914 }
915
916 /**
917 * Gets the registered Globals.
918 *
919 * @return array An array of globals
920 *
921 * @internal
922 */
923 public function getGlobals()
924 {
925 if ($this->extensionSet->isInitialized()) {
926 if (null === $this->resolvedGlobals) {
927 $this->resolvedGlobals = array_merge($this->extensionSet->getGlobals(), $this->globals);
928 }
929
930 return $this->resolvedGlobals;
931 }
932
933 return array_merge($this->extensionSet->getGlobals(), $this->globals);
934 }
935
936 /**
937 * Merges a context with the defined globals.
938 *
939 * @param array $context An array representing the context
940 *
941 * @return array The context merged with the globals
942 */
943 public function mergeGlobals(array $context)
944 {
945 // we don't use array_merge as the context being generally
946 // bigger than globals, this code is faster.
947 foreach ($this->getGlobals() as $key => $value) {
948 if (!\array_key_exists($key, $context)) {
949 $context[$key] = $value;
950 }
951 }
952
953 return $context;
954 }
955
956 /**
957 * Gets the registered unary Operators.
958 *
959 * @return array An array of unary operators
960 *
961 * @internal
962 */
963 public function getUnaryOperators()
964 {
965 return $this->extensionSet->getUnaryOperators();
966 }
967
968 /**
969 * Gets the registered binary Operators.
970 *
971 * @return array An array of binary operators
972 *
973 * @internal
974 */
975 public function getBinaryOperators()
976 {
977 return $this->extensionSet->getBinaryOperators();
978 }
979
980 private function updateOptionsHash()
981 {
982 $this->optionsHash = implode(':', [
983 $this->extensionSet->getSignature(),
984 \PHP_MAJOR_VERSION,
985 \PHP_MINOR_VERSION,
986 self::VERSION,
987 (int) $this->debug,
988 $this->baseTemplateClass,
989 (int) $this->strictVariables,
990 ]);
991 }
992 }
993
994 class_alias('Twig\Environment', 'Twig_Environment');
995