vendor/twig/twig/src/Environment.php line 359

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