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

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\GlobalsInterface;
  22. use Twig\Extension\InitRuntimeInterface;
  23. use Twig\Extension\OptimizerExtension;
  24. use Twig\Extension\StagingExtension;
  25. use Twig\Loader\ArrayLoader;
  26. use Twig\Loader\ChainLoader;
  27. use Twig\Loader\LoaderInterface;
  28. use Twig\Loader\SourceContextLoaderInterface;
  29. use Twig\Node\ModuleNode;
  30. use Twig\NodeVisitor\NodeVisitorInterface;
  31. use Twig\RuntimeLoader\RuntimeLoaderInterface;
  32. use Twig\TokenParser\TokenParserInterface;
  33. /**
  34.  * Stores the Twig configuration.
  35.  *
  36.  * @author Fabien Potencier <fabien@symfony.com>
  37.  */
  38. class Environment
  39. {
  40.     const VERSION '1.42.2';
  41.     const VERSION_ID 14202;
  42.     const MAJOR_VERSION 1;
  43.     const MINOR_VERSION 42;
  44.     const RELEASE_VERSION 2;
  45.     const EXTRA_VERSION '';
  46.     protected $charset;
  47.     protected $loader;
  48.     protected $debug;
  49.     protected $autoReload;
  50.     protected $cache;
  51.     protected $lexer;
  52.     protected $parser;
  53.     protected $compiler;
  54.     protected $baseTemplateClass;
  55.     protected $extensions;
  56.     protected $parsers;
  57.     protected $visitors;
  58.     protected $filters;
  59.     protected $tests;
  60.     protected $functions;
  61.     protected $globals;
  62.     protected $runtimeInitialized false;
  63.     protected $extensionInitialized false;
  64.     protected $loadedTemplates;
  65.     protected $strictVariables;
  66.     protected $unaryOperators;
  67.     protected $binaryOperators;
  68.     protected $templateClassPrefix '__TwigTemplate_';
  69.     protected $functionCallbacks = [];
  70.     protected $filterCallbacks = [];
  71.     protected $staging;
  72.     private $originalCache;
  73.     private $bcWriteCacheFile false;
  74.     private $bcGetCacheFilename false;
  75.     private $lastModifiedExtension 0;
  76.     private $extensionsByClass = [];
  77.     private $runtimeLoaders = [];
  78.     private $runtimes = [];
  79.     private $optionsHash;
  80.     /**
  81.      * Constructor.
  82.      *
  83.      * Available options:
  84.      *
  85.      *  * debug: When set to true, it automatically set "auto_reload" to true as
  86.      *           well (default to false).
  87.      *
  88.      *  * charset: The charset used by the templates (default to UTF-8).
  89.      *
  90.      *  * base_template_class: The base template class to use for generated
  91.      *                         templates (default to \Twig\Template).
  92.      *
  93.      *  * cache: An absolute path where to store the compiled templates,
  94.      *           a \Twig\Cache\CacheInterface implementation,
  95.      *           or false to disable compilation cache (default).
  96.      *
  97.      *  * auto_reload: Whether to reload the template if the original source changed.
  98.      *                 If you don't provide the auto_reload option, it will be
  99.      *                 determined automatically based on the debug value.
  100.      *
  101.      *  * strict_variables: Whether to ignore invalid variables in templates
  102.      *                      (default to false).
  103.      *
  104.      *  * autoescape: Whether to enable auto-escaping (default to html):
  105.      *                  * false: disable auto-escaping
  106.      *                  * true: equivalent to html
  107.      *                  * html, js: set the autoescaping to one of the supported strategies
  108.      *                  * name: set the autoescaping strategy based on the template name extension
  109.      *                  * PHP callback: a PHP callback that returns an escaping strategy based on the template "name"
  110.      *
  111.      *  * optimizations: A flag that indicates which optimizations to apply
  112.      *                   (default to -1 which means that all optimizations are enabled;
  113.      *                   set it to 0 to disable).
  114.      */
  115.     public function __construct(LoaderInterface $loader null$options = [])
  116.     {
  117.         if (null !== $loader) {
  118.             $this->setLoader($loader);
  119.         } else {
  120.             @trigger_error('Not passing a "Twig\Lodaer\LoaderInterface" as the first constructor argument of "Twig\Environment" is deprecated since version 1.21.'E_USER_DEPRECATED);
  121.         }
  122.         $options array_merge([
  123.             'debug' => false,
  124.             'charset' => 'UTF-8',
  125.             'base_template_class' => '\Twig\Template',
  126.             'strict_variables' => false,
  127.             'autoescape' => 'html',
  128.             'cache' => false,
  129.             'auto_reload' => null,
  130.             'optimizations' => -1,
  131.         ], $options);
  132.         $this->debug = (bool) $options['debug'];
  133.         $this->charset strtoupper($options['charset']);
  134.         $this->baseTemplateClass $options['base_template_class'];
  135.         $this->autoReload null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
  136.         $this->strictVariables = (bool) $options['strict_variables'];
  137.         $this->setCache($options['cache']);
  138.         $this->addExtension(new CoreExtension());
  139.         $this->addExtension(new EscaperExtension($options['autoescape']));
  140.         $this->addExtension(new OptimizerExtension($options['optimizations']));
  141.         $this->staging = new StagingExtension();
  142.         // For BC
  143.         if (\is_string($this->originalCache)) {
  144.             $r = new \ReflectionMethod($this'writeCacheFile');
  145.             if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
  146.                 @trigger_error('The Twig\Environment::writeCacheFile method is deprecated since version 1.22 and will be removed in Twig 2.0.'E_USER_DEPRECATED);
  147.                 $this->bcWriteCacheFile true;
  148.             }
  149.             $r = new \ReflectionMethod($this'getCacheFilename');
  150.             if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
  151.                 @trigger_error('The Twig\Environment::getCacheFilename method is deprecated since version 1.22 and will be removed in Twig 2.0.'E_USER_DEPRECATED);
  152.                 $this->bcGetCacheFilename true;
  153.             }
  154.         }
  155.     }
  156.     /**
  157.      * Gets the base template class for compiled templates.
  158.      *
  159.      * @return string The base template class name
  160.      */
  161.     public function getBaseTemplateClass()
  162.     {
  163.         return $this->baseTemplateClass;
  164.     }
  165.     /**
  166.      * Sets the base template class for compiled templates.
  167.      *
  168.      * @param string $class The base template class name
  169.      */
  170.     public function setBaseTemplateClass($class)
  171.     {
  172.         $this->baseTemplateClass $class;
  173.         $this->updateOptionsHash();
  174.     }
  175.     /**
  176.      * Enables debugging mode.
  177.      */
  178.     public function enableDebug()
  179.     {
  180.         $this->debug true;
  181.         $this->updateOptionsHash();
  182.     }
  183.     /**
  184.      * Disables debugging mode.
  185.      */
  186.     public function disableDebug()
  187.     {
  188.         $this->debug false;
  189.         $this->updateOptionsHash();
  190.     }
  191.     /**
  192.      * Checks if debug mode is enabled.
  193.      *
  194.      * @return bool true if debug mode is enabled, false otherwise
  195.      */
  196.     public function isDebug()
  197.     {
  198.         return $this->debug;
  199.     }
  200.     /**
  201.      * Enables the auto_reload option.
  202.      */
  203.     public function enableAutoReload()
  204.     {
  205.         $this->autoReload true;
  206.     }
  207.     /**
  208.      * Disables the auto_reload option.
  209.      */
  210.     public function disableAutoReload()
  211.     {
  212.         $this->autoReload false;
  213.     }
  214.     /**
  215.      * Checks if the auto_reload option is enabled.
  216.      *
  217.      * @return bool true if auto_reload is enabled, false otherwise
  218.      */
  219.     public function isAutoReload()
  220.     {
  221.         return $this->autoReload;
  222.     }
  223.     /**
  224.      * Enables the strict_variables option.
  225.      */
  226.     public function enableStrictVariables()
  227.     {
  228.         $this->strictVariables true;
  229.         $this->updateOptionsHash();
  230.     }
  231.     /**
  232.      * Disables the strict_variables option.
  233.      */
  234.     public function disableStrictVariables()
  235.     {
  236.         $this->strictVariables false;
  237.         $this->updateOptionsHash();
  238.     }
  239.     /**
  240.      * Checks if the strict_variables option is enabled.
  241.      *
  242.      * @return bool true if strict_variables is enabled, false otherwise
  243.      */
  244.     public function isStrictVariables()
  245.     {
  246.         return $this->strictVariables;
  247.     }
  248.     /**
  249.      * Gets the current cache implementation.
  250.      *
  251.      * @param bool $original Whether to return the original cache option or the real cache instance
  252.      *
  253.      * @return CacheInterface|string|false A Twig\Cache\CacheInterface implementation,
  254.      *                                     an absolute path to the compiled templates,
  255.      *                                     or false to disable cache
  256.      */
  257.     public function getCache($original true)
  258.     {
  259.         return $original $this->originalCache $this->cache;
  260.     }
  261.     /**
  262.      * Sets the current cache implementation.
  263.      *
  264.      * @param CacheInterface|string|false $cache A Twig\Cache\CacheInterface implementation,
  265.      *                                           an absolute path to the compiled templates,
  266.      *                                           or false to disable cache
  267.      */
  268.     public function setCache($cache)
  269.     {
  270.         if (\is_string($cache)) {
  271.             $this->originalCache $cache;
  272.             $this->cache = new FilesystemCache($cache);
  273.         } elseif (false === $cache) {
  274.             $this->originalCache $cache;
  275.             $this->cache = new NullCache();
  276.         } elseif (null === $cache) {
  277.             @trigger_error('Using "null" as the cache strategy is deprecated since version 1.23 and will be removed in Twig 2.0.'E_USER_DEPRECATED);
  278.             $this->originalCache false;
  279.             $this->cache = new NullCache();
  280.         } elseif ($cache instanceof CacheInterface) {
  281.             $this->originalCache $this->cache $cache;
  282.         } else {
  283.             throw new \LogicException(sprintf('Cache can only be a string, false, or a \Twig\Cache\CacheInterface implementation.'));
  284.         }
  285.     }
  286.     /**
  287.      * Gets the cache filename for a given template.
  288.      *
  289.      * @param string $name The template name
  290.      *
  291.      * @return string|false The cache file name or false when caching is disabled
  292.      *
  293.      * @deprecated since 1.22 (to be removed in 2.0)
  294.      */
  295.     public function getCacheFilename($name)
  296.     {
  297.         @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.'__METHOD__), E_USER_DEPRECATED);
  298.         $key $this->cache->generateKey($name$this->getTemplateClass($name));
  299.         return !$key false $key;
  300.     }
  301.     /**
  302.      * Gets the template class associated with the given string.
  303.      *
  304.      * The generated template class is based on the following parameters:
  305.      *
  306.      *  * The cache key for the given template;
  307.      *  * The currently enabled extensions;
  308.      *  * Whether the Twig C extension is available or not;
  309.      *  * PHP version;
  310.      *  * Twig version;
  311.      *  * Options with what environment was created.
  312.      *
  313.      * @param string   $name  The name for which to calculate the template class name
  314.      * @param int|null $index The index if it is an embedded template
  315.      *
  316.      * @return string The template class name
  317.      */
  318.     public function getTemplateClass($name$index null)
  319.     {
  320.         $key $this->getLoader()->getCacheKey($name).$this->optionsHash;
  321.         return $this->templateClassPrefix.hash('sha256'$key).(null === $index '' '___'.$index);
  322.     }
  323.     /**
  324.      * Gets the template class prefix.
  325.      *
  326.      * @return string The template class prefix
  327.      *
  328.      * @deprecated since 1.22 (to be removed in 2.0)
  329.      */
  330.     public function getTemplateClassPrefix()
  331.     {
  332.         @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.'__METHOD__), E_USER_DEPRECATED);
  333.         return $this->templateClassPrefix;
  334.     }
  335.     /**
  336.      * Renders a template.
  337.      *
  338.      * @param string|TemplateWrapper $name    The template name
  339.      * @param array                  $context An array of parameters to pass to the template
  340.      *
  341.      * @return string The rendered template
  342.      *
  343.      * @throws LoaderError  When the template cannot be found
  344.      * @throws SyntaxError  When an error occurred during compilation
  345.      * @throws RuntimeError When an error occurred during rendering
  346.      */
  347.     public function render($name, array $context = [])
  348.     {
  349.         return $this->load($name)->render($context);
  350.     }
  351.     /**
  352.      * Displays a template.
  353.      *
  354.      * @param string|TemplateWrapper $name    The template name
  355.      * @param array                  $context An array of parameters to pass to the template
  356.      *
  357.      * @throws LoaderError  When the template cannot be found
  358.      * @throws SyntaxError  When an error occurred during compilation
  359.      * @throws RuntimeError When an error occurred during rendering
  360.      */
  361.     public function display($name, array $context = [])
  362.     {
  363.         $this->load($name)->display($context);
  364.     }
  365.     /**
  366.      * Loads a template.
  367.      *
  368.      * @param string|TemplateWrapper|\Twig\Template $name The template name
  369.      *
  370.      * @throws LoaderError  When the template cannot be found
  371.      * @throws RuntimeError When a previously generated cache is corrupted
  372.      * @throws SyntaxError  When an error occurred during compilation
  373.      *
  374.      * @return TemplateWrapper
  375.      */
  376.     public function load($name)
  377.     {
  378.         if ($name instanceof TemplateWrapper) {
  379.             return $name;
  380.         }
  381.         if ($name instanceof Template) {
  382.             return new TemplateWrapper($this$name);
  383.         }
  384.         return new TemplateWrapper($this$this->loadTemplate($name));
  385.     }
  386.     /**
  387.      * Loads a template internal representation.
  388.      *
  389.      * This method is for internal use only and should never be called
  390.      * directly.
  391.      *
  392.      * @param string $name  The template name
  393.      * @param int    $index The index if it is an embedded template
  394.      *
  395.      * @return \Twig_TemplateInterface A template instance representing the given template name
  396.      *
  397.      * @throws LoaderError  When the template cannot be found
  398.      * @throws RuntimeError When a previously generated cache is corrupted
  399.      * @throws SyntaxError  When an error occurred during compilation
  400.      *
  401.      * @internal
  402.      */
  403.     public function loadTemplate($name$index null)
  404.     {
  405.         return $this->loadClass($this->getTemplateClass($name), $name$index);
  406.     }
  407.     /**
  408.      * @internal
  409.      */
  410.     public function loadClass($cls$name$index null)
  411.     {
  412.         $mainCls $cls;
  413.         if (null !== $index) {
  414.             $cls .= '___'.$index;
  415.         }
  416.         if (isset($this->loadedTemplates[$cls])) {
  417.             return $this->loadedTemplates[$cls];
  418.         }
  419.         if (!class_exists($clsfalse)) {
  420.             if ($this->bcGetCacheFilename) {
  421.                 $key $this->getCacheFilename($name);
  422.             } else {
  423.                 $key $this->cache->generateKey($name$mainCls);
  424.             }
  425.             if (!$this->isAutoReload() || $this->isTemplateFresh($name$this->cache->getTimestamp($key))) {
  426.                 $this->cache->load($key);
  427.             }
  428.             $source null;
  429.             if (!class_exists($clsfalse)) {
  430.                 $loader $this->getLoader();
  431.                 if (!$loader instanceof SourceContextLoaderInterface) {
  432.                     $source = new Source($loader->getSource($name), $name);
  433.                 } else {
  434.                     $source $loader->getSourceContext($name);
  435.                 }
  436.                 $content $this->compileSource($source);
  437.                 if ($this->bcWriteCacheFile) {
  438.                     $this->writeCacheFile($key$content);
  439.                 } else {
  440.                     $this->cache->write($key$content);
  441.                     $this->cache->load($key);
  442.                 }
  443.                 if (!class_exists($mainClsfalse)) {
  444.                     /* Last line of defense if either $this->bcWriteCacheFile was used,
  445.                      * $this->cache is implemented as a no-op or we have a race condition
  446.                      * where the cache was cleared between the above calls to write to and load from
  447.                      * the cache.
  448.                      */
  449.                     eval('?>'.$content);
  450.                 }
  451.             }
  452.             if (!class_exists($clsfalse)) {
  453.                 throw new RuntimeError(sprintf('Failed to load Twig template "%s", index "%s": cache might be corrupted.'$name$index), -1$source);
  454.             }
  455.         }
  456.         if (!$this->runtimeInitialized) {
  457.             $this->initRuntime();
  458.         }
  459.         return $this->loadedTemplates[$cls] = new $cls($this);
  460.     }
  461.     /**
  462.      * Creates a template from source.
  463.      *
  464.      * This method should not be used as a generic way to load templates.
  465.      *
  466.      * @param string $template The template name
  467.      * @param string $name     An optional name of the template to be used in error messages
  468.      *
  469.      * @return TemplateWrapper A template instance representing the given template name
  470.      *
  471.      * @throws LoaderError When the template cannot be found
  472.      * @throws SyntaxError When an error occurred during compilation
  473.      */
  474.     public function createTemplate($template$name null)
  475.     {
  476.         $hash hash('sha256'$templatefalse);
  477.         if (null !== $name) {
  478.             $name sprintf('%s (string template %s)'$name$hash);
  479.         } else {
  480.             $name sprintf('__string_template__%s'$hash);
  481.         }
  482.         $loader = new ChainLoader([
  483.             new ArrayLoader([$name => $template]),
  484.             $current $this->getLoader(),
  485.         ]);
  486.         $this->setLoader($loader);
  487.         try {
  488.             $template = new TemplateWrapper($this$this->loadTemplate($name));
  489.         } catch (\Exception $e) {
  490.             $this->setLoader($current);
  491.             throw $e;
  492.         } catch (\Throwable $e) {
  493.             $this->setLoader($current);
  494.             throw $e;
  495.         }
  496.         $this->setLoader($current);
  497.         return $template;
  498.     }
  499.     /**
  500.      * Returns true if the template is still fresh.
  501.      *
  502.      * Besides checking the loader for freshness information,
  503.      * this method also checks if the enabled extensions have
  504.      * not changed.
  505.      *
  506.      * @param string $name The template name
  507.      * @param int    $time The last modification time of the cached template
  508.      *
  509.      * @return bool true if the template is fresh, false otherwise
  510.      */
  511.     public function isTemplateFresh($name$time)
  512.     {
  513.         if (=== $this->lastModifiedExtension) {
  514.             foreach ($this->extensions as $extension) {
  515.                 $r = new \ReflectionObject($extension);
  516.                 if (file_exists($r->getFileName()) && ($extensionTime filemtime($r->getFileName())) > $this->lastModifiedExtension) {
  517.                     $this->lastModifiedExtension $extensionTime;
  518.                 }
  519.             }
  520.         }
  521.         return $this->lastModifiedExtension <= $time && $this->getLoader()->isFresh($name$time);
  522.     }
  523.     /**
  524.      * Tries to load a template consecutively from an array.
  525.      *
  526.      * Similar to load() but it also accepts instances of \Twig\Template and
  527.      * \Twig\TemplateWrapper, and an array of templates where each is tried to be loaded.
  528.      *
  529.      * @param string|Template|\Twig\TemplateWrapper|array $names A template or an array of templates to try consecutively
  530.      *
  531.      * @return TemplateWrapper|Template
  532.      *
  533.      * @throws LoaderError When none of the templates can be found
  534.      * @throws SyntaxError When an error occurred during compilation
  535.      */
  536.     public function resolveTemplate($names)
  537.     {
  538.         if (!\is_array($names)) {
  539.             $names = [$names];
  540.         }
  541.         foreach ($names as $name) {
  542.             if ($name instanceof Template) {
  543.                 return $name;
  544.             }
  545.             if ($name instanceof TemplateWrapper) {
  546.                 return $name;
  547.             }
  548.             try {
  549.                 return $this->loadTemplate($name);
  550.             } catch (LoaderError $e) {
  551.                 if (=== \count($names)) {
  552.                     throw $e;
  553.                 }
  554.             }
  555.         }
  556.         throw new LoaderError(sprintf('Unable to find one of the following templates: "%s".'implode('", "'$names)));
  557.     }
  558.     /**
  559.      * Clears the internal template cache.
  560.      *
  561.      * @deprecated since 1.18.3 (to be removed in 2.0)
  562.      */
  563.     public function clearTemplateCache()
  564.     {
  565.         @trigger_error(sprintf('The %s method is deprecated since version 1.18.3 and will be removed in Twig 2.0.'__METHOD__), E_USER_DEPRECATED);
  566.         $this->loadedTemplates = [];
  567.     }
  568.     /**
  569.      * Clears the template cache files on the filesystem.
  570.      *
  571.      * @deprecated since 1.22 (to be removed in 2.0)
  572.      */
  573.     public function clearCacheFiles()
  574.     {
  575.         @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.'__METHOD__), E_USER_DEPRECATED);
  576.         if (\is_string($this->originalCache)) {
  577.             foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->originalCache), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
  578.                 if ($file->isFile()) {
  579.                     @unlink($file->getPathname());
  580.                 }
  581.             }
  582.         }
  583.     }
  584.     /**
  585.      * Gets the Lexer instance.
  586.      *
  587.      * @return \Twig_LexerInterface
  588.      *
  589.      * @deprecated since 1.25 (to be removed in 2.0)
  590.      */
  591.     public function getLexer()
  592.     {
  593.         @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.'__FUNCTION__), E_USER_DEPRECATED);
  594.         if (null === $this->lexer) {
  595.             $this->lexer = new Lexer($this);
  596.         }
  597.         return $this->lexer;
  598.     }
  599.     public function setLexer(\Twig_LexerInterface $lexer)
  600.     {
  601.         $this->lexer $lexer;
  602.     }
  603.     /**
  604.      * Tokenizes a source code.
  605.      *
  606.      * @param string|Source $source The template source code
  607.      * @param string        $name   The template name (deprecated)
  608.      *
  609.      * @return TokenStream
  610.      *
  611.      * @throws SyntaxError When the code is syntactically wrong
  612.      */
  613.     public function tokenize($source$name null)
  614.     {
  615.         if (!$source instanceof Source) {
  616.             @trigger_error(sprintf('Passing a string as the $source argument of %s() is deprecated since version 1.27. Pass a Twig\Source instance instead.'__METHOD__), E_USER_DEPRECATED);
  617.             $source = new Source($source$name);
  618.         }
  619.         if (null === $this->lexer) {
  620.             $this->lexer = new Lexer($this);
  621.         }
  622.         return $this->lexer->tokenize($source);
  623.     }
  624.     /**
  625.      * Gets the Parser instance.
  626.      *
  627.      * @return \Twig_ParserInterface
  628.      *
  629.      * @deprecated since 1.25 (to be removed in 2.0)
  630.      */
  631.     public function getParser()
  632.     {
  633.         @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.'__FUNCTION__), E_USER_DEPRECATED);
  634.         if (null === $this->parser) {
  635.             $this->parser = new Parser($this);
  636.         }
  637.         return $this->parser;
  638.     }
  639.     public function setParser(\Twig_ParserInterface $parser)
  640.     {
  641.         $this->parser $parser;
  642.     }
  643.     /**
  644.      * Converts a token stream to a node tree.
  645.      *
  646.      * @return ModuleNode
  647.      *
  648.      * @throws SyntaxError When the token stream is syntactically or semantically wrong
  649.      */
  650.     public function parse(TokenStream $stream)
  651.     {
  652.         if (null === $this->parser) {
  653.             $this->parser = new Parser($this);
  654.         }
  655.         return $this->parser->parse($stream);
  656.     }
  657.     /**
  658.      * Gets the Compiler instance.
  659.      *
  660.      * @return \Twig_CompilerInterface
  661.      *
  662.      * @deprecated since 1.25 (to be removed in 2.0)
  663.      */
  664.     public function getCompiler()
  665.     {
  666.         @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.'__FUNCTION__), E_USER_DEPRECATED);
  667.         if (null === $this->compiler) {
  668.             $this->compiler = new Compiler($this);
  669.         }
  670.         return $this->compiler;
  671.     }
  672.     public function setCompiler(\Twig_CompilerInterface $compiler)
  673.     {
  674.         $this->compiler $compiler;
  675.     }
  676.     /**
  677.      * Compiles a node and returns the PHP code.
  678.      *
  679.      * @return string The compiled PHP source code
  680.      */
  681.     public function compile(\Twig_NodeInterface $node)
  682.     {
  683.         if (null === $this->compiler) {
  684.             $this->compiler = new Compiler($this);
  685.         }
  686.         return $this->compiler->compile($node)->getSource();
  687.     }
  688.     /**
  689.      * Compiles a template source code.
  690.      *
  691.      * @param string|Source $source The template source code
  692.      * @param string        $name   The template name (deprecated)
  693.      *
  694.      * @return string The compiled PHP source code
  695.      *
  696.      * @throws SyntaxError When there was an error during tokenizing, parsing or compiling
  697.      */
  698.     public function compileSource($source$name null)
  699.     {
  700.         if (!$source instanceof Source) {
  701.             @trigger_error(sprintf('Passing a string as the $source argument of %s() is deprecated since version 1.27. Pass a Twig\Source instance instead.'__METHOD__), E_USER_DEPRECATED);
  702.             $source = new Source($source$name);
  703.         }
  704.         try {
  705.             return $this->compile($this->parse($this->tokenize($source)));
  706.         } catch (Error $e) {
  707.             $e->setSourceContext($source);
  708.             throw $e;
  709.         } catch (\Exception $e) {
  710.             throw new SyntaxError(sprintf('An exception has been thrown during the compilation of a template ("%s").'$e->getMessage()), -1$source$e);
  711.         }
  712.     }
  713.     public function setLoader(LoaderInterface $loader)
  714.     {
  715.         if (!$loader instanceof SourceContextLoaderInterface && !== strpos(\get_class($loader), 'Mock_')) {
  716.             @trigger_error(sprintf('Twig loader "%s" should implement Twig\Loader\SourceContextLoaderInterface since version 1.27.', \get_class($loader)), E_USER_DEPRECATED);
  717.         }
  718.         $this->loader $loader;
  719.     }
  720.     /**
  721.      * Gets the Loader instance.
  722.      *
  723.      * @return LoaderInterface
  724.      */
  725.     public function getLoader()
  726.     {
  727.         if (null === $this->loader) {
  728.             throw new \LogicException('You must set a loader first.');
  729.         }
  730.         return $this->loader;
  731.     }
  732.     /**
  733.      * Sets the default template charset.
  734.      *
  735.      * @param string $charset The default charset
  736.      */
  737.     public function setCharset($charset)
  738.     {
  739.         $this->charset strtoupper($charset);
  740.     }
  741.     /**
  742.      * Gets the default template charset.
  743.      *
  744.      * @return string The default charset
  745.      */
  746.     public function getCharset()
  747.     {
  748.         return $this->charset;
  749.     }
  750.     /**
  751.      * Initializes the runtime environment.
  752.      *
  753.      * @deprecated since 1.23 (to be removed in 2.0)
  754.      */
  755.     public function initRuntime()
  756.     {
  757.         $this->runtimeInitialized true;
  758.         foreach ($this->getExtensions() as $name => $extension) {
  759.             if (!$extension instanceof InitRuntimeInterface) {
  760.                 $m = new \ReflectionMethod($extension'initRuntime');
  761.                 $parentClass $m->getDeclaringClass()->getName();
  762.                 if ('Twig_Extension' !== $parentClass && 'Twig\Extension\AbstractExtension' !== $parentClass) {
  763.                     @trigger_error(sprintf('Defining the initRuntime() method in the "%s" extension is deprecated since version 1.23. Use the `needs_environment` option to get the \Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig\Extension\InitRuntimeInterface if needed (not recommended).'$name), E_USER_DEPRECATED);
  764.                 }
  765.             }
  766.             $extension->initRuntime($this);
  767.         }
  768.     }
  769.     /**
  770.      * Returns true if the given extension is registered.
  771.      *
  772.      * @param string $class The extension class name
  773.      *
  774.      * @return bool Whether the extension is registered or not
  775.      */
  776.     public function hasExtension($class)
  777.     {
  778.         $class ltrim($class'\\');
  779.         if (!isset($this->extensionsByClass[$class]) && class_exists($classfalse)) {
  780.             // For BC/FC with namespaced aliases
  781.             $class = new \ReflectionClass($class);
  782.             $class $class->name;
  783.         }
  784.         if (isset($this->extensions[$class])) {
  785.             if ($class !== \get_class($this->extensions[$class])) {
  786.                 @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.'$class), E_USER_DEPRECATED);
  787.             }
  788.             return true;
  789.         }
  790.         return isset($this->extensionsByClass[$class]);
  791.     }
  792.     /**
  793.      * Adds a runtime loader.
  794.      */
  795.     public function addRuntimeLoader(RuntimeLoaderInterface $loader)
  796.     {
  797.         $this->runtimeLoaders[] = $loader;
  798.     }
  799.     /**
  800.      * Gets an extension by class name.
  801.      *
  802.      * @param string $class The extension class name
  803.      *
  804.      * @return ExtensionInterface
  805.      */
  806.     public function getExtension($class)
  807.     {
  808.         $class ltrim($class'\\');
  809.         if (!isset($this->extensionsByClass[$class]) && class_exists($classfalse)) {
  810.             // For BC/FC with namespaced aliases
  811.             $class = new \ReflectionClass($class);
  812.             $class $class->name;
  813.         }
  814.         if (isset($this->extensions[$class])) {
  815.             if ($class !== \get_class($this->extensions[$class])) {
  816.                 @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.'$class), E_USER_DEPRECATED);
  817.             }
  818.             return $this->extensions[$class];
  819.         }
  820.         if (!isset($this->extensionsByClass[$class])) {
  821.             throw new RuntimeError(sprintf('The "%s" extension is not enabled.'$class));
  822.         }
  823.         return $this->extensionsByClass[$class];
  824.     }
  825.     /**
  826.      * Returns the runtime implementation of a Twig element (filter/function/test).
  827.      *
  828.      * @param string $class A runtime class name
  829.      *
  830.      * @return object The runtime implementation
  831.      *
  832.      * @throws RuntimeError When the template cannot be found
  833.      */
  834.     public function getRuntime($class)
  835.     {
  836.         if (isset($this->runtimes[$class])) {
  837.             return $this->runtimes[$class];
  838.         }
  839.         foreach ($this->runtimeLoaders as $loader) {
  840.             if (null !== $runtime $loader->load($class)) {
  841.                 return $this->runtimes[$class] = $runtime;
  842.             }
  843.         }
  844.         throw new RuntimeError(sprintf('Unable to load the "%s" runtime.'$class));
  845.     }
  846.     public function addExtension(ExtensionInterface $extension)
  847.     {
  848.         if ($this->extensionInitialized) {
  849.             throw new \LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.'$extension->getName()));
  850.         }
  851.         $class = \get_class($extension);
  852.         if ($class !== $extension->getName()) {
  853.             if (isset($this->extensions[$extension->getName()])) {
  854.                 unset($this->extensions[$extension->getName()], $this->extensionsByClass[$class]);
  855.                 @trigger_error(sprintf('The possibility to register the same extension twice ("%s") is deprecated since version 1.23 and will be removed in Twig 2.0. Use proper PHP inheritance instead.'$extension->getName()), E_USER_DEPRECATED);
  856.             }
  857.         }
  858.         $this->lastModifiedExtension 0;
  859.         $this->extensionsByClass[$class] = $extension;
  860.         $this->extensions[$extension->getName()] = $extension;
  861.         $this->updateOptionsHash();
  862.     }
  863.     /**
  864.      * Removes an extension by name.
  865.      *
  866.      * This method is deprecated and you should not use it.
  867.      *
  868.      * @param string $name The extension name
  869.      *
  870.      * @deprecated since 1.12 (to be removed in 2.0)
  871.      */
  872.     public function removeExtension($name)
  873.     {
  874.         @trigger_error(sprintf('The %s method is deprecated since version 1.12 and will be removed in Twig 2.0.'__METHOD__), E_USER_DEPRECATED);
  875.         if ($this->extensionInitialized) {
  876.             throw new \LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.'$name));
  877.         }
  878.         $class ltrim($name'\\');
  879.         if (!isset($this->extensionsByClass[$class]) && class_exists($classfalse)) {
  880.             // For BC/FC with namespaced aliases
  881.             $class = new \ReflectionClass($class);
  882.             $class $class->name;
  883.         }
  884.         if (isset($this->extensions[$class])) {
  885.             if ($class !== \get_class($this->extensions[$class])) {
  886.                 @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.'$class), E_USER_DEPRECATED);
  887.             }
  888.             unset($this->extensions[$class]);
  889.         }
  890.         unset($this->extensions[$class]);
  891.         $this->updateOptionsHash();
  892.     }
  893.     /**
  894.      * Registers an array of extensions.
  895.      *
  896.      * @param array $extensions An array of extensions
  897.      */
  898.     public function setExtensions(array $extensions)
  899.     {
  900.         foreach ($extensions as $extension) {
  901.             $this->addExtension($extension);
  902.         }
  903.     }
  904.     /**
  905.      * Returns all registered extensions.
  906.      *
  907.      * @return ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on)
  908.      */
  909.     public function getExtensions()
  910.     {
  911.         return $this->extensions;
  912.     }
  913.     public function addTokenParser(TokenParserInterface $parser)
  914.     {
  915.         if ($this->extensionInitialized) {
  916.             throw new \LogicException('Unable to add a token parser as extensions have already been initialized.');
  917.         }
  918.         $this->staging->addTokenParser($parser);
  919.     }
  920.     /**
  921.      * Gets the registered Token Parsers.
  922.      *
  923.      * @return \Twig_TokenParserBrokerInterface
  924.      *
  925.      * @internal
  926.      */
  927.     public function getTokenParsers()
  928.     {
  929.         if (!$this->extensionInitialized) {
  930.             $this->initExtensions();
  931.         }
  932.         return $this->parsers;
  933.     }
  934.     /**
  935.      * Gets registered tags.
  936.      *
  937.      * Be warned that this method cannot return tags defined by \Twig_TokenParserBrokerInterface classes.
  938.      *
  939.      * @return TokenParserInterface[]
  940.      *
  941.      * @internal
  942.      */
  943.     public function getTags()
  944.     {
  945.         $tags = [];
  946.         foreach ($this->getTokenParsers()->getParsers() as $parser) {
  947.             if ($parser instanceof TokenParserInterface) {
  948.                 $tags[$parser->getTag()] = $parser;
  949.             }
  950.         }
  951.         return $tags;
  952.     }
  953.     public function addNodeVisitor(NodeVisitorInterface $visitor)
  954.     {
  955.         if ($this->extensionInitialized) {
  956.             throw new \LogicException('Unable to add a node visitor as extensions have already been initialized.');
  957.         }
  958.         $this->staging->addNodeVisitor($visitor);
  959.     }
  960.     /**
  961.      * Gets the registered Node Visitors.
  962.      *
  963.      * @return NodeVisitorInterface[]
  964.      *
  965.      * @internal
  966.      */
  967.     public function getNodeVisitors()
  968.     {
  969.         if (!$this->extensionInitialized) {
  970.             $this->initExtensions();
  971.         }
  972.         return $this->visitors;
  973.     }
  974.     /**
  975.      * Registers a Filter.
  976.      *
  977.      * @param string|TwigFilter                $name   The filter name or a \Twig_SimpleFilter instance
  978.      * @param \Twig_FilterInterface|TwigFilter $filter
  979.      */
  980.     public function addFilter($name$filter null)
  981.     {
  982.         if (!$name instanceof TwigFilter && !($filter instanceof TwigFilter || $filter instanceof \Twig_FilterInterface)) {
  983.             throw new \LogicException('A filter must be an instance of \Twig_FilterInterface or \Twig_SimpleFilter.');
  984.         }
  985.         if ($name instanceof TwigFilter) {
  986.             $filter $name;
  987.             $name $filter->getName();
  988.         } else {
  989.             @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFilter" instead when defining filter "%s".'__METHOD__$name), E_USER_DEPRECATED);
  990.         }
  991.         if ($this->extensionInitialized) {
  992.             throw new \LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.'$name));
  993.         }
  994.         $this->staging->addFilter($name$filter);
  995.     }
  996.     /**
  997.      * Get a filter by name.
  998.      *
  999.      * Subclasses may override this method and load filters differently;
  1000.      * so no list of filters is available.
  1001.      *
  1002.      * @param string $name The filter name
  1003.      *
  1004.      * @return \Twig_Filter|false
  1005.      *
  1006.      * @internal
  1007.      */
  1008.     public function getFilter($name)
  1009.     {
  1010.         if (!$this->extensionInitialized) {
  1011.             $this->initExtensions();
  1012.         }
  1013.         if (isset($this->filters[$name])) {
  1014.             return $this->filters[$name];
  1015.         }
  1016.         foreach ($this->filters as $pattern => $filter) {
  1017.             $pattern str_replace('\\*''(.*?)'preg_quote($pattern'#'), $count);
  1018.             if ($count) {
  1019.                 if (preg_match('#^'.$pattern.'$#'$name$matches)) {
  1020.                     array_shift($matches);
  1021.                     $filter->setArguments($matches);
  1022.                     return $filter;
  1023.                 }
  1024.             }
  1025.         }
  1026.         foreach ($this->filterCallbacks as $callback) {
  1027.             if (false !== $filter = \call_user_func($callback$name)) {
  1028.                 return $filter;
  1029.             }
  1030.         }
  1031.         return false;
  1032.     }
  1033.     public function registerUndefinedFilterCallback($callable)
  1034.     {
  1035.         $this->filterCallbacks[] = $callable;
  1036.     }
  1037.     /**
  1038.      * Gets the registered Filters.
  1039.      *
  1040.      * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
  1041.      *
  1042.      * @return \Twig_FilterInterface[]
  1043.      *
  1044.      * @see registerUndefinedFilterCallback
  1045.      *
  1046.      * @internal
  1047.      */
  1048.     public function getFilters()
  1049.     {
  1050.         if (!$this->extensionInitialized) {
  1051.             $this->initExtensions();
  1052.         }
  1053.         return $this->filters;
  1054.     }
  1055.     /**
  1056.      * Registers a Test.
  1057.      *
  1058.      * @param string|TwigTest              $name The test name or a \Twig_SimpleTest instance
  1059.      * @param \Twig_TestInterface|TwigTest $test A \Twig_TestInterface instance or a \Twig_SimpleTest instance
  1060.      */
  1061.     public function addTest($name$test null)
  1062.     {
  1063.         if (!$name instanceof TwigTest && !($test instanceof TwigTest || $test instanceof \Twig_TestInterface)) {
  1064.             throw new \LogicException('A test must be an instance of \Twig_TestInterface or \Twig_SimpleTest.');
  1065.         }
  1066.         if ($name instanceof TwigTest) {
  1067.             $test $name;
  1068.             $name $test->getName();
  1069.         } else {
  1070.             @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleTest" instead when defining test "%s".'__METHOD__$name), E_USER_DEPRECATED);
  1071.         }
  1072.         if ($this->extensionInitialized) {
  1073.             throw new \LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.'$name));
  1074.         }
  1075.         $this->staging->addTest($name$test);
  1076.     }
  1077.     /**
  1078.      * Gets the registered Tests.
  1079.      *
  1080.      * @return \Twig_TestInterface[]
  1081.      *
  1082.      * @internal
  1083.      */
  1084.     public function getTests()
  1085.     {
  1086.         if (!$this->extensionInitialized) {
  1087.             $this->initExtensions();
  1088.         }
  1089.         return $this->tests;
  1090.     }
  1091.     /**
  1092.      * Gets a test by name.
  1093.      *
  1094.      * @param string $name The test name
  1095.      *
  1096.      * @return \Twig_Test|false
  1097.      *
  1098.      * @internal
  1099.      */
  1100.     public function getTest($name)
  1101.     {
  1102.         if (!$this->extensionInitialized) {
  1103.             $this->initExtensions();
  1104.         }
  1105.         if (isset($this->tests[$name])) {
  1106.             return $this->tests[$name];
  1107.         }
  1108.         foreach ($this->tests as $pattern => $test) {
  1109.             $pattern str_replace('\\*''(.*?)'preg_quote($pattern'#'), $count);
  1110.             if ($count) {
  1111.                 if (preg_match('#^'.$pattern.'$#'$name$matches)) {
  1112.                     array_shift($matches);
  1113.                     $test->setArguments($matches);
  1114.                     return $test;
  1115.                 }
  1116.             }
  1117.         }
  1118.         return false;
  1119.     }
  1120.     /**
  1121.      * Registers a Function.
  1122.      *
  1123.      * @param string|TwigFunction                  $name     The function name or a \Twig_SimpleFunction instance
  1124.      * @param \Twig_FunctionInterface|TwigFunction $function
  1125.      */
  1126.     public function addFunction($name$function null)
  1127.     {
  1128.         if (!$name instanceof TwigFunction && !($function instanceof TwigFunction || $function instanceof \Twig_FunctionInterface)) {
  1129.             throw new \LogicException('A function must be an instance of \Twig_FunctionInterface or \Twig_SimpleFunction.');
  1130.         }
  1131.         if ($name instanceof TwigFunction) {
  1132.             $function $name;
  1133.             $name $function->getName();
  1134.         } else {
  1135.             @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".'__METHOD__$name), E_USER_DEPRECATED);
  1136.         }
  1137.         if ($this->extensionInitialized) {
  1138.             throw new \LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.'$name));
  1139.         }
  1140.         $this->staging->addFunction($name$function);
  1141.     }
  1142.     /**
  1143.      * Get a function by name.
  1144.      *
  1145.      * Subclasses may override this method and load functions differently;
  1146.      * so no list of functions is available.
  1147.      *
  1148.      * @param string $name function name
  1149.      *
  1150.      * @return \Twig_Function|false
  1151.      *
  1152.      * @internal
  1153.      */
  1154.     public function getFunction($name)
  1155.     {
  1156.         if (!$this->extensionInitialized) {
  1157.             $this->initExtensions();
  1158.         }
  1159.         if (isset($this->functions[$name])) {
  1160.             return $this->functions[$name];
  1161.         }
  1162.         foreach ($this->functions as $pattern => $function) {
  1163.             $pattern str_replace('\\*''(.*?)'preg_quote($pattern'#'), $count);
  1164.             if ($count) {
  1165.                 if (preg_match('#^'.$pattern.'$#'$name$matches)) {
  1166.                     array_shift($matches);
  1167.                     $function->setArguments($matches);
  1168.                     return $function;
  1169.                 }
  1170.             }
  1171.         }
  1172.         foreach ($this->functionCallbacks as $callback) {
  1173.             if (false !== $function = \call_user_func($callback$name)) {
  1174.                 return $function;
  1175.             }
  1176.         }
  1177.         return false;
  1178.     }
  1179.     public function registerUndefinedFunctionCallback($callable)
  1180.     {
  1181.         $this->functionCallbacks[] = $callable;
  1182.     }
  1183.     /**
  1184.      * Gets registered functions.
  1185.      *
  1186.      * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
  1187.      *
  1188.      * @return \Twig_FunctionInterface[]
  1189.      *
  1190.      * @see registerUndefinedFunctionCallback
  1191.      *
  1192.      * @internal
  1193.      */
  1194.     public function getFunctions()
  1195.     {
  1196.         if (!$this->extensionInitialized) {
  1197.             $this->initExtensions();
  1198.         }
  1199.         return $this->functions;
  1200.     }
  1201.     /**
  1202.      * Registers a Global.
  1203.      *
  1204.      * New globals can be added before compiling or rendering a template;
  1205.      * but after, you can only update existing globals.
  1206.      *
  1207.      * @param string $name  The global name
  1208.      * @param mixed  $value The global value
  1209.      */
  1210.     public function addGlobal($name$value)
  1211.     {
  1212.         if ($this->extensionInitialized || $this->runtimeInitialized) {
  1213.             if (null === $this->globals) {
  1214.                 $this->globals $this->initGlobals();
  1215.             }
  1216.             if (!\array_key_exists($name$this->globals)) {
  1217.                 // The deprecation notice must be turned into the following exception in Twig 2.0
  1218.                 @trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated since version 1.21.'$name), E_USER_DEPRECATED);
  1219.                 //throw new \LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
  1220.             }
  1221.         }
  1222.         if ($this->extensionInitialized || $this->runtimeInitialized) {
  1223.             // update the value
  1224.             $this->globals[$name] = $value;
  1225.         } else {
  1226.             $this->staging->addGlobal($name$value);
  1227.         }
  1228.     }
  1229.     /**
  1230.      * Gets the registered Globals.
  1231.      *
  1232.      * @return array An array of globals
  1233.      *
  1234.      * @internal
  1235.      */
  1236.     public function getGlobals()
  1237.     {
  1238.         if (!$this->runtimeInitialized && !$this->extensionInitialized) {
  1239.             return $this->initGlobals();
  1240.         }
  1241.         if (null === $this->globals) {
  1242.             $this->globals $this->initGlobals();
  1243.         }
  1244.         return $this->globals;
  1245.     }
  1246.     /**
  1247.      * Merges a context with the defined globals.
  1248.      *
  1249.      * @param array $context An array representing the context
  1250.      *
  1251.      * @return array The context merged with the globals
  1252.      */
  1253.     public function mergeGlobals(array $context)
  1254.     {
  1255.         // we don't use array_merge as the context being generally
  1256.         // bigger than globals, this code is faster.
  1257.         foreach ($this->getGlobals() as $key => $value) {
  1258.             if (!\array_key_exists($key$context)) {
  1259.                 $context[$key] = $value;
  1260.             }
  1261.         }
  1262.         return $context;
  1263.     }
  1264.     /**
  1265.      * Gets the registered unary Operators.
  1266.      *
  1267.      * @return array An array of unary operators
  1268.      *
  1269.      * @internal
  1270.      */
  1271.     public function getUnaryOperators()
  1272.     {
  1273.         if (!$this->extensionInitialized) {
  1274.             $this->initExtensions();
  1275.         }
  1276.         return $this->unaryOperators;
  1277.     }
  1278.     /**
  1279.      * Gets the registered binary Operators.
  1280.      *
  1281.      * @return array An array of binary operators
  1282.      *
  1283.      * @internal
  1284.      */
  1285.     public function getBinaryOperators()
  1286.     {
  1287.         if (!$this->extensionInitialized) {
  1288.             $this->initExtensions();
  1289.         }
  1290.         return $this->binaryOperators;
  1291.     }
  1292.     /**
  1293.      * @deprecated since 1.23 (to be removed in 2.0)
  1294.      */
  1295.     public function computeAlternatives($name$items)
  1296.     {
  1297.         @trigger_error(sprintf('The %s method is deprecated since version 1.23 and will be removed in Twig 2.0.'__METHOD__), E_USER_DEPRECATED);
  1298.         return SyntaxError::computeAlternatives($name$items);
  1299.     }
  1300.     /**
  1301.      * @internal
  1302.      */
  1303.     protected function initGlobals()
  1304.     {
  1305.         $globals = [];
  1306.         foreach ($this->extensions as $name => $extension) {
  1307.             if (!$extension instanceof GlobalsInterface) {
  1308.                 $m = new \ReflectionMethod($extension'getGlobals');
  1309.                 $parentClass $m->getDeclaringClass()->getName();
  1310.                 if ('Twig_Extension' !== $parentClass && 'Twig\Extension\AbstractExtension' !== $parentClass) {
  1311.                     @trigger_error(sprintf('Defining the getGlobals() method in the "%s" extension without explicitly implementing Twig\Extension\GlobalsInterface is deprecated since version 1.23.'$name), E_USER_DEPRECATED);
  1312.                 }
  1313.             }
  1314.             $extGlob $extension->getGlobals();
  1315.             if (!\is_array($extGlob)) {
  1316.                 throw new \UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', \get_class($extension)));
  1317.             }
  1318.             $globals[] = $extGlob;
  1319.         }
  1320.         $globals[] = $this->staging->getGlobals();
  1321.         return \call_user_func_array('array_merge'$globals);
  1322.     }
  1323.     /**
  1324.      * @internal
  1325.      */
  1326.     protected function initExtensions()
  1327.     {
  1328.         if ($this->extensionInitialized) {
  1329.             return;
  1330.         }
  1331.         $this->parsers = new \Twig_TokenParserBroker([], [], false);
  1332.         $this->filters = [];
  1333.         $this->functions = [];
  1334.         $this->tests = [];
  1335.         $this->visitors = [];
  1336.         $this->unaryOperators = [];
  1337.         $this->binaryOperators = [];
  1338.         foreach ($this->extensions as $extension) {
  1339.             $this->initExtension($extension);
  1340.         }
  1341.         $this->initExtension($this->staging);
  1342.         // Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception
  1343.         $this->extensionInitialized true;
  1344.     }
  1345.     /**
  1346.      * @internal
  1347.      */
  1348.     protected function initExtension(ExtensionInterface $extension)
  1349.     {
  1350.         // filters
  1351.         foreach ($extension->getFilters() as $name => $filter) {
  1352.             if ($filter instanceof TwigFilter) {
  1353.                 $name $filter->getName();
  1354.             } else {
  1355.                 @trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated since version 1.21. Use \Twig_SimpleFilter instead.', \get_class($filter), $name), E_USER_DEPRECATED);
  1356.             }
  1357.             $this->filters[$name] = $filter;
  1358.         }
  1359.         // functions
  1360.         foreach ($extension->getFunctions() as $name => $function) {
  1361.             if ($function instanceof TwigFunction) {
  1362.                 $name $function->getName();
  1363.             } else {
  1364.                 @trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated since version 1.21. Use \Twig_SimpleFunction instead.', \get_class($function), $name), E_USER_DEPRECATED);
  1365.             }
  1366.             $this->functions[$name] = $function;
  1367.         }
  1368.         // tests
  1369.         foreach ($extension->getTests() as $name => $test) {
  1370.             if ($test instanceof TwigTest) {
  1371.                 $name $test->getName();
  1372.             } else {
  1373.                 @trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated since version 1.21. Use \Twig_SimpleTest instead.', \get_class($test), $name), E_USER_DEPRECATED);
  1374.             }
  1375.             $this->tests[$name] = $test;
  1376.         }
  1377.         // token parsers
  1378.         foreach ($extension->getTokenParsers() as $parser) {
  1379.             if ($parser instanceof TokenParserInterface) {
  1380.                 $this->parsers->addTokenParser($parser);
  1381.             } elseif ($parser instanceof \Twig_TokenParserBrokerInterface) {
  1382.                 @trigger_error('Registering a \Twig_TokenParserBrokerInterface instance is deprecated since version 1.21.'E_USER_DEPRECATED);
  1383.                 $this->parsers->addTokenParserBroker($parser);
  1384.             } else {
  1385.                 throw new \LogicException('getTokenParsers() must return an array of \Twig_TokenParserInterface or \Twig_TokenParserBrokerInterface instances.');
  1386.             }
  1387.         }
  1388.         // node visitors
  1389.         foreach ($extension->getNodeVisitors() as $visitor) {
  1390.             $this->visitors[] = $visitor;
  1391.         }
  1392.         // operators
  1393.         if ($operators $extension->getOperators()) {
  1394.             if (!\is_array($operators)) {
  1395.                 throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', \get_class($extension), \is_object($operators) ? \get_class($operators) : \gettype($operators).(\is_resource($operators) ? '' '#'.$operators)));
  1396.             }
  1397.             if (!== \count($operators)) {
  1398.                 throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', \get_class($extension), \count($operators)));
  1399.             }
  1400.             $this->unaryOperators array_merge($this->unaryOperators$operators[0]);
  1401.             $this->binaryOperators array_merge($this->binaryOperators$operators[1]);
  1402.         }
  1403.     }
  1404.     /**
  1405.      * @deprecated since 1.22 (to be removed in 2.0)
  1406.      */
  1407.     protected function writeCacheFile($file$content)
  1408.     {
  1409.         $this->cache->write($file$content);
  1410.     }
  1411.     private function updateOptionsHash()
  1412.     {
  1413.         $hashParts array_merge(
  1414.             array_keys($this->extensions),
  1415.             [
  1416.                 (int) \function_exists('twig_template_get_attributes'),
  1417.                 PHP_MAJOR_VERSION,
  1418.                 PHP_MINOR_VERSION,
  1419.                 self::VERSION,
  1420.                 (int) $this->debug,
  1421.                 $this->baseTemplateClass,
  1422.                 (int) $this->strictVariables,
  1423.             ]
  1424.         );
  1425.         $this->optionsHash implode(':'$hashParts);
  1426.     }
  1427. }
  1428. class_alias('Twig\Environment''Twig_Environment');