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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Environment.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 28.06 KiB


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