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