/vendor/twig/twig/lib/Twig/ExtensionSet.php

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