ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Environment.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of Twig.
5  *
6  * (c) Fabien Potencier
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11 
18 {
19  const VERSION = '1.35.3';
20  const VERSION_ID = 13503;
21  const MAJOR_VERSION = 1;
22  const MINOR_VERSION = 35;
23  const RELEASE_VERSION = 3;
24  const EXTRA_VERSION = '';
25 
26  protected $charset;
27  protected $loader;
28  protected $debug;
29  protected $autoReload;
30  protected $cache;
31  protected $lexer;
32  protected $parser;
33  protected $compiler;
34  protected $baseTemplateClass;
35  protected $extensions;
36  protected $parsers;
37  protected $visitors;
38  protected $filters;
39  protected $tests;
40  protected $functions;
41  protected $globals;
42  protected $runtimeInitialized = false;
43  protected $extensionInitialized = false;
44  protected $loadedTemplates;
45  protected $strictVariables;
46  protected $unaryOperators;
47  protected $binaryOperators;
48  protected $templateClassPrefix = '__TwigTemplate_';
49  protected $functionCallbacks = array();
50  protected $filterCallbacks = array();
51  protected $staging;
52 
53  private $originalCache;
54  private $bcWriteCacheFile = false;
55  private $bcGetCacheFilename = false;
57  private $extensionsByClass = array();
58  private $runtimeLoaders = array();
59  private $runtimes = array();
60  private $optionsHash;
61  private $loading = array();
62 
101  public function __construct(Twig_LoaderInterface $loader = null, $options = array())
102  {
103  if (null !== $loader) {
104  $this->setLoader($loader);
105  } else {
106  @trigger_error('Not passing a Twig_LoaderInterface as the first constructor argument of Twig_Environment is deprecated since version 1.21.', E_USER_DEPRECATED);
107  }
108 
109  $options = array_merge(array(
110  'debug' => false,
111  'charset' => 'UTF-8',
112  'base_template_class' => 'Twig_Template',
113  'strict_variables' => false,
114  'autoescape' => 'html',
115  'cache' => false,
116  'auto_reload' => null,
117  'optimizations' => -1,
118  ), $options);
119 
120  $this->debug = (bool) $options['debug'];
121  $this->charset = strtoupper($options['charset']);
122  $this->baseTemplateClass = $options['base_template_class'];
123  $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
124  $this->strictVariables = (bool) $options['strict_variables'];
125  $this->setCache($options['cache']);
126 
127  $this->addExtension(new Twig_Extension_Core());
128  $this->addExtension(new Twig_Extension_Escaper($options['autoescape']));
129  $this->addExtension(new Twig_Extension_Optimizer($options['optimizations']));
130  $this->staging = new Twig_Extension_Staging();
131 
132  // For BC
133  if (is_string($this->originalCache)) {
134  $r = new ReflectionMethod($this, 'writeCacheFile');
135  if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
136  @trigger_error('The Twig_Environment::writeCacheFile method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
137 
138  $this->bcWriteCacheFile = true;
139  }
140 
141  $r = new ReflectionMethod($this, 'getCacheFilename');
142  if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
143  @trigger_error('The Twig_Environment::getCacheFilename method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
144 
145  $this->bcGetCacheFilename = true;
146  }
147  }
148  }
149 
155  public function getBaseTemplateClass()
156  {
158  }
159 
165  public function setBaseTemplateClass($class)
166  {
167  $this->baseTemplateClass = $class;
168  $this->updateOptionsHash();
169  }
170 
174  public function enableDebug()
175  {
176  $this->debug = true;
177  $this->updateOptionsHash();
178  }
179 
183  public function disableDebug()
184  {
185  $this->debug = false;
186  $this->updateOptionsHash();
187  }
188 
194  public function isDebug()
195  {
196  return $this->debug;
197  }
198 
202  public function enableAutoReload()
203  {
204  $this->autoReload = true;
205  }
206 
210  public function disableAutoReload()
211  {
212  $this->autoReload = false;
213  }
214 
220  public function isAutoReload()
221  {
222  return $this->autoReload;
223  }
224 
228  public function enableStrictVariables()
229  {
230  $this->strictVariables = true;
231  $this->updateOptionsHash();
232  }
233 
237  public function disableStrictVariables()
238  {
239  $this->strictVariables = false;
240  $this->updateOptionsHash();
241  }
242 
248  public function isStrictVariables()
249  {
250  return $this->strictVariables;
251  }
252 
262  public function getCache($original = true)
263  {
264  return $original ? $this->originalCache : $this->cache;
265  }
266 
274  public function setCache($cache)
275  {
276  if (is_string($cache)) {
277  $this->originalCache = $cache;
278  $this->cache = new Twig_Cache_Filesystem($cache);
279  } elseif (false === $cache) {
280  $this->originalCache = $cache;
281  $this->cache = new Twig_Cache_Null();
282  } elseif (null === $cache) {
283  @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);
284  $this->originalCache = false;
285  $this->cache = new Twig_Cache_Null();
286  } elseif ($cache instanceof Twig_CacheInterface) {
287  $this->originalCache = $this->cache = $cache;
288  } else {
289  throw new LogicException(sprintf('Cache can only be a string, false, or a Twig_CacheInterface implementation.'));
290  }
291  }
292 
302  public function getCacheFilename($name)
303  {
304  @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
305 
306  $key = $this->cache->generateKey($name, $this->getTemplateClass($name));
307 
308  return !$key ? false : $key;
309  }
310 
328  public function getTemplateClass($name, $index = null)
329  {
330  $key = $this->getLoader()->getCacheKey($name).$this->optionsHash;
331 
332  return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index);
333  }
334 
342  public function getTemplateClassPrefix()
343  {
344  @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
345 
347  }
348 
361  public function render($name, array $context = array())
362  {
363  return $this->loadTemplate($name)->render($context);
364  }
365 
376  public function display($name, array $context = array())
377  {
378  $this->loadTemplate($name)->display($context);
379  }
380 
392  public function load($name)
393  {
394  if ($name instanceof Twig_TemplateWrapper) {
395  return $name;
396  }
397 
398  if ($name instanceof Twig_Template) {
399  return new Twig_TemplateWrapper($this, $name);
400  }
401 
402  return new Twig_TemplateWrapper($this, $this->loadTemplate($name));
403  }
404 
422  public function loadTemplate($name, $index = null)
423  {
424  $cls = $mainCls = $this->getTemplateClass($name);
425  if (null !== $index) {
426  $cls .= '_'.$index;
427  }
428 
429  if (isset($this->loadedTemplates[$cls])) {
430  return $this->loadedTemplates[$cls];
431  }
432 
433  if (!class_exists($cls, false)) {
434  if ($this->bcGetCacheFilename) {
435  $key = $this->getCacheFilename($name);
436  } else {
437  $key = $this->cache->generateKey($name, $mainCls);
438  }
439 
440  if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) {
441  $this->cache->load($key);
442  }
443 
444  if (!class_exists($cls, false)) {
445  $loader = $this->getLoader();
446  if (!$loader instanceof Twig_SourceContextLoaderInterface) {
447  $source = new Twig_Source($loader->getSource($name), $name);
448  } else {
449  $source = $loader->getSourceContext($name);
450  }
451 
452  $content = $this->compileSource($source);
453 
454  if ($this->bcWriteCacheFile) {
455  $this->writeCacheFile($key, $content);
456  } else {
457  $this->cache->write($key, $content);
458  $this->cache->load($key);
459  }
460 
461  if (!class_exists($mainCls, false)) {
462  /* Last line of defense if either $this->bcWriteCacheFile was used,
463  * $this->cache is implemented as a no-op or we have a race condition
464  * where the cache was cleared between the above calls to write to and load from
465  * the cache.
466  */
467  eval('?>'.$content);
468  }
469  }
470 
471  if (!class_exists($cls, false)) {
472  throw new Twig_Error_Runtime(sprintf('Failed to load Twig template "%s", index "%s": cache is corrupted.', $name, $index), -1, $source);
473  }
474  }
475 
476  if (!$this->runtimeInitialized) {
477  $this->initRuntime();
478  }
479 
480  if (isset($this->loading[$cls])) {
481  throw new Twig_Error_Runtime(sprintf('Circular reference detected for Twig template "%s", path: %s.', $name, implode(' -> ', array_merge($this->loading, array($name)))));
482  }
483 
484  $this->loading[$cls] = $name;
485 
486  try {
487  $this->loadedTemplates[$cls] = new $cls($this);
488  unset($this->loading[$cls]);
489  } catch (\Exception $e) {
490  unset($this->loading[$cls]);
491 
492  throw $e;
493  }
494 
495  return $this->loadedTemplates[$cls];
496  }
497 
510  public function createTemplate($template)
511  {
512  $name = sprintf('__string_template__%s', hash('sha256', $template, false));
513 
514  $loader = new Twig_Loader_Chain(array(
515  new Twig_Loader_Array(array($name => $template)),
516  $current = $this->getLoader(),
517  ));
518 
519  $this->setLoader($loader);
520  try {
521  $template = $this->loadTemplate($name);
522  } catch (Exception $e) {
523  $this->setLoader($current);
524 
525  throw $e;
526  } catch (Throwable $e) {
527  $this->setLoader($current);
528 
529  throw $e;
530  }
531  $this->setLoader($current);
532 
533  return $template;
534  }
535 
548  public function isTemplateFresh($name, $time)
549  {
550  if (0 === $this->lastModifiedExtension) {
551  foreach ($this->extensions as $extension) {
552  $r = new ReflectionObject($extension);
553  if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModifiedExtension) {
554  $this->lastModifiedExtension = $extensionTime;
555  }
556  }
557  }
558 
559  return $this->lastModifiedExtension <= $time && $this->getLoader()->isFresh($name, $time);
560  }
561 
575  public function resolveTemplate($names)
576  {
577  if (!is_array($names)) {
578  $names = array($names);
579  }
580 
581  foreach ($names as $name) {
582  if ($name instanceof Twig_Template) {
583  return $name;
584  }
585 
586  if ($name instanceof Twig_TemplateWrapper) {
587  return $name;
588  }
589 
590  try {
591  return $this->loadTemplate($name);
592  } catch (Twig_Error_Loader $e) {
593  }
594  }
595 
596  if (1 === count($names)) {
597  throw $e;
598  }
599 
600  throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
601  }
602 
608  public function clearTemplateCache()
609  {
610  @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);
611 
612  $this->loadedTemplates = array();
613  }
614 
620  public function clearCacheFiles()
621  {
622  @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
623 
624  if (is_string($this->originalCache)) {
625  foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->originalCache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
626  if ($file->isFile()) {
627  @unlink($file->getPathname());
628  }
629  }
630  }
631  }
632 
640  public function getLexer()
641  {
642  @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
643 
644  if (null === $this->lexer) {
645  $this->lexer = new Twig_Lexer($this);
646  }
647 
648  return $this->lexer;
649  }
650 
652  {
653  $this->lexer = $lexer;
654  }
655 
666  public function tokenize($source, $name = null)
667  {
668  if (!$source instanceof Twig_Source) {
669  @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);
670  $source = new Twig_Source($source, $name);
671  }
672 
673  if (null === $this->lexer) {
674  $this->lexer = new Twig_Lexer($this);
675  }
676 
677  return $this->lexer->tokenize($source);
678  }
679 
687  public function getParser()
688  {
689  @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
690 
691  if (null === $this->parser) {
692  $this->parser = new Twig_Parser($this);
693  }
694 
695  return $this->parser;
696  }
697 
699  {
700  $this->parser = $parser;
701  }
702 
710  public function parse(Twig_TokenStream $stream)
711  {
712  if (null === $this->parser) {
713  $this->parser = new Twig_Parser($this);
714  }
715 
716  return $this->parser->parse($stream);
717  }
718 
726  public function getCompiler()
727  {
728  @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
729 
730  if (null === $this->compiler) {
731  $this->compiler = new Twig_Compiler($this);
732  }
733 
734  return $this->compiler;
735  }
736 
738  {
739  $this->compiler = $compiler;
740  }
741 
747  public function compile(Twig_NodeInterface $node)
748  {
749  if (null === $this->compiler) {
750  $this->compiler = new Twig_Compiler($this);
751  }
752 
753  return $this->compiler->compile($node)->getSource();
754  }
755 
766  public function compileSource($source, $name = null)
767  {
768  if (!$source instanceof Twig_Source) {
769  @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);
770  $source = new Twig_Source($source, $name);
771  }
772 
773  try {
774  return $this->compile($this->parse($this->tokenize($source)));
775  } catch (Twig_Error $e) {
777  throw $e;
778  } catch (Exception $e) {
779  throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e);
780  }
781  }
782 
784  {
785  if (!$loader instanceof Twig_SourceContextLoaderInterface && 0 !== strpos(get_class($loader), 'Mock_')) {
786  @trigger_error(sprintf('Twig loader "%s" should implement Twig_SourceContextLoaderInterface since version 1.27.', get_class($loader)), E_USER_DEPRECATED);
787  }
788 
789  $this->loader = $loader;
790  }
791 
797  public function getLoader()
798  {
799  if (null === $this->loader) {
800  throw new LogicException('You must set a loader first.');
801  }
802 
803  return $this->loader;
804  }
805 
811  public function setCharset($charset)
812  {
813  $this->charset = strtoupper($charset);
814  }
815 
821  public function getCharset()
822  {
823  return $this->charset;
824  }
825 
831  public function initRuntime()
832  {
833  $this->runtimeInitialized = true;
834 
835  foreach ($this->getExtensions() as $name => $extension) {
836  if (!$extension instanceof Twig_Extension_InitRuntimeInterface) {
837  $m = new ReflectionMethod($extension, 'initRuntime');
838 
839  if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
840  @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);
841  }
842  }
843 
844  $extension->initRuntime($this);
845  }
846  }
847 
855  public function hasExtension($class)
856  {
857  $class = ltrim($class, '\\');
858  if (!isset($this->extensionsByClass[$class]) && class_exists($class, false)) {
859  // For BC/FC with namespaced aliases
860  $class = new ReflectionClass($class);
861  $class = $class->name;
862  }
863 
864  if (isset($this->extensions[$class])) {
865  if ($class !== get_class($this->extensions[$class])) {
866  @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);
867  }
868 
869  return true;
870  }
871 
872  return isset($this->extensionsByClass[$class]);
873  }
874 
879  {
880  $this->runtimeLoaders[] = $loader;
881  }
882 
890  public function getExtension($class)
891  {
892  $class = ltrim($class, '\\');
893  if (!isset($this->extensionsByClass[$class]) && class_exists($class, false)) {
894  // For BC/FC with namespaced aliases
895  $class = new ReflectionClass($class);
896  $class = $class->name;
897  }
898 
899  if (isset($this->extensions[$class])) {
900  if ($class !== get_class($this->extensions[$class])) {
901  @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);
902  }
903 
904  return $this->extensions[$class];
905  }
906 
907  if (!isset($this->extensionsByClass[$class])) {
908  throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $class));
909  }
910 
911  return $this->extensionsByClass[$class];
912  }
913 
923  public function getRuntime($class)
924  {
925  if (isset($this->runtimes[$class])) {
926  return $this->runtimes[$class];
927  }
928 
929  foreach ($this->runtimeLoaders as $loader) {
930  if (null !== $runtime = $loader->load($class)) {
931  return $this->runtimes[$class] = $runtime;
932  }
933  }
934 
935  throw new Twig_Error_Runtime(sprintf('Unable to load the "%s" runtime.', $class));
936  }
937 
938  public function addExtension(Twig_ExtensionInterface $extension)
939  {
940  if ($this->extensionInitialized) {
941  throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName()));
942  }
943 
944  $class = get_class($extension);
945  if ($class !== $extension->getName()) {
946  if (isset($this->extensions[$extension->getName()])) {
947  unset($this->extensions[$extension->getName()], $this->extensionsByClass[$class]);
948  @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);
949  }
950  }
951 
952  $this->lastModifiedExtension = 0;
953  $this->extensionsByClass[$class] = $extension;
954  $this->extensions[$extension->getName()] = $extension;
955  $this->updateOptionsHash();
956  }
957 
967  public function removeExtension($name)
968  {
969  @trigger_error(sprintf('The %s method is deprecated since version 1.12 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
970 
971  if ($this->extensionInitialized) {
972  throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
973  }
974 
975  $class = ltrim($name, '\\');
976  if (!isset($this->extensionsByClass[$class]) && class_exists($class, false)) {
977  // For BC/FC with namespaced aliases
978  $class = new ReflectionClass($class);
979  $class = $class->name;
980  }
981 
982  if (isset($this->extensions[$class])) {
983  if ($class !== get_class($this->extensions[$class])) {
984  @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);
985  }
986 
987  unset($this->extensions[$class]);
988  }
989 
990  unset($this->extensions[$class]);
991  $this->updateOptionsHash();
992  }
993 
999  public function setExtensions(array $extensions)
1000  {
1001  foreach ($extensions as $extension) {
1002  $this->addExtension($extension);
1003  }
1004  }
1005 
1011  public function getExtensions()
1012  {
1013  return $this->extensions;
1014  }
1015 
1017  {
1018  if ($this->extensionInitialized) {
1019  throw new LogicException('Unable to add a token parser as extensions have already been initialized.');
1020  }
1021 
1022  $this->staging->addTokenParser($parser);
1023  }
1024 
1032  public function getTokenParsers()
1033  {
1034  if (!$this->extensionInitialized) {
1035  $this->initExtensions();
1036  }
1037 
1038  return $this->parsers;
1039  }
1040 
1050  public function getTags()
1051  {
1052  $tags = array();
1053  foreach ($this->getTokenParsers()->getParsers() as $parser) {
1054  if ($parser instanceof Twig_TokenParserInterface) {
1055  $tags[$parser->getTag()] = $parser;
1056  }
1057  }
1058 
1059  return $tags;
1060  }
1061 
1062  public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
1063  {
1064  if ($this->extensionInitialized) {
1065  throw new LogicException('Unable to add a node visitor as extensions have already been initialized.');
1066  }
1067 
1068  $this->staging->addNodeVisitor($visitor);
1069  }
1070 
1078  public function getNodeVisitors()
1079  {
1080  if (!$this->extensionInitialized) {
1081  $this->initExtensions();
1082  }
1083 
1084  return $this->visitors;
1085  }
1086 
1093  public function addFilter($name, $filter = null)
1094  {
1095  if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) {
1096  throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter.');
1097  }
1098 
1099  if ($name instanceof Twig_SimpleFilter) {
1100  $filter = $name;
1101  $name = $filter->getName();
1102  } else {
1103  @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);
1104  }
1105 
1106  if ($this->extensionInitialized) {
1107  throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $name));
1108  }
1109 
1110  $this->staging->addFilter($name, $filter);
1111  }
1112 
1125  public function getFilter($name)
1126  {
1127  if (!$this->extensionInitialized) {
1128  $this->initExtensions();
1129  }
1130 
1131  if (isset($this->filters[$name])) {
1132  return $this->filters[$name];
1133  }
1134 
1135  foreach ($this->filters as $pattern => $filter) {
1136  $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
1137 
1138  if ($count) {
1139  if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
1140  array_shift($matches);
1141  $filter->setArguments($matches);
1142 
1143  return $filter;
1144  }
1145  }
1146  }
1147 
1148  foreach ($this->filterCallbacks as $callback) {
1149  if (false !== $filter = call_user_func($callback, $name)) {
1150  return $filter;
1151  }
1152  }
1153 
1154  return false;
1155  }
1156 
1157  public function registerUndefinedFilterCallback($callable)
1158  {
1159  $this->filterCallbacks[] = $callable;
1160  }
1161 
1173  public function getFilters()
1174  {
1175  if (!$this->extensionInitialized) {
1176  $this->initExtensions();
1177  }
1178 
1179  return $this->filters;
1180  }
1181 
1188  public function addTest($name, $test = null)
1189  {
1190  if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) {
1191  throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest.');
1192  }
1193 
1194  if ($name instanceof Twig_SimpleTest) {
1195  $test = $name;
1196  $name = $test->getName();
1197  } else {
1198  @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);
1199  }
1200 
1201  if ($this->extensionInitialized) {
1202  throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name));
1203  }
1204 
1205  $this->staging->addTest($name, $test);
1206  }
1207 
1215  public function getTests()
1216  {
1217  if (!$this->extensionInitialized) {
1218  $this->initExtensions();
1219  }
1220 
1221  return $this->tests;
1222  }
1223 
1233  public function getTest($name)
1234  {
1235  if (!$this->extensionInitialized) {
1236  $this->initExtensions();
1237  }
1238 
1239  if (isset($this->tests[$name])) {
1240  return $this->tests[$name];
1241  }
1242 
1243  return false;
1244  }
1245 
1252  public function addFunction($name, $function = null)
1253  {
1254  if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) {
1255  throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction.');
1256  }
1257 
1258  if ($name instanceof Twig_SimpleFunction) {
1259  $function = $name;
1260  $name = $function->getName();
1261  } else {
1262  @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);
1263  }
1264 
1265  if ($this->extensionInitialized) {
1266  throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name));
1267  }
1268 
1269  $this->staging->addFunction($name, $function);
1270  }
1271 
1284  public function getFunction($name)
1285  {
1286  if (!$this->extensionInitialized) {
1287  $this->initExtensions();
1288  }
1289 
1290  if (isset($this->functions[$name])) {
1291  return $this->functions[$name];
1292  }
1293 
1294  foreach ($this->functions as $pattern => $function) {
1295  $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
1296 
1297  if ($count) {
1298  if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
1299  array_shift($matches);
1300  $function->setArguments($matches);
1301 
1302  return $function;
1303  }
1304  }
1305  }
1306 
1307  foreach ($this->functionCallbacks as $callback) {
1308  if (false !== $function = call_user_func($callback, $name)) {
1309  return $function;
1310  }
1311  }
1312 
1313  return false;
1314  }
1315 
1316  public function registerUndefinedFunctionCallback($callable)
1317  {
1318  $this->functionCallbacks[] = $callable;
1319  }
1320 
1332  public function getFunctions()
1333  {
1334  if (!$this->extensionInitialized) {
1335  $this->initExtensions();
1336  }
1337 
1338  return $this->functions;
1339  }
1340 
1350  public function addGlobal($name, $value)
1351  {
1352  if ($this->extensionInitialized || $this->runtimeInitialized) {
1353  if (null === $this->globals) {
1354  $this->globals = $this->initGlobals();
1355  }
1356 
1357  if (!array_key_exists($name, $this->globals)) {
1358  // The deprecation notice must be turned into the following exception in Twig 2.0
1359  @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);
1360  //throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
1361  }
1362  }
1363 
1364  if ($this->extensionInitialized || $this->runtimeInitialized) {
1365  // update the value
1366  $this->globals[$name] = $value;
1367  } else {
1368  $this->staging->addGlobal($name, $value);
1369  }
1370  }
1371 
1379  public function getGlobals()
1380  {
1381  if (!$this->runtimeInitialized && !$this->extensionInitialized) {
1382  return $this->initGlobals();
1383  }
1384 
1385  if (null === $this->globals) {
1386  $this->globals = $this->initGlobals();
1387  }
1388 
1389  return $this->globals;
1390  }
1391 
1399  public function mergeGlobals(array $context)
1400  {
1401  // we don't use array_merge as the context being generally
1402  // bigger than globals, this code is faster.
1403  foreach ($this->getGlobals() as $key => $value) {
1404  if (!array_key_exists($key, $context)) {
1405  $context[$key] = $value;
1406  }
1407  }
1408 
1409  return $context;
1410  }
1411 
1419  public function getUnaryOperators()
1420  {
1421  if (!$this->extensionInitialized) {
1422  $this->initExtensions();
1423  }
1424 
1425  return $this->unaryOperators;
1426  }
1427 
1435  public function getBinaryOperators()
1436  {
1437  if (!$this->extensionInitialized) {
1438  $this->initExtensions();
1439  }
1440 
1441  return $this->binaryOperators;
1442  }
1443 
1447  public function computeAlternatives($name, $items)
1448  {
1449  @trigger_error(sprintf('The %s method is deprecated since version 1.23 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
1450 
1452  }
1453 
1457  protected function initGlobals()
1458  {
1459  $globals = array();
1460  foreach ($this->extensions as $name => $extension) {
1461  if (!$extension instanceof Twig_Extension_GlobalsInterface) {
1462  $m = new ReflectionMethod($extension, 'getGlobals');
1463 
1464  if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
1465  @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);
1466  }
1467  }
1468 
1469  $extGlob = $extension->getGlobals();
1470  if (!is_array($extGlob)) {
1471  throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension)));
1472  }
1473 
1474  $globals[] = $extGlob;
1475  }
1476 
1477  $globals[] = $this->staging->getGlobals();
1478 
1479  return call_user_func_array('array_merge', $globals);
1480  }
1481 
1485  protected function initExtensions()
1486  {
1487  if ($this->extensionInitialized) {
1488  return;
1489  }
1490 
1491  $this->parsers = new Twig_TokenParserBroker(array(), array(), false);
1492  $this->filters = array();
1493  $this->functions = array();
1494  $this->tests = array();
1495  $this->visitors = array();
1496  $this->unaryOperators = array();
1497  $this->binaryOperators = array();
1498 
1499  foreach ($this->extensions as $extension) {
1500  $this->initExtension($extension);
1501  }
1502  $this->initExtension($this->staging);
1503  // Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception
1504  $this->extensionInitialized = true;
1505  }
1506 
1510  protected function initExtension(Twig_ExtensionInterface $extension)
1511  {
1512  // filters
1513  foreach ($extension->getFilters() as $name => $filter) {
1514  if ($filter instanceof Twig_SimpleFilter) {
1515  $name = $filter->getName();
1516  } else {
1517  @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);
1518  }
1519 
1520  $this->filters[$name] = $filter;
1521  }
1522 
1523  // functions
1524  foreach ($extension->getFunctions() as $name => $function) {
1525  if ($function instanceof Twig_SimpleFunction) {
1526  $name = $function->getName();
1527  } else {
1528  @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);
1529  }
1530 
1531  $this->functions[$name] = $function;
1532  }
1533 
1534  // tests
1535  foreach ($extension->getTests() as $name => $test) {
1536  if ($test instanceof Twig_SimpleTest) {
1537  $name = $test->getName();
1538  } else {
1539  @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);
1540  }
1541 
1542  $this->tests[$name] = $test;
1543  }
1544 
1545  // token parsers
1546  foreach ($extension->getTokenParsers() as $parser) {
1547  if ($parser instanceof Twig_TokenParserInterface) {
1548  $this->parsers->addTokenParser($parser);
1549  } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
1550  @trigger_error('Registering a Twig_TokenParserBrokerInterface instance is deprecated since version 1.21.', E_USER_DEPRECATED);
1551 
1552  $this->parsers->addTokenParserBroker($parser);
1553  } else {
1554  throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances.');
1555  }
1556  }
1557 
1558  // node visitors
1559  foreach ($extension->getNodeVisitors() as $visitor) {
1560  $this->visitors[] = $visitor;
1561  }
1562 
1563  // operators
1564  if ($operators = $extension->getOperators()) {
1565  if (!is_array($operators)) {
1566  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)));
1567  }
1568 
1569  if (2 !== count($operators)) {
1570  throw new InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', get_class($extension), count($operators)));
1571  }
1572 
1573  $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
1574  $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
1575  }
1576  }
1577 
1581  protected function writeCacheFile($file, $content)
1582  {
1583  $this->cache->write($file, $content);
1584  }
1585 
1586  private function updateOptionsHash()
1587  {
1588  $hashParts = array_merge(
1589  array_keys($this->extensions),
1590  array(
1591  (int) function_exists('twig_template_get_attributes'),
1592  PHP_MAJOR_VERSION,
1593  PHP_MINOR_VERSION,
1594  self::VERSION,
1595  (int) $this->debug,
1596  $this->baseTemplateClass,
1597  (int) $this->strictVariables,
1598  )
1599  );
1600  $this->optionsHash = implode(':', $hashParts);
1601  }
1602 }
1603 
1604 class_alias('Twig_Environment', 'Twig\Environment', false);
Interface implemented by extension classes.
getBinaryOperators()
Gets the registered binary Operators.
getCache($original=true)
Gets the current cache implementation.
Represents a node in the AST.
writeCacheFile($file, $content)
getCacheFilename($name)
Gets the cache filename for a given template.
resolveTemplate($names)
Tries to load a template consecutively from an array.
getTemplateClass($name, $index=null)
Gets the template class associated with the given string.
getLexer()
Gets the Lexer instance.
Interface implemented by token parser brokers.
Interface implemented by parser classes.
Represents a template function.
$context
Definition: webdav.php:25
isDebug()
Checks if debug mode is enabled.
removeExtension($name)
Removes an extension by name.
Exposes a template to userland.
Represents a template test.
$template
setCharset($charset)
Sets the default template charset.
setParser(Twig_ParserInterface $parser)
Default parser implementation.
Definition: Parser.php:18
addExtension(Twig_ExtensionInterface $extension)
getFilter($name)
Get a filter by name.
__construct(Twig_LoaderInterface $loader=null, $options=array())
Constructor.
getNodeVisitors()
Returns the node visitor instances to add to the existing list.
Exception thrown when an error occurs during template loading.
Definition: Loader.php:25
getBaseTemplateClass()
Gets the base template class for compiled templates.
registerUndefinedFunctionCallback($callable)
display($name, array $context=array())
Displays a template.
isTemplateFresh($name, $time)
Returns true if the template is still fresh.
setSourceContext(Twig_Source $source=null)
Sets the source context of the Twig template where the error occurred.
Definition: Error.php:199
static computeAlternatives($name, $items)
Definition: Syntax.php:40
addFilter($name, $filter=null)
Registers a Filter.
Twig base exception.
Definition: Error.php:34
Represents a template filter.
addFunction($name, $function=null)
Registers a Function.
computeAlternatives($name, $items)
Creates runtime implementations for Twig elements (filters/functions/tests).
Enables usage of the deprecated Twig_Extension::getGlobals() method.
Exception thrown when an error occurs at runtime.
Definition: Runtime.php:18
$index
Definition: metadata.php:60
Represents a token stream.
Definition: TokenStream.php:20
enableAutoReload()
Enables the auto_reload option.
getNodeVisitors()
Gets the registered Node Visitors.
Enables usage of the deprecated Twig_Extension::initRuntime() method.
$stream
PHP stream implementation.
Represents a template filter.
Exception thrown when a syntax error occurs during lexing or parsing of a template.
Definition: Syntax.php:18
getCharset()
Gets the default template charset.
getLoader()
Gets the Loader instance.
$time
Definition: cron.php:21
initRuntime()
Initializes the runtime environment.
setLexer(Twig_LexerInterface $lexer)
getFunction($name)
Get a function by name.
getFunctions()
Returns a list of functions to add to the existing list.
$r
Definition: example_031.php:79
getTests()
Gets the registered Tests.
setLoader(Twig_LoaderInterface $loader)
getRuntime($class)
Returns the runtime implementation of a Twig element (filter/function/test).
getTags()
Gets registered tags.
getFilters()
Gets the registered Filters.
Twig_NodeVisitorInterface is the interface the all node visitor classes must implement.
tokenize($source, $name=null)
Tokenizes a source code.
mergeGlobals(array $context)
Merges a context with the defined globals.
addRuntimeLoader(Twig_RuntimeLoaderInterface $loader)
Adds a runtime loader.
$tags
Definition: croninfo.php:19
Interface implemented by lexer classes.
compileSource($source, $name=null)
Compiles a template source code.
Represents a template function.
getOperators()
Returns a list of operators to add to the existing list.
getTests()
Returns a list of tests to add to the existing list.
enableDebug()
Enables debugging mode.
getTest($name)
Gets a test by name.
getTemplateClassPrefix()
Gets the template class prefix.
clearTemplateCache()
Clears the internal template cache.
getTokenParsers()
Returns the token parser instances to add to the existing list.
Adds a getSourceContext() method for loaders.
getCompiler()
Gets the Compiler instance.
getParser()
Gets the Parser instance.
addGlobal($name, $value)
Registers a Global.
getGlobals()
Gets the registered Globals.
Loads templates from other loaders.
Definition: Chain.php:19
getFilters()
Returns a list of filters to add to the existing list.
addNodeVisitor(Twig_NodeVisitorInterface $visitor)
parse(Twig_TokenStream $stream)
Converts a token stream to a node tree.
hasExtension($class)
Returns true if the given extension is registered.
setCompiler(Twig_CompilerInterface $compiler)
getTokenParsers()
Gets the registered Token Parsers.
Default base class for compiled templates.
Definition: Template.php:24
setCache($cache)
Sets the current cache implementation.
addTokenParser(Twig_TokenParserInterface $parser)
clearCacheFiles()
Clears the template cache files on the filesystem.
loadTemplate($name, $index=null)
Loads a template internal representation.
initExtension(Twig_ExtensionInterface $extension)
enableStrictVariables()
Enables the strict_variables option.
isAutoReload()
Checks if the auto_reload option is enabled.
registerUndefinedFilterCallback($callable)
Implements a cache on the filesystem.
Definition: Filesystem.php:17
setExtensions(array $extensions)
Registers an array of extensions.
getExtensions()
Returns all registered extensions.
Interface implemented by token parsers.
Default implementation of a token parser broker.
render($name, array $context=array())
Renders a template.
Represents a template test.
Definition: SimpleTest.php:19
Holds information about a non-compiled Twig template.
Definition: Source.php:19
disableStrictVariables()
Disables the strict_variables option.
setBaseTemplateClass($class)
Sets the base template class for compiled templates.
getUnaryOperators()
Gets the registered unary Operators.
compile(Twig_NodeInterface $node)
Compiles a node and returns the PHP code.
$source
Definition: linkback.php:22
Stores the Twig configuration.
Definition: Environment.php:17
Interface implemented by cache classes.
addTest($name, $test=null)
Registers a Test.
disableDebug()
Disables debugging mode.
createTemplate($template)
Creates a template from source.
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.
Definition: functions.php:406
Interface all loaders must implement.
Loads a template from an array.
Definition: Array.php:26
Implements a no-cache strategy.
Definition: Null.php:19
getFunctions()
Gets registered functions.
$key
Definition: croninfo.php:18
getExtension($class)
Gets an extension by class name.
load($name)
Loads a template.
Internal class.
Definition: Staging.php:21
getName()
Returns the name of the extension.
$test
Definition: Utf8Test.php:84
isStrictVariables()
Checks if the strict_variables option is enabled.
Interface implemented by compiler classes.
Lexes a template string.
Definition: Lexer.php:18
disableAutoReload()
Disables the auto_reload option.