Twig-1.3.0/lib/Twig/Environment.php
changeset 4 9a001a04b634
equal deleted inserted replaced
3:6d109e3804ac 4:9a001a04b634
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of Twig.
       
     5  *
       
     6  * (c) 2009 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 
       
    12 /**
       
    13  * Stores the Twig configuration.
       
    14  *
       
    15  * @package twig
       
    16  * @author  Fabien Potencier <fabien@symfony.com>
       
    17  */
       
    18 class Twig_Environment
       
    19 {
       
    20     const VERSION = '1.4.0-DEV';
       
    21 
       
    22     protected $charset;
       
    23     protected $loader;
       
    24     protected $debug;
       
    25     protected $autoReload;
       
    26     protected $cache;
       
    27     protected $lexer;
       
    28     protected $parser;
       
    29     protected $compiler;
       
    30     protected $baseTemplateClass;
       
    31     protected $extensions;
       
    32     protected $parsers;
       
    33     protected $visitors;
       
    34     protected $filters;
       
    35     protected $tests;
       
    36     protected $functions;
       
    37     protected $globals;
       
    38     protected $runtimeInitialized;
       
    39     protected $loadedTemplates;
       
    40     protected $strictVariables;
       
    41     protected $unaryOperators;
       
    42     protected $binaryOperators;
       
    43     protected $templateClassPrefix = '__TwigTemplate_';
       
    44     protected $functionCallbacks;
       
    45     protected $filterCallbacks;
       
    46 
       
    47     /**
       
    48      * Constructor.
       
    49      *
       
    50      * Available options:
       
    51      *
       
    52      *  * debug: When set to `true`, the generated templates have a __toString()
       
    53      *           method that you can use to display the generated nodes (default to
       
    54      *           false).
       
    55      *
       
    56      *  * charset: The charset used by the templates (default to utf-8).
       
    57      *
       
    58      *  * base_template_class: The base template class to use for generated
       
    59      *                         templates (default to Twig_Template).
       
    60      *
       
    61      *  * cache: An absolute path where to store the compiled templates, or
       
    62      *           false to disable compilation cache (default)
       
    63      *
       
    64      *  * auto_reload: Whether to reload the template is the original source changed.
       
    65      *                 If you don't provide the auto_reload option, it will be
       
    66      *                 determined automatically base on the debug value.
       
    67      *
       
    68      *  * strict_variables: Whether to ignore invalid variables in templates
       
    69      *                      (default to false).
       
    70      *
       
    71      *  * autoescape: Whether to enable auto-escaping (default to true);
       
    72      *
       
    73      *  * optimizations: A flag that indicates which optimizations to apply
       
    74      *                   (default to -1 which means that all optimizations are enabled;
       
    75      *                   set it to 0 to disable)
       
    76      *
       
    77      * @param Twig_LoaderInterface   $loader  A Twig_LoaderInterface instance
       
    78      * @param array                  $options An array of options
       
    79      */
       
    80     public function __construct(Twig_LoaderInterface $loader = null, $options = array())
       
    81     {
       
    82         if (null !== $loader) {
       
    83             $this->setLoader($loader);
       
    84         }
       
    85 
       
    86         $options = array_merge(array(
       
    87             'debug'               => false,
       
    88             'charset'             => 'UTF-8',
       
    89             'base_template_class' => 'Twig_Template',
       
    90             'strict_variables'    => false,
       
    91             'autoescape'          => true,
       
    92             'cache'               => false,
       
    93             'auto_reload'         => null,
       
    94             'optimizations'       => -1,
       
    95         ), $options);
       
    96 
       
    97         $this->debug              = (bool) $options['debug'];
       
    98         $this->charset            = $options['charset'];
       
    99         $this->baseTemplateClass  = $options['base_template_class'];
       
   100         $this->autoReload         = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
       
   101         $this->extensions         = array(
       
   102             'core'      => new Twig_Extension_Core(),
       
   103             'escaper'   => new Twig_Extension_Escaper((bool) $options['autoescape']),
       
   104             'optimizer' => new Twig_Extension_Optimizer($options['optimizations']),
       
   105         );
       
   106         $this->strictVariables    = (bool) $options['strict_variables'];
       
   107         $this->runtimeInitialized = false;
       
   108         $this->setCache($options['cache']);
       
   109         $this->functionCallbacks = array();
       
   110         $this->filterCallbacks = array();
       
   111     }
       
   112 
       
   113     /**
       
   114      * Gets the base template class for compiled templates.
       
   115      *
       
   116      * @return string The base template class name
       
   117      */
       
   118     public function getBaseTemplateClass()
       
   119     {
       
   120         return $this->baseTemplateClass;
       
   121     }
       
   122 
       
   123     /**
       
   124      * Sets the base template class for compiled templates.
       
   125      *
       
   126      * @param string $class The base template class name
       
   127      */
       
   128     public function setBaseTemplateClass($class)
       
   129     {
       
   130         $this->baseTemplateClass = $class;
       
   131     }
       
   132 
       
   133     /**
       
   134      * Enables debugging mode.
       
   135      */
       
   136     public function enableDebug()
       
   137     {
       
   138         $this->debug = true;
       
   139     }
       
   140 
       
   141     /**
       
   142      * Disables debugging mode.
       
   143      */
       
   144     public function disableDebug()
       
   145     {
       
   146         $this->debug = false;
       
   147     }
       
   148 
       
   149     /**
       
   150      * Checks if debug mode is enabled.
       
   151      *
       
   152      * @return Boolean true if debug mode is enabled, false otherwise
       
   153      */
       
   154     public function isDebug()
       
   155     {
       
   156         return $this->debug;
       
   157     }
       
   158 
       
   159     /**
       
   160      * Enables the auto_reload option.
       
   161      */
       
   162     public function enableAutoReload()
       
   163     {
       
   164         $this->autoReload = true;
       
   165     }
       
   166 
       
   167     /**
       
   168      * Disables the auto_reload option.
       
   169      */
       
   170     public function disableAutoReload()
       
   171     {
       
   172         $this->autoReload = false;
       
   173     }
       
   174 
       
   175     /**
       
   176      * Checks if the auto_reload option is enabled.
       
   177      *
       
   178      * @return Boolean true if auto_reload is enabled, false otherwise
       
   179      */
       
   180     public function isAutoReload()
       
   181     {
       
   182         return $this->autoReload;
       
   183     }
       
   184 
       
   185     /**
       
   186      * Enables the strict_variables option.
       
   187      */
       
   188     public function enableStrictVariables()
       
   189     {
       
   190         $this->strictVariables = true;
       
   191     }
       
   192 
       
   193     /**
       
   194      * Disables the strict_variables option.
       
   195      */
       
   196     public function disableStrictVariables()
       
   197     {
       
   198         $this->strictVariables = false;
       
   199     }
       
   200 
       
   201     /**
       
   202      * Checks if the strict_variables option is enabled.
       
   203      *
       
   204      * @return Boolean true if strict_variables is enabled, false otherwise
       
   205      */
       
   206     public function isStrictVariables()
       
   207     {
       
   208         return $this->strictVariables;
       
   209     }
       
   210 
       
   211     /**
       
   212      * Gets the cache directory or false if cache is disabled.
       
   213      *
       
   214      * @return string|false
       
   215      */
       
   216     public function getCache()
       
   217     {
       
   218         return $this->cache;
       
   219     }
       
   220 
       
   221      /**
       
   222       * Sets the cache directory or false if cache is disabled.
       
   223       *
       
   224       * @param string|false $cache The absolute path to the compiled templates,
       
   225       *                            or false to disable cache
       
   226       */
       
   227     public function setCache($cache)
       
   228     {
       
   229         $this->cache = $cache ? $cache : false;
       
   230     }
       
   231 
       
   232     /**
       
   233      * Gets the cache filename for a given template.
       
   234      *
       
   235      * @param string $name The template name
       
   236      *
       
   237      * @return string The cache file name
       
   238      */
       
   239     public function getCacheFilename($name)
       
   240     {
       
   241         if (false === $this->cache) {
       
   242             return false;
       
   243         }
       
   244 
       
   245         $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix));
       
   246 
       
   247         return $this->getCache().'/'.substr($class, 0, 2).'/'.substr($class, 2, 2).'/'.substr($class, 4).'.php';
       
   248     }
       
   249 
       
   250     /**
       
   251      * Gets the template class associated with the given string.
       
   252      *
       
   253      * @param string $name The name for which to calculate the template class name
       
   254      *
       
   255      * @return string The template class name
       
   256      */
       
   257     public function getTemplateClass($name)
       
   258     {
       
   259         return $this->templateClassPrefix.md5($this->loader->getCacheKey($name));
       
   260     }
       
   261 
       
   262     /**
       
   263      * Gets the template class prefix.
       
   264      *
       
   265      * @return string The template class prefix
       
   266      */
       
   267     public function getTemplateClassPrefix()
       
   268     {
       
   269         return $this->templateClassPrefix;
       
   270     }
       
   271 
       
   272     /**
       
   273      * Renders a template.
       
   274      *
       
   275      * @param string $name    The template name
       
   276      * @param array  $context An array of parameters to pass to the template
       
   277      *
       
   278      * @return string The rendered template
       
   279      */
       
   280     public function render($name, array $context = array())
       
   281     {
       
   282         return $this->loadTemplate($name)->render($context);
       
   283     }
       
   284 
       
   285     /**
       
   286      * Loads a template by name.
       
   287      *
       
   288      * @param  string  $name  The template name
       
   289      *
       
   290      * @return Twig_TemplateInterface A template instance representing the given template name
       
   291      */
       
   292     public function loadTemplate($name)
       
   293     {
       
   294         $cls = $this->getTemplateClass($name);
       
   295 
       
   296         if (isset($this->loadedTemplates[$cls])) {
       
   297             return $this->loadedTemplates[$cls];
       
   298         }
       
   299 
       
   300         if (!class_exists($cls, false)) {
       
   301             if (false === $cache = $this->getCacheFilename($name)) {
       
   302                 eval('?>'.$this->compileSource($this->loader->getSource($name), $name));
       
   303             } else {
       
   304                 if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
       
   305                     $this->writeCacheFile($cache, $this->compileSource($this->loader->getSource($name), $name));
       
   306                 }
       
   307 
       
   308                 require_once $cache;
       
   309             }
       
   310         }
       
   311 
       
   312         if (!$this->runtimeInitialized) {
       
   313             $this->initRuntime();
       
   314         }
       
   315 
       
   316         return $this->loadedTemplates[$cls] = new $cls($this);
       
   317     }
       
   318 
       
   319     /**
       
   320      * Returns true if the template is still fresh.
       
   321      *
       
   322      * Besides checking the loader for freshness information,
       
   323      * this method also checks if the enabled extensions have
       
   324      * not changed.
       
   325      *
       
   326      * @param string    $name The template name
       
   327      * @param timestamp $time The last modification time of the cached template
       
   328      *
       
   329      * @return Boolean true if the template is fresh, false otherwise
       
   330      */
       
   331     public function isTemplateFresh($name, $time)
       
   332     {
       
   333         foreach ($this->extensions as $extension) {
       
   334             $r = new ReflectionObject($extension);
       
   335             if (filemtime($r->getFileName()) > $time) {
       
   336                 return false;
       
   337             }
       
   338         }
       
   339 
       
   340         return $this->loader->isFresh($name, $time);
       
   341     }
       
   342 
       
   343     public function resolveTemplate($names)
       
   344     {
       
   345         if (!is_array($names)) {
       
   346             $names = array($names);
       
   347         }
       
   348 
       
   349         foreach ($names as $name) {
       
   350             if ($name instanceof Twig_Template) {
       
   351                 return $name;
       
   352             }
       
   353 
       
   354             try {
       
   355                 return $this->loadTemplate($name);
       
   356             } catch (Twig_Error_Loader $e) {
       
   357             }
       
   358         }
       
   359 
       
   360         if (1 === count($names)) {
       
   361             throw $e;
       
   362         }
       
   363 
       
   364         throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
       
   365     }
       
   366 
       
   367     /**
       
   368      * Clears the internal template cache.
       
   369      */
       
   370     public function clearTemplateCache()
       
   371     {
       
   372         $this->loadedTemplates = array();
       
   373     }
       
   374 
       
   375     /**
       
   376      * Clears the template cache files on the filesystem.
       
   377      */
       
   378     public function clearCacheFiles()
       
   379     {
       
   380         if (false === $this->cache) {
       
   381             return;
       
   382         }
       
   383 
       
   384         foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
       
   385             if ($file->isFile()) {
       
   386                 @unlink($file->getPathname());
       
   387             }
       
   388         }
       
   389     }
       
   390 
       
   391     /**
       
   392      * Gets the Lexer instance.
       
   393      *
       
   394      * @return Twig_LexerInterface A Twig_LexerInterface instance
       
   395      */
       
   396     public function getLexer()
       
   397     {
       
   398         if (null === $this->lexer) {
       
   399             $this->lexer = new Twig_Lexer($this);
       
   400         }
       
   401 
       
   402         return $this->lexer;
       
   403     }
       
   404 
       
   405     /**
       
   406      * Sets the Lexer instance.
       
   407      *
       
   408      * @param Twig_LexerInterface A Twig_LexerInterface instance
       
   409      */
       
   410     public function setLexer(Twig_LexerInterface $lexer)
       
   411     {
       
   412         $this->lexer = $lexer;
       
   413     }
       
   414 
       
   415     /**
       
   416      * Tokenizes a source code.
       
   417      *
       
   418      * @param string $source The template source code
       
   419      * @param string $name   The template name
       
   420      *
       
   421      * @return Twig_TokenStream A Twig_TokenStream instance
       
   422      */
       
   423     public function tokenize($source, $name = null)
       
   424     {
       
   425         return $this->getLexer()->tokenize($source, $name);
       
   426     }
       
   427 
       
   428     /**
       
   429      * Gets the Parser instance.
       
   430      *
       
   431      * @return Twig_ParserInterface A Twig_ParserInterface instance
       
   432      */
       
   433     public function getParser()
       
   434     {
       
   435         if (null === $this->parser) {
       
   436             $this->parser = new Twig_Parser($this);
       
   437         }
       
   438 
       
   439         return $this->parser;
       
   440     }
       
   441 
       
   442     /**
       
   443      * Sets the Parser instance.
       
   444      *
       
   445      * @param Twig_ParserInterface A Twig_ParserInterface instance
       
   446      */
       
   447     public function setParser(Twig_ParserInterface $parser)
       
   448     {
       
   449         $this->parser = $parser;
       
   450     }
       
   451 
       
   452     /**
       
   453      * Parses a token stream.
       
   454      *
       
   455      * @param Twig_TokenStream $tokens A Twig_TokenStream instance
       
   456      *
       
   457      * @return Twig_Node_Module A Node tree
       
   458      */
       
   459     public function parse(Twig_TokenStream $tokens)
       
   460     {
       
   461         return $this->getParser()->parse($tokens);
       
   462     }
       
   463 
       
   464     /**
       
   465      * Gets the Compiler instance.
       
   466      *
       
   467      * @return Twig_CompilerInterface A Twig_CompilerInterface instance
       
   468      */
       
   469     public function getCompiler()
       
   470     {
       
   471         if (null === $this->compiler) {
       
   472             $this->compiler = new Twig_Compiler($this);
       
   473         }
       
   474 
       
   475         return $this->compiler;
       
   476     }
       
   477 
       
   478     /**
       
   479      * Sets the Compiler instance.
       
   480      *
       
   481      * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance
       
   482      */
       
   483     public function setCompiler(Twig_CompilerInterface $compiler)
       
   484     {
       
   485         $this->compiler = $compiler;
       
   486     }
       
   487 
       
   488     /**
       
   489      * Compiles a Node.
       
   490      *
       
   491      * @param Twig_NodeInterface $node A Twig_NodeInterface instance
       
   492      *
       
   493      * @return string The compiled PHP source code
       
   494      */
       
   495     public function compile(Twig_NodeInterface $node)
       
   496     {
       
   497         return $this->getCompiler()->compile($node)->getSource();
       
   498     }
       
   499 
       
   500     /**
       
   501      * Compiles a template source code.
       
   502      *
       
   503      * @param string $source The template source code
       
   504      * @param string $name   The template name
       
   505      *
       
   506      * @return string The compiled PHP source code
       
   507      */
       
   508     public function compileSource($source, $name = null)
       
   509     {
       
   510         try {
       
   511             return $this->compile($this->parse($this->tokenize($source, $name)));
       
   512         } catch (Twig_Error $e) {
       
   513             $e->setTemplateFile($name);
       
   514             throw $e;
       
   515         } catch (Exception $e) {
       
   516             throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
       
   517         }
       
   518     }
       
   519 
       
   520     /**
       
   521      * Sets the Loader instance.
       
   522      *
       
   523      * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
       
   524      */
       
   525     public function setLoader(Twig_LoaderInterface $loader)
       
   526     {
       
   527         $this->loader = $loader;
       
   528     }
       
   529 
       
   530     /**
       
   531      * Gets the Loader instance.
       
   532      *
       
   533      * @return Twig_LoaderInterface A Twig_LoaderInterface instance
       
   534      */
       
   535     public function getLoader()
       
   536     {
       
   537         return $this->loader;
       
   538     }
       
   539 
       
   540     /**
       
   541      * Sets the default template charset.
       
   542      *
       
   543      * @param string $charset The default charset
       
   544      */
       
   545     public function setCharset($charset)
       
   546     {
       
   547         $this->charset = $charset;
       
   548     }
       
   549 
       
   550     /**
       
   551      * Gets the default template charset.
       
   552      *
       
   553      * @return string The default charset
       
   554      */
       
   555     public function getCharset()
       
   556     {
       
   557         return $this->charset;
       
   558     }
       
   559 
       
   560     /**
       
   561      * Initializes the runtime environment.
       
   562      */
       
   563     public function initRuntime()
       
   564     {
       
   565         $this->runtimeInitialized = true;
       
   566 
       
   567         foreach ($this->getExtensions() as $extension) {
       
   568             $extension->initRuntime($this);
       
   569         }
       
   570     }
       
   571 
       
   572     /**
       
   573      * Returns true if the given extension is registered.
       
   574      *
       
   575      * @param string $name The extension name
       
   576      *
       
   577      * @return Boolean Whether the extension is registered or not
       
   578      */
       
   579     public function hasExtension($name)
       
   580     {
       
   581         return isset($this->extensions[$name]);
       
   582     }
       
   583 
       
   584     /**
       
   585      * Gets an extension by name.
       
   586      *
       
   587      * @param string $name The extension name
       
   588      *
       
   589      * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance
       
   590      */
       
   591     public function getExtension($name)
       
   592     {
       
   593         if (!isset($this->extensions[$name])) {
       
   594             throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name));
       
   595         }
       
   596 
       
   597         return $this->extensions[$name];
       
   598     }
       
   599 
       
   600     /**
       
   601      * Registers an extension.
       
   602      *
       
   603      * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance
       
   604      */
       
   605     public function addExtension(Twig_ExtensionInterface $extension)
       
   606     {
       
   607         $this->extensions[$extension->getName()] = $extension;
       
   608     }
       
   609 
       
   610     /**
       
   611      * Removes an extension by name.
       
   612      *
       
   613      * @param string $name The extension name
       
   614      */
       
   615     public function removeExtension($name)
       
   616     {
       
   617         unset($this->extensions[$name]);
       
   618     }
       
   619 
       
   620     /**
       
   621      * Registers an array of extensions.
       
   622      *
       
   623      * @param array $extensions An array of extensions
       
   624      */
       
   625     public function setExtensions(array $extensions)
       
   626     {
       
   627         foreach ($extensions as $extension) {
       
   628             $this->addExtension($extension);
       
   629         }
       
   630     }
       
   631 
       
   632     /**
       
   633      * Returns all registered extensions.
       
   634      *
       
   635      * @return array An array of extensions
       
   636      */
       
   637     public function getExtensions()
       
   638     {
       
   639         return $this->extensions;
       
   640     }
       
   641 
       
   642     /**
       
   643      * Registers a Token Parser.
       
   644      *
       
   645      * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
       
   646      */
       
   647     public function addTokenParser(Twig_TokenParserInterface $parser)
       
   648     {
       
   649         if (null === $this->parsers) {
       
   650             $this->getTokenParsers();
       
   651         }
       
   652 
       
   653         $this->parsers->addTokenParser($parser);
       
   654     }
       
   655 
       
   656     /**
       
   657      * Gets the registered Token Parsers.
       
   658      *
       
   659      * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances
       
   660      */
       
   661     public function getTokenParsers()
       
   662     {
       
   663         if (null === $this->parsers) {
       
   664             $this->parsers = new Twig_TokenParserBroker;
       
   665             foreach ($this->getExtensions() as $extension) {
       
   666                 $parsers = $extension->getTokenParsers();
       
   667                 foreach($parsers as $parser) {
       
   668                     if ($parser instanceof Twig_TokenParserInterface) {
       
   669                         $this->parsers->addTokenParser($parser);
       
   670                     } else if ($parser instanceof Twig_TokenParserBrokerInterface) {
       
   671                         $this->parsers->addTokenParserBroker($parser);
       
   672                     } else {
       
   673                         throw new Twig_Error_Runtime('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
       
   674                     }
       
   675                 }
       
   676             }
       
   677         }
       
   678 
       
   679         return $this->parsers;
       
   680     }
       
   681 
       
   682     /**
       
   683      * Registers a Node Visitor.
       
   684      *
       
   685      * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
       
   686      */
       
   687     public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
       
   688     {
       
   689         if (null === $this->visitors) {
       
   690             $this->getNodeVisitors();
       
   691         }
       
   692 
       
   693         $this->visitors[] = $visitor;
       
   694     }
       
   695 
       
   696     /**
       
   697      * Gets the registered Node Visitors.
       
   698      *
       
   699      * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
       
   700      */
       
   701     public function getNodeVisitors()
       
   702     {
       
   703         if (null === $this->visitors) {
       
   704             $this->visitors = array();
       
   705             foreach ($this->getExtensions() as $extension) {
       
   706                 $this->visitors = array_merge($this->visitors, $extension->getNodeVisitors());
       
   707             }
       
   708         }
       
   709 
       
   710         return $this->visitors;
       
   711     }
       
   712 
       
   713     /**
       
   714      * Registers a Filter.
       
   715      *
       
   716      * @param string               $name    The filter name
       
   717      * @param Twig_FilterInterface $visitor A Twig_FilterInterface instance
       
   718      */
       
   719     public function addFilter($name, Twig_FilterInterface $filter)
       
   720     {
       
   721         if (null === $this->filters) {
       
   722             $this->loadFilters();
       
   723         }
       
   724 
       
   725         $this->filters[$name] = $filter;
       
   726     }
       
   727 
       
   728     /**
       
   729      * Get a filter by name.
       
   730      *
       
   731      * Subclasses may override this method and load filters differently;
       
   732      * so no list of filters is available.
       
   733      *
       
   734      * @param string $name The filter name
       
   735      *
       
   736      * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exists
       
   737      */
       
   738     public function getFilter($name)
       
   739     {
       
   740         if (null === $this->filters) {
       
   741             $this->loadFilters();
       
   742         }
       
   743 
       
   744         if (isset($this->filters[$name])) {
       
   745             return $this->filters[$name];
       
   746         }
       
   747 
       
   748         foreach ($this->filterCallbacks as $callback) {
       
   749             if (false !== $filter = call_user_func($callback, $name)) {
       
   750                 return $filter;
       
   751             }
       
   752         }
       
   753 
       
   754         return false;
       
   755     }
       
   756 
       
   757     public function registerUndefinedFilterCallback($callable)
       
   758     {
       
   759         $this->filterCallbacks[] = $callable;
       
   760     }
       
   761 
       
   762     /**
       
   763      * Gets the registered Filters.
       
   764      *
       
   765      * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
       
   766      */
       
   767     protected function loadFilters()
       
   768     {
       
   769         $this->filters = array();
       
   770         foreach ($this->getExtensions() as $extension) {
       
   771             $this->filters = array_merge($this->filters, $extension->getFilters());
       
   772         }
       
   773     }
       
   774 
       
   775     /**
       
   776      * Registers a Test.
       
   777      *
       
   778      * @param string             $name    The test name
       
   779      * @param Twig_TestInterface $visitor A Twig_TestInterface instance
       
   780      */
       
   781     public function addTest($name, Twig_TestInterface $test)
       
   782     {
       
   783         if (null === $this->tests) {
       
   784             $this->getTests();
       
   785         }
       
   786 
       
   787         $this->tests[$name] = $test;
       
   788     }
       
   789 
       
   790     /**
       
   791      * Gets the registered Tests.
       
   792      *
       
   793      * @return Twig_TestInterface[] An array of Twig_TestInterface instances
       
   794      */
       
   795     public function getTests()
       
   796     {
       
   797         if (null === $this->tests) {
       
   798             $this->tests = array();
       
   799             foreach ($this->getExtensions() as $extension) {
       
   800                 $this->tests = array_merge($this->tests, $extension->getTests());
       
   801             }
       
   802         }
       
   803 
       
   804         return $this->tests;
       
   805     }
       
   806 
       
   807     /**
       
   808      * Registers a Function.
       
   809      *
       
   810      * @param string                 $name     The function name
       
   811      * @param Twig_FunctionInterface $function A Twig_FunctionInterface instance
       
   812      */
       
   813     public function addFunction($name, Twig_FunctionInterface $function)
       
   814     {
       
   815         if (null === $this->functions) {
       
   816             $this->loadFunctions();
       
   817         }
       
   818 
       
   819         $this->functions[$name] = $function;
       
   820     }
       
   821 
       
   822     /**
       
   823      * Get a function by name.
       
   824      *
       
   825      * Subclasses may override this method and load functions differently;
       
   826      * so no list of functions is available.
       
   827      *
       
   828      * @param string $name function name
       
   829      *
       
   830      * @return Twig_Function|false A Twig_Function instance or false if the function does not exists
       
   831      */
       
   832     public function getFunction($name)
       
   833     {
       
   834         if (null === $this->functions) {
       
   835             $this->loadFunctions();
       
   836         }
       
   837 
       
   838         if (isset($this->functions[$name])) {
       
   839             return $this->functions[$name];
       
   840         }
       
   841 
       
   842         foreach ($this->functionCallbacks as $callback) {
       
   843             if (false !== $function = call_user_func($callback, $name)) {
       
   844                 return $function;
       
   845             }
       
   846         }
       
   847 
       
   848         return false;
       
   849     }
       
   850 
       
   851     public function registerUndefinedFunctionCallback($callable)
       
   852     {
       
   853         $this->functionCallbacks[] = $callable;
       
   854     }
       
   855 
       
   856     protected function loadFunctions()
       
   857     {
       
   858         $this->functions = array();
       
   859         foreach ($this->getExtensions() as $extension) {
       
   860             $this->functions = array_merge($this->functions, $extension->getFunctions());
       
   861         }
       
   862     }
       
   863 
       
   864     /**
       
   865      * Registers a Global.
       
   866      *
       
   867      * @param string $name  The global name
       
   868      * @param mixed  $value The global value
       
   869      */
       
   870     public function addGlobal($name, $value)
       
   871     {
       
   872         if (null === $this->globals) {
       
   873             $this->getGlobals();
       
   874         }
       
   875 
       
   876         $this->globals[$name] = $value;
       
   877     }
       
   878 
       
   879     /**
       
   880      * Gets the registered Globals.
       
   881      *
       
   882      * @return array An array of globals
       
   883      */
       
   884     public function getGlobals()
       
   885     {
       
   886         if (null === $this->globals) {
       
   887             $this->globals = array();
       
   888             foreach ($this->getExtensions() as $extension) {
       
   889                 $this->globals = array_merge($this->globals, $extension->getGlobals());
       
   890             }
       
   891         }
       
   892 
       
   893         return $this->globals;
       
   894     }
       
   895 
       
   896     /**
       
   897      * Gets the registered unary Operators.
       
   898      *
       
   899      * @return array An array of unary operators
       
   900      */
       
   901     public function getUnaryOperators()
       
   902     {
       
   903         if (null === $this->unaryOperators) {
       
   904             $this->initOperators();
       
   905         }
       
   906 
       
   907         return $this->unaryOperators;
       
   908     }
       
   909 
       
   910     /**
       
   911      * Gets the registered binary Operators.
       
   912      *
       
   913      * @return array An array of binary operators
       
   914      */
       
   915     public function getBinaryOperators()
       
   916     {
       
   917         if (null === $this->binaryOperators) {
       
   918             $this->initOperators();
       
   919         }
       
   920 
       
   921         return $this->binaryOperators;
       
   922     }
       
   923 
       
   924     protected function initOperators()
       
   925     {
       
   926         $this->unaryOperators = array();
       
   927         $this->binaryOperators = array();
       
   928         foreach ($this->getExtensions() as $extension) {
       
   929             $operators = $extension->getOperators();
       
   930 
       
   931             if (!$operators) {
       
   932                 continue;
       
   933             }
       
   934 
       
   935             if (2 !== count($operators)) {
       
   936                 throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension)));
       
   937             }
       
   938 
       
   939             $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
       
   940             $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
       
   941         }
       
   942     }
       
   943 
       
   944     protected function writeCacheFile($file, $content)
       
   945     {
       
   946         if (!is_dir(dirname($file))) {
       
   947             mkdir(dirname($file), 0777, true);
       
   948         }
       
   949 
       
   950         $tmpFile = tempnam(dirname($file), basename($file));
       
   951         if (false !== @file_put_contents($tmpFile, $content)) {
       
   952             // rename does not work on Win32 before 5.2.6
       
   953             if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) {
       
   954                 chmod($file, 0644);
       
   955 
       
   956                 return;
       
   957             }
       
   958         }
       
   959 
       
   960         throw new Twig_Error_Runtime(sprintf('Failed to write cache file "%s".', $file));
       
   961     }
       
   962 }