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

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