- <?php
- /*
-  * This file is part of Twig.
-  *
-  * (c) Fabien Potencier
-  *
-  * For the full copyright and license information, please view the LICENSE
-  * file that was distributed with this source code.
-  */
- namespace Twig;
- use Twig\Cache\CacheInterface;
- use Twig\Cache\FilesystemCache;
- use Twig\Cache\NullCache;
- use Twig\Error\Error;
- use Twig\Error\LoaderError;
- use Twig\Error\RuntimeError;
- use Twig\Error\SyntaxError;
- use Twig\Extension\CoreExtension;
- use Twig\Extension\EscaperExtension;
- use Twig\Extension\ExtensionInterface;
- use Twig\Extension\OptimizerExtension;
- use Twig\Loader\ArrayLoader;
- use Twig\Loader\ChainLoader;
- use Twig\Loader\LoaderInterface;
- use Twig\Node\Expression\Binary\AbstractBinary;
- use Twig\Node\Expression\Unary\AbstractUnary;
- use Twig\Node\ModuleNode;
- use Twig\Node\Node;
- use Twig\NodeVisitor\NodeVisitorInterface;
- use Twig\RuntimeLoader\RuntimeLoaderInterface;
- use Twig\TokenParser\TokenParserInterface;
- /**
-  * Stores the Twig configuration and renders templates.
-  *
-  * @author Fabien Potencier <fabien@symfony.com>
-  */
- class Environment
- {
-     public const VERSION = '3.5.1';
-     public const VERSION_ID = 30501;
-     public const MAJOR_VERSION = 3;
-     public const MINOR_VERSION = 5;
-     public const RELEASE_VERSION = 1;
-     public const EXTRA_VERSION = '';
-     private $charset;
-     private $loader;
-     private $debug;
-     private $autoReload;
-     private $cache;
-     private $lexer;
-     private $parser;
-     private $compiler;
-     /** @var array<string, mixed> */
-     private $globals = [];
-     private $resolvedGlobals;
-     private $loadedTemplates;
-     private $strictVariables;
-     private $templateClassPrefix = '__TwigTemplate_';
-     private $originalCache;
-     private $extensionSet;
-     private $runtimeLoaders = [];
-     private $runtimes = [];
-     private $optionsHash;
-     /**
-      * Constructor.
-      *
-      * Available options:
-      *
-      *  * debug: When set to true, it automatically set "auto_reload" to true as
-      *           well (default to false).
-      *
-      *  * charset: The charset used by the templates (default to UTF-8).
-      *
-      *  * cache: An absolute path where to store the compiled templates,
-      *           a \Twig\Cache\CacheInterface implementation,
-      *           or false to disable compilation cache (default).
-      *
-      *  * auto_reload: Whether to reload the template if the original source changed.
-      *                 If you don't provide the auto_reload option, it will be
-      *                 determined automatically based on the debug value.
-      *
-      *  * strict_variables: Whether to ignore invalid variables in templates
-      *                      (default to false).
-      *
-      *  * autoescape: Whether to enable auto-escaping (default to html):
-      *                  * false: disable auto-escaping
-      *                  * html, js: set the autoescaping to one of the supported strategies
-      *                  * name: set the autoescaping strategy based on the template name extension
-      *                  * PHP callback: a PHP callback that returns an escaping strategy based on the template "name"
-      *
-      *  * optimizations: A flag that indicates which optimizations to apply
-      *                   (default to -1 which means that all optimizations are enabled;
-      *                   set it to 0 to disable).
-      */
-     public function __construct(LoaderInterface $loader, $options = [])
-     {
-         $this->setLoader($loader);
-         $options = array_merge([
-             'debug' => false,
-             'charset' => 'UTF-8',
-             'strict_variables' => false,
-             'autoescape' => 'html',
-             'cache' => false,
-             'auto_reload' => null,
-             'optimizations' => -1,
-         ], $options);
-         $this->debug = (bool) $options['debug'];
-         $this->setCharset($options['charset'] ?? 'UTF-8');
-         $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
-         $this->strictVariables = (bool) $options['strict_variables'];
-         $this->setCache($options['cache']);
-         $this->extensionSet = new ExtensionSet();
-         $this->addExtension(new CoreExtension());
-         $this->addExtension(new EscaperExtension($options['autoescape']));
-         $this->addExtension(new OptimizerExtension($options['optimizations']));
-     }
-     /**
-      * Enables debugging mode.
-      */
-     public function enableDebug()
-     {
-         $this->debug = true;
-         $this->updateOptionsHash();
-     }
-     /**
-      * Disables debugging mode.
-      */
-     public function disableDebug()
-     {
-         $this->debug = false;
-         $this->updateOptionsHash();
-     }
-     /**
-      * Checks if debug mode is enabled.
-      *
-      * @return bool true if debug mode is enabled, false otherwise
-      */
-     public function isDebug()
-     {
-         return $this->debug;
-     }
-     /**
-      * Enables the auto_reload option.
-      */
-     public function enableAutoReload()
-     {
-         $this->autoReload = true;
-     }
-     /**
-      * Disables the auto_reload option.
-      */
-     public function disableAutoReload()
-     {
-         $this->autoReload = false;
-     }
-     /**
-      * Checks if the auto_reload option is enabled.
-      *
-      * @return bool true if auto_reload is enabled, false otherwise
-      */
-     public function isAutoReload()
-     {
-         return $this->autoReload;
-     }
-     /**
-      * Enables the strict_variables option.
-      */
-     public function enableStrictVariables()
-     {
-         $this->strictVariables = true;
-         $this->updateOptionsHash();
-     }
-     /**
-      * Disables the strict_variables option.
-      */
-     public function disableStrictVariables()
-     {
-         $this->strictVariables = false;
-         $this->updateOptionsHash();
-     }
-     /**
-      * Checks if the strict_variables option is enabled.
-      *
-      * @return bool true if strict_variables is enabled, false otherwise
-      */
-     public function isStrictVariables()
-     {
-         return $this->strictVariables;
-     }
-     /**
-      * Gets the current cache implementation.
-      *
-      * @param bool $original Whether to return the original cache option or the real cache instance
-      *
-      * @return CacheInterface|string|false A Twig\Cache\CacheInterface implementation,
-      *                                     an absolute path to the compiled templates,
-      *                                     or false to disable cache
-      */
-     public function getCache($original = true)
-     {
-         return $original ? $this->originalCache : $this->cache;
-     }
-     /**
-      * Sets the current cache implementation.
-      *
-      * @param CacheInterface|string|false $cache A Twig\Cache\CacheInterface implementation,
-      *                                           an absolute path to the compiled templates,
-      *                                           or false to disable cache
-      */
-     public function setCache($cache)
-     {
-         if (\is_string($cache)) {
-             $this->originalCache = $cache;
-             $this->cache = new FilesystemCache($cache, $this->autoReload ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0);
-         } elseif (false === $cache) {
-             $this->originalCache = $cache;
-             $this->cache = new NullCache();
-         } elseif ($cache instanceof CacheInterface) {
-             $this->originalCache = $this->cache = $cache;
-         } else {
-             throw new \LogicException('Cache can only be a string, false, or a \Twig\Cache\CacheInterface implementation.');
-         }
-     }
-     /**
-      * Gets the template class associated with the given string.
-      *
-      * The generated template class is based on the following parameters:
-      *
-      *  * The cache key for the given template;
-      *  * The currently enabled extensions;
-      *  * Whether the Twig C extension is available or not;
-      *  * PHP version;
-      *  * Twig version;
-      *  * Options with what environment was created.
-      *
-      * @param string   $name  The name for which to calculate the template class name
-      * @param int|null $index The index if it is an embedded template
-      *
-      * @internal
-      */
-     public function getTemplateClass(string $name, int $index = null): string
-     {
-         $key = $this->getLoader()->getCacheKey($name).$this->optionsHash;
-         return $this->templateClassPrefix.hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $key).(null === $index ? '' : '___'.$index);
-     }
-     /**
-      * Renders a template.
-      *
-      * @param string|TemplateWrapper $name The template name
-      *
-      * @throws LoaderError  When the template cannot be found
-      * @throws SyntaxError  When an error occurred during compilation
-      * @throws RuntimeError When an error occurred during rendering
-      */
-     public function render($name, array $context = []): string
-     {
-         return $this->load($name)->render($context);
-     }
-     /**
-      * Displays a template.
-      *
-      * @param string|TemplateWrapper $name The template name
-      *
-      * @throws LoaderError  When the template cannot be found
-      * @throws SyntaxError  When an error occurred during compilation
-      * @throws RuntimeError When an error occurred during rendering
-      */
-     public function display($name, array $context = []): void
-     {
-         $this->load($name)->display($context);
-     }
-     /**
-      * Loads a template.
-      *
-      * @param string|TemplateWrapper $name The template name
-      *
-      * @throws LoaderError  When the template cannot be found
-      * @throws RuntimeError When a previously generated cache is corrupted
-      * @throws SyntaxError  When an error occurred during compilation
-      */
-     public function load($name): TemplateWrapper
-     {
-         if ($name instanceof TemplateWrapper) {
-             return $name;
-         }
-         return new TemplateWrapper($this, $this->loadTemplate($this->getTemplateClass($name), $name));
-     }
-     /**
-      * Loads a template internal representation.
-      *
-      * This method is for internal use only and should never be called
-      * directly.
-      *
-      * @param string $name  The template name
-      * @param int    $index The index if it is an embedded template
-      *
-      * @throws LoaderError  When the template cannot be found
-      * @throws RuntimeError When a previously generated cache is corrupted
-      * @throws SyntaxError  When an error occurred during compilation
-      *
-      * @internal
-      */
-     public function loadTemplate(string $cls, string $name, int $index = null): Template
-     {
-         $mainCls = $cls;
-         if (null !== $index) {
-             $cls .= '___'.$index;
-         }
-         if (isset($this->loadedTemplates[$cls])) {
-             return $this->loadedTemplates[$cls];
-         }
-         if (!class_exists($cls, false)) {
-             $key = $this->cache->generateKey($name, $mainCls);
-             if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) {
-                 $this->cache->load($key);
-             }
-             $source = null;
-             if (!class_exists($cls, false)) {
-                 $source = $this->getLoader()->getSourceContext($name);
-                 $content = $this->compileSource($source);
-                 $this->cache->write($key, $content);
-                 $this->cache->load($key);
-                 if (!class_exists($mainCls, false)) {
-                     /* Last line of defense if either $this->bcWriteCacheFile was used,
-                      * $this->cache is implemented as a no-op or we have a race condition
-                      * where the cache was cleared between the above calls to write to and load from
-                      * the cache.
-                      */
-                     eval('?>'.$content);
-                 }
-                 if (!class_exists($cls, false)) {
-                     throw new RuntimeError(sprintf('Failed to load Twig template "%s", index "%s": cache might be corrupted.', $name, $index), -1, $source);
-                 }
-             }
-         }
-         $this->extensionSet->initRuntime();
-         return $this->loadedTemplates[$cls] = new $cls($this);
-     }
-     /**
-      * Creates a template from source.
-      *
-      * This method should not be used as a generic way to load templates.
-      *
-      * @param string $template The template source
-      * @param string $name     An optional name of the template to be used in error messages
-      *
-      * @throws LoaderError When the template cannot be found
-      * @throws SyntaxError When an error occurred during compilation
-      */
-     public function createTemplate(string $template, string $name = null): TemplateWrapper
-     {
-         $hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $template, false);
-         if (null !== $name) {
-             $name = sprintf('%s (string template %s)', $name, $hash);
-         } else {
-             $name = sprintf('__string_template__%s', $hash);
-         }
-         $loader = new ChainLoader([
-             new ArrayLoader([$name => $template]),
-             $current = $this->getLoader(),
-         ]);
-         $this->setLoader($loader);
-         try {
-             return new TemplateWrapper($this, $this->loadTemplate($this->getTemplateClass($name), $name));
-         } finally {
-             $this->setLoader($current);
-         }
-     }
-     /**
-      * Returns true if the template is still fresh.
-      *
-      * Besides checking the loader for freshness information,
-      * this method also checks if the enabled extensions have
-      * not changed.
-      *
-      * @param int $time The last modification time of the cached template
-      */
-     public function isTemplateFresh(string $name, int $time): bool
-     {
-         return $this->extensionSet->getLastModified() <= $time && $this->getLoader()->isFresh($name, $time);
-     }
-     /**
-      * Tries to load a template consecutively from an array.
-      *
-      * Similar to load() but it also accepts instances of \Twig\Template and
-      * \Twig\TemplateWrapper, and an array of templates where each is tried to be loaded.
-      *
-      * @param string|TemplateWrapper|array $names A template or an array of templates to try consecutively
-      *
-      * @throws LoaderError When none of the templates can be found
-      * @throws SyntaxError When an error occurred during compilation
-      */
-     public function resolveTemplate($names): TemplateWrapper
-     {
-         if (!\is_array($names)) {
-             return $this->load($names);
-         }
-         $count = \count($names);
-         foreach ($names as $name) {
-             if ($name instanceof Template) {
-                 return $name;
-             }
-             if ($name instanceof TemplateWrapper) {
-                 return $name;
-             }
-             if (1 !== $count && !$this->getLoader()->exists($name)) {
-                 continue;
-             }
-             return $this->load($name);
-         }
-         throw new LoaderError(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
-     }
-     public function setLexer(Lexer $lexer)
-     {
-         $this->lexer = $lexer;
-     }
-     /**
-      * @throws SyntaxError When the code is syntactically wrong
-      */
-     public function tokenize(Source $source): TokenStream
-     {
-         if (null === $this->lexer) {
-             $this->lexer = new Lexer($this);
-         }
-         return $this->lexer->tokenize($source);
-     }
-     public function setParser(Parser $parser)
-     {
-         $this->parser = $parser;
-     }
-     /**
-      * Converts a token stream to a node tree.
-      *
-      * @throws SyntaxError When the token stream is syntactically or semantically wrong
-      */
-     public function parse(TokenStream $stream): ModuleNode
-     {
-         if (null === $this->parser) {
-             $this->parser = new Parser($this);
-         }
-         return $this->parser->parse($stream);
-     }
-     public function setCompiler(Compiler $compiler)
-     {
-         $this->compiler = $compiler;
-     }
-     /**
-      * Compiles a node and returns the PHP code.
-      */
-     public function compile(Node $node): string
-     {
-         if (null === $this->compiler) {
-             $this->compiler = new Compiler($this);
-         }
-         return $this->compiler->compile($node)->getSource();
-     }
-     /**
-      * Compiles a template source code.
-      *
-      * @throws SyntaxError When there was an error during tokenizing, parsing or compiling
-      */
-     public function compileSource(Source $source): string
-     {
-         try {
-             return $this->compile($this->parse($this->tokenize($source)));
-         } catch (Error $e) {
-             $e->setSourceContext($source);
-             throw $e;
-         } catch (\Exception $e) {
-             throw new SyntaxError(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e);
-         }
-     }
-     public function setLoader(LoaderInterface $loader)
-     {
-         $this->loader = $loader;
-     }
-     public function getLoader(): LoaderInterface
-     {
-         return $this->loader;
-     }
-     public function setCharset(string $charset)
-     {
-         if ('UTF8' === $charset = null === $charset ? null : strtoupper($charset)) {
-             // iconv on Windows requires "UTF-8" instead of "UTF8"
-             $charset = 'UTF-8';
-         }
-         $this->charset = $charset;
-     }
-     public function getCharset(): string
-     {
-         return $this->charset;
-     }
-     public function hasExtension(string $class): bool
-     {
-         return $this->extensionSet->hasExtension($class);
-     }
-     public function addRuntimeLoader(RuntimeLoaderInterface $loader)
-     {
-         $this->runtimeLoaders[] = $loader;
-     }
-     /**
-      * @template TExtension of ExtensionInterface
-      *
-      * @param class-string<TExtension> $class
-      *
-      * @return TExtension
-      */
-     public function getExtension(string $class): ExtensionInterface
-     {
-         return $this->extensionSet->getExtension($class);
-     }
-     /**
-      * Returns the runtime implementation of a Twig element (filter/function/tag/test).
-      *
-      * @template TRuntime of object
-      *
-      * @param class-string<TRuntime> $class A runtime class name
-      *
-      * @return TRuntime The runtime implementation
-      *
-      * @throws RuntimeError When the template cannot be found
-      */
-     public function getRuntime(string $class)
-     {
-         if (isset($this->runtimes[$class])) {
-             return $this->runtimes[$class];
-         }
-         foreach ($this->runtimeLoaders as $loader) {
-             if (null !== $runtime = $loader->load($class)) {
-                 return $this->runtimes[$class] = $runtime;
-             }
-         }
-         throw new RuntimeError(sprintf('Unable to load the "%s" runtime.', $class));
-     }
-     public function addExtension(ExtensionInterface $extension)
-     {
-         $this->extensionSet->addExtension($extension);
-         $this->updateOptionsHash();
-     }
-     /**
-      * @param ExtensionInterface[] $extensions An array of extensions
-      */
-     public function setExtensions(array $extensions)
-     {
-         $this->extensionSet->setExtensions($extensions);
-         $this->updateOptionsHash();
-     }
-     /**
-      * @return ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on)
-      */
-     public function getExtensions(): array
-     {
-         return $this->extensionSet->getExtensions();
-     }
-     public function addTokenParser(TokenParserInterface $parser)
-     {
-         $this->extensionSet->addTokenParser($parser);
-     }
-     /**
-      * @return TokenParserInterface[]
-      *
-      * @internal
-      */
-     public function getTokenParsers(): array
-     {
-         return $this->extensionSet->getTokenParsers();
-     }
-     /**
-      * @internal
-      */
-     public function getTokenParser(string $name): ?TokenParserInterface
-     {
-         return $this->extensionSet->getTokenParser($name);
-     }
-     public function registerUndefinedTokenParserCallback(callable $callable): void
-     {
-         $this->extensionSet->registerUndefinedTokenParserCallback($callable);
-     }
-     public function addNodeVisitor(NodeVisitorInterface $visitor)
-     {
-         $this->extensionSet->addNodeVisitor($visitor);
-     }
-     /**
-      * @return NodeVisitorInterface[]
-      *
-      * @internal
-      */
-     public function getNodeVisitors(): array
-     {
-         return $this->extensionSet->getNodeVisitors();
-     }
-     public function addFilter(TwigFilter $filter)
-     {
-         $this->extensionSet->addFilter($filter);
-     }
-     /**
-      * @internal
-      */
-     public function getFilter(string $name): ?TwigFilter
-     {
-         return $this->extensionSet->getFilter($name);
-     }
-     public function registerUndefinedFilterCallback(callable $callable): void
-     {
-         $this->extensionSet->registerUndefinedFilterCallback($callable);
-     }
-     /**
-      * Gets the registered Filters.
-      *
-      * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
-      *
-      * @return TwigFilter[]
-      *
-      * @see registerUndefinedFilterCallback
-      *
-      * @internal
-      */
-     public function getFilters(): array
-     {
-         return $this->extensionSet->getFilters();
-     }
-     public function addTest(TwigTest $test)
-     {
-         $this->extensionSet->addTest($test);
-     }
-     /**
-      * @return TwigTest[]
-      *
-      * @internal
-      */
-     public function getTests(): array
-     {
-         return $this->extensionSet->getTests();
-     }
-     /**
-      * @internal
-      */
-     public function getTest(string $name): ?TwigTest
-     {
-         return $this->extensionSet->getTest($name);
-     }
-     public function addFunction(TwigFunction $function)
-     {
-         $this->extensionSet->addFunction($function);
-     }
-     /**
-      * @internal
-      */
-     public function getFunction(string $name): ?TwigFunction
-     {
-         return $this->extensionSet->getFunction($name);
-     }
-     public function registerUndefinedFunctionCallback(callable $callable): void
-     {
-         $this->extensionSet->registerUndefinedFunctionCallback($callable);
-     }
-     /**
-      * Gets registered functions.
-      *
-      * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
-      *
-      * @return TwigFunction[]
-      *
-      * @see registerUndefinedFunctionCallback
-      *
-      * @internal
-      */
-     public function getFunctions(): array
-     {
-         return $this->extensionSet->getFunctions();
-     }
-     /**
-      * Registers a Global.
-      *
-      * New globals can be added before compiling or rendering a template;
-      * but after, you can only update existing globals.
-      *
-      * @param mixed $value The global value
-      */
-     public function addGlobal(string $name, $value)
-     {
-         if ($this->extensionSet->isInitialized() && !\array_key_exists($name, $this->getGlobals())) {
-             throw new \LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
-         }
-         if (null !== $this->resolvedGlobals) {
-             $this->resolvedGlobals[$name] = $value;
-         } else {
-             $this->globals[$name] = $value;
-         }
-     }
-     /**
-      * @internal
-      *
-      * @return array<string, mixed>
-      */
-     public function getGlobals(): array
-     {
-         if ($this->extensionSet->isInitialized()) {
-             if (null === $this->resolvedGlobals) {
-                 $this->resolvedGlobals = array_merge($this->extensionSet->getGlobals(), $this->globals);
-             }
-             return $this->resolvedGlobals;
-         }
-         return array_merge($this->extensionSet->getGlobals(), $this->globals);
-     }
-     public function mergeGlobals(array $context): array
-     {
-         // we don't use array_merge as the context being generally
-         // bigger than globals, this code is faster.
-         foreach ($this->getGlobals() as $key => $value) {
-             if (!\array_key_exists($key, $context)) {
-                 $context[$key] = $value;
-             }
-         }
-         return $context;
-     }
-     /**
-      * @internal
-      *
-      * @return array<string, array{precedence: int, class: class-string<AbstractUnary>}>
-      */
-     public function getUnaryOperators(): array
-     {
-         return $this->extensionSet->getUnaryOperators();
-     }
-     /**
-      * @internal
-      *
-      * @return array<string, array{precedence: int, class: class-string<AbstractBinary>, associativity: ExpressionParser::OPERATOR_*}>
-      */
-     public function getBinaryOperators(): array
-     {
-         return $this->extensionSet->getBinaryOperators();
-     }
-     private function updateOptionsHash(): void
-     {
-         $this->optionsHash = implode(':', [
-             $this->extensionSet->getSignature(),
-             \PHP_MAJOR_VERSION,
-             \PHP_MINOR_VERSION,
-             self::VERSION,
-             (int) $this->debug,
-             (int) $this->strictVariables,
-         ]);
-     }
- }
-