PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/Twig/ExtensionSet.php

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