vendor/twig/twig/src/ExtensionSet.php line 184

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\Error\RuntimeError;
  12. use Twig\Extension\ExtensionInterface;
  13. use Twig\Extension\GlobalsInterface;
  14. use Twig\Extension\InitRuntimeInterface;
  15. use Twig\Extension\StagingExtension;
  16. use Twig\NodeVisitor\NodeVisitorInterface;
  17. use Twig\TokenParser\TokenParserInterface;
  18. /**
  19.  * @author Fabien Potencier <fabien@symfony.com>
  20.  *
  21.  * @internal
  22.  */
  23. final class ExtensionSet
  24. {
  25.     private $extensions;
  26.     private $initialized false;
  27.     private $runtimeInitialized false;
  28.     private $staging;
  29.     private $parsers;
  30.     private $visitors;
  31.     private $filters;
  32.     private $tests;
  33.     private $functions;
  34.     private $unaryOperators;
  35.     private $binaryOperators;
  36.     private $globals;
  37.     private $functionCallbacks = [];
  38.     private $filterCallbacks = [];
  39.     private $lastModified 0;
  40.     public function __construct()
  41.     {
  42.         $this->staging = new StagingExtension();
  43.     }
  44.     /**
  45.      * Initializes the runtime environment.
  46.      *
  47.      * @deprecated since Twig 2.7
  48.      */
  49.     public function initRuntime(Environment $env)
  50.     {
  51.         if ($this->runtimeInitialized) {
  52.             return;
  53.         }
  54.         $this->runtimeInitialized true;
  55.         foreach ($this->extensions as $extension) {
  56.             if ($extension instanceof InitRuntimeInterface) {
  57.                 $extension->initRuntime($env);
  58.             }
  59.         }
  60.     }
  61.     public function hasExtension(string $class): bool
  62.     {
  63.         $class ltrim($class'\\');
  64.         if (!isset($this->extensions[$class]) && class_exists($classfalse)) {
  65.             // For BC/FC with namespaced aliases
  66.             $class = (new \ReflectionClass($class))->name;
  67.         }
  68.         return isset($this->extensions[$class]);
  69.     }
  70.     public function getExtension(string $class): ExtensionInterface
  71.     {
  72.         $class ltrim($class'\\');
  73.         if (!isset($this->extensions[$class]) && class_exists($classfalse)) {
  74.             // For BC/FC with namespaced aliases
  75.             $class = (new \ReflectionClass($class))->name;
  76.         }
  77.         if (!isset($this->extensions[$class])) {
  78.             throw new RuntimeError(sprintf('The "%s" extension is not enabled.'$class));
  79.         }
  80.         return $this->extensions[$class];
  81.     }
  82.     /**
  83.      * @param ExtensionInterface[] $extensions
  84.      */
  85.     public function setExtensions(array $extensions)
  86.     {
  87.         foreach ($extensions as $extension) {
  88.             $this->addExtension($extension);
  89.         }
  90.     }
  91.     /**
  92.      * @return ExtensionInterface[]
  93.      */
  94.     public function getExtensions(): array
  95.     {
  96.         return $this->extensions;
  97.     }
  98.     public function getSignature(): string
  99.     {
  100.         return json_encode(array_keys($this->extensions));
  101.     }
  102.     public function isInitialized(): bool
  103.     {
  104.         return $this->initialized || $this->runtimeInitialized;
  105.     }
  106.     public function getLastModified(): int
  107.     {
  108.         if (!== $this->lastModified) {
  109.             return $this->lastModified;
  110.         }
  111.         foreach ($this->extensions as $extension) {
  112.             $r = new \ReflectionObject($extension);
  113.             if (file_exists($r->getFileName()) && ($extensionTime filemtime($r->getFileName())) > $this->lastModified) {
  114.                 $this->lastModified $extensionTime;
  115.             }
  116.         }
  117.         return $this->lastModified;
  118.     }
  119.     public function addExtension(ExtensionInterface $extension)
  120.     {
  121.         $class = \get_class($extension);
  122.         if ($this->initialized) {
  123.             throw new \LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.'$class));
  124.         }
  125.         if (isset($this->extensions[$class])) {
  126.             throw new \LogicException(sprintf('Unable to register extension "%s" as it is already registered.'$class));
  127.         }
  128.         // For BC/FC with namespaced aliases
  129.         $class = (new \ReflectionClass($class))->name;
  130.         $this->extensions[$class] = $extension;
  131.     }
  132.     public function addFunction(TwigFunction $function)
  133.     {
  134.         if ($this->initialized) {
  135.             throw new \LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.'$function->getName()));
  136.         }
  137.         $this->staging->addFunction($function);
  138.     }
  139.     /**
  140.      * @return TwigFunction[]
  141.      */
  142.     public function getFunctions(): array
  143.     {
  144.         if (!$this->initialized) {
  145.             $this->initExtensions();
  146.         }
  147.         return $this->functions;
  148.     }
  149.     /**
  150.      * @return TwigFunction|false
  151.      */
  152.     public function getFunction(string $name)
  153.     {
  154.         if (!$this->initialized) {
  155.             $this->initExtensions();
  156.         }
  157.         if (isset($this->functions[$name])) {
  158.             return $this->functions[$name];
  159.         }
  160.         foreach ($this->functions as $pattern => $function) {
  161.             $pattern str_replace('\\*''(.*?)'preg_quote($pattern'#'), $count);
  162.             if ($count && preg_match('#^'.$pattern.'$#'$name$matches)) {
  163.                 array_shift($matches);
  164.                 $function->setArguments($matches);
  165.                 return $function;
  166.             }
  167.         }
  168.         foreach ($this->functionCallbacks as $callback) {
  169.             if (false !== $function $callback($name)) {
  170.                 return $function;
  171.             }
  172.         }
  173.         return false;
  174.     }
  175.     public function registerUndefinedFunctionCallback(callable $callable)
  176.     {
  177.         $this->functionCallbacks[] = $callable;
  178.     }
  179.     public function addFilter(TwigFilter $filter)
  180.     {
  181.         if ($this->initialized) {
  182.             throw new \LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.'$filter->getName()));
  183.         }
  184.         $this->staging->addFilter($filter);
  185.     }
  186.     /**
  187.      * @return TwigFilter[]
  188.      */
  189.     public function getFilters(): array
  190.     {
  191.         if (!$this->initialized) {
  192.             $this->initExtensions();
  193.         }
  194.         return $this->filters;
  195.     }
  196.     /**
  197.      * @return TwigFilter|false
  198.      */
  199.     public function getFilter(string $name)
  200.     {
  201.         if (!$this->initialized) {
  202.             $this->initExtensions();
  203.         }
  204.         if (isset($this->filters[$name])) {
  205.             return $this->filters[$name];
  206.         }
  207.         foreach ($this->filters as $pattern => $filter) {
  208.             $pattern str_replace('\\*''(.*?)'preg_quote($pattern'#'), $count);
  209.             if ($count && preg_match('#^'.$pattern.'$#'$name$matches)) {
  210.                 array_shift($matches);
  211.                 $filter->setArguments($matches);
  212.                 return $filter;
  213.             }
  214.         }
  215.         foreach ($this->filterCallbacks as $callback) {
  216.             if (false !== $filter $callback($name)) {
  217.                 return $filter;
  218.             }
  219.         }
  220.         return false;
  221.     }
  222.     public function registerUndefinedFilterCallback(callable $callable)
  223.     {
  224.         $this->filterCallbacks[] = $callable;
  225.     }
  226.     public function addNodeVisitor(NodeVisitorInterface $visitor)
  227.     {
  228.         if ($this->initialized) {
  229.             throw new \LogicException('Unable to add a node visitor as extensions have already been initialized.');
  230.         }
  231.         $this->staging->addNodeVisitor($visitor);
  232.     }
  233.     /**
  234.      * @return NodeVisitorInterface[]
  235.      */
  236.     public function getNodeVisitors(): array
  237.     {
  238.         if (!$this->initialized) {
  239.             $this->initExtensions();
  240.         }
  241.         return $this->visitors;
  242.     }
  243.     public function addTokenParser(TokenParserInterface $parser)
  244.     {
  245.         if ($this->initialized) {
  246.             throw new \LogicException('Unable to add a token parser as extensions have already been initialized.');
  247.         }
  248.         $this->staging->addTokenParser($parser);
  249.     }
  250.     /**
  251.      * @return TokenParserInterface[]
  252.      */
  253.     public function getTokenParsers(): array
  254.     {
  255.         if (!$this->initialized) {
  256.             $this->initExtensions();
  257.         }
  258.         return $this->parsers;
  259.     }
  260.     public function getGlobals(): array
  261.     {
  262.         if (null !== $this->globals) {
  263.             return $this->globals;
  264.         }
  265.         $globals = [];
  266.         foreach ($this->extensions as $extension) {
  267.             if (!$extension instanceof GlobalsInterface) {
  268.                 continue;
  269.             }
  270.             $extGlobals $extension->getGlobals();
  271.             if (!\is_array($extGlobals)) {
  272.                 throw new \UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', \get_class($extension)));
  273.             }
  274.             $globals array_merge($globals$extGlobals);
  275.         }
  276.         if ($this->initialized) {
  277.             $this->globals $globals;
  278.         }
  279.         return $globals;
  280.     }
  281.     public function addTest(TwigTest $test)
  282.     {
  283.         if ($this->initialized) {
  284.             throw new \LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.'$test->getName()));
  285.         }
  286.         $this->staging->addTest($test);
  287.     }
  288.     /**
  289.      * @return TwigTest[]
  290.      */
  291.     public function getTests(): array
  292.     {
  293.         if (!$this->initialized) {
  294.             $this->initExtensions();
  295.         }
  296.         return $this->tests;
  297.     }
  298.     /**
  299.      * @return TwigTest|false
  300.      */
  301.     public function getTest(string $name)
  302.     {
  303.         if (!$this->initialized) {
  304.             $this->initExtensions();
  305.         }
  306.         if (isset($this->tests[$name])) {
  307.             return $this->tests[$name];
  308.         }
  309.         foreach ($this->tests as $pattern => $test) {
  310.             $pattern str_replace('\\*''(.*?)'preg_quote($pattern'#'), $count);
  311.             if ($count) {
  312.                 if (preg_match('#^'.$pattern.'$#'$name$matches)) {
  313.                     array_shift($matches);
  314.                     $test->setArguments($matches);
  315.                     return $test;
  316.                 }
  317.             }
  318.         }
  319.         return false;
  320.     }
  321.     public function getUnaryOperators(): array
  322.     {
  323.         if (!$this->initialized) {
  324.             $this->initExtensions();
  325.         }
  326.         return $this->unaryOperators;
  327.     }
  328.     public function getBinaryOperators(): array
  329.     {
  330.         if (!$this->initialized) {
  331.             $this->initExtensions();
  332.         }
  333.         return $this->binaryOperators;
  334.     }
  335.     private function initExtensions()
  336.     {
  337.         $this->parsers = [];
  338.         $this->filters = [];
  339.         $this->functions = [];
  340.         $this->tests = [];
  341.         $this->visitors = [];
  342.         $this->unaryOperators = [];
  343.         $this->binaryOperators = [];
  344.         foreach ($this->extensions as $extension) {
  345.             $this->initExtension($extension);
  346.         }
  347.         $this->initExtension($this->staging);
  348.         // Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception
  349.         $this->initialized true;
  350.     }
  351.     private function initExtension(ExtensionInterface $extension)
  352.     {
  353.         // filters
  354.         foreach ($extension->getFilters() as $filter) {
  355.             $this->filters[$filter->getName()] = $filter;
  356.         }
  357.         // functions
  358.         foreach ($extension->getFunctions() as $function) {
  359.             $this->functions[$function->getName()] = $function;
  360.         }
  361.         // tests
  362.         foreach ($extension->getTests() as $test) {
  363.             $this->tests[$test->getName()] = $test;
  364.         }
  365.         // token parsers
  366.         foreach ($extension->getTokenParsers() as $parser) {
  367.             if (!$parser instanceof TokenParserInterface) {
  368.                 throw new \LogicException('getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.');
  369.             }
  370.             $this->parsers[] = $parser;
  371.         }
  372.         // node visitors
  373.         foreach ($extension->getNodeVisitors() as $visitor) {
  374.             $this->visitors[] = $visitor;
  375.         }
  376.         // operators
  377.         if ($operators $extension->getOperators()) {
  378.             if (!\is_array($operators)) {
  379.                 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)));
  380.             }
  381.             if (!== \count($operators)) {
  382.                 throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', \get_class($extension), \count($operators)));
  383.             }
  384.             $this->unaryOperators array_merge($this->unaryOperators$operators[0]);
  385.             $this->binaryOperators array_merge($this->binaryOperators$operators[1]);
  386.         }
  387.     }
  388. }
  389. class_alias('Twig\ExtensionSet''Twig_ExtensionSet');