[345] | 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.potencier@symfony-project.com> |
---|
| 17 | */ |
---|
| 18 | class Twig_Environment |
---|
| 19 | { |
---|
| 20 | const VERSION = '1.0.0-RC2'; |
---|
| 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 (!file_exists($cache) || ($this->isAutoReload() && !$this->loader->isFresh($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 | * Clears the internal template cache. |
---|
| 321 | */ |
---|
| 322 | public function clearTemplateCache() |
---|
| 323 | { |
---|
| 324 | $this->loadedTemplates = array(); |
---|
| 325 | } |
---|
| 326 | |
---|
| 327 | /** |
---|
| 328 | * Clears the template cache files on the filesystem. |
---|
| 329 | */ |
---|
| 330 | public function clearCacheFiles() |
---|
| 331 | { |
---|
| 332 | if (false === $this->cache) { |
---|
| 333 | return; |
---|
| 334 | } |
---|
| 335 | |
---|
| 336 | foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { |
---|
| 337 | if ($file->isFile()) { |
---|
| 338 | @unlink($file->getPathname()); |
---|
| 339 | } |
---|
| 340 | } |
---|
| 341 | } |
---|
| 342 | |
---|
| 343 | /** |
---|
| 344 | * Gets the Lexer instance. |
---|
| 345 | * |
---|
| 346 | * @return Twig_LexerInterface A Twig_LexerInterface instance |
---|
| 347 | */ |
---|
| 348 | public function getLexer() |
---|
| 349 | { |
---|
| 350 | if (null === $this->lexer) { |
---|
| 351 | $this->lexer = new Twig_Lexer($this); |
---|
| 352 | } |
---|
| 353 | |
---|
| 354 | return $this->lexer; |
---|
| 355 | } |
---|
| 356 | |
---|
| 357 | /** |
---|
| 358 | * Sets the Lexer instance. |
---|
| 359 | * |
---|
| 360 | * @param Twig_LexerInterface A Twig_LexerInterface instance |
---|
| 361 | */ |
---|
| 362 | public function setLexer(Twig_LexerInterface $lexer) |
---|
| 363 | { |
---|
| 364 | $this->lexer = $lexer; |
---|
| 365 | } |
---|
| 366 | |
---|
| 367 | /** |
---|
| 368 | * Tokenizes a source code. |
---|
| 369 | * |
---|
| 370 | * @param string $source The template source code |
---|
| 371 | * @param string $name The template name |
---|
| 372 | * |
---|
| 373 | * @return Twig_TokenStream A Twig_TokenStream instance |
---|
| 374 | */ |
---|
| 375 | public function tokenize($source, $name = null) |
---|
| 376 | { |
---|
| 377 | return $this->getLexer()->tokenize($source, $name); |
---|
| 378 | } |
---|
| 379 | |
---|
| 380 | /** |
---|
| 381 | * Gets the Parser instance. |
---|
| 382 | * |
---|
| 383 | * @return Twig_ParserInterface A Twig_ParserInterface instance |
---|
| 384 | */ |
---|
| 385 | public function getParser() |
---|
| 386 | { |
---|
| 387 | if (null === $this->parser) { |
---|
| 388 | $this->parser = new Twig_Parser($this); |
---|
| 389 | } |
---|
| 390 | |
---|
| 391 | return $this->parser; |
---|
| 392 | } |
---|
| 393 | |
---|
| 394 | /** |
---|
| 395 | * Sets the Parser instance. |
---|
| 396 | * |
---|
| 397 | * @param Twig_ParserInterface A Twig_ParserInterface instance |
---|
| 398 | */ |
---|
| 399 | public function setParser(Twig_ParserInterface $parser) |
---|
| 400 | { |
---|
| 401 | $this->parser = $parser; |
---|
| 402 | } |
---|
| 403 | |
---|
| 404 | /** |
---|
| 405 | * Parses a token stream. |
---|
| 406 | * |
---|
| 407 | * @param Twig_TokenStream $tokens A Twig_TokenStream instance |
---|
| 408 | * |
---|
| 409 | * @return Twig_Node_Module A Node tree |
---|
| 410 | */ |
---|
| 411 | public function parse(Twig_TokenStream $tokens) |
---|
| 412 | { |
---|
| 413 | return $this->getParser()->parse($tokens); |
---|
| 414 | } |
---|
| 415 | |
---|
| 416 | /** |
---|
| 417 | * Gets the Compiler instance. |
---|
| 418 | * |
---|
| 419 | * @return Twig_CompilerInterface A Twig_CompilerInterface instance |
---|
| 420 | */ |
---|
| 421 | public function getCompiler() |
---|
| 422 | { |
---|
| 423 | if (null === $this->compiler) { |
---|
| 424 | $this->compiler = new Twig_Compiler($this); |
---|
| 425 | } |
---|
| 426 | |
---|
| 427 | return $this->compiler; |
---|
| 428 | } |
---|
| 429 | |
---|
| 430 | /** |
---|
| 431 | * Sets the Compiler instance. |
---|
| 432 | * |
---|
| 433 | * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance |
---|
| 434 | */ |
---|
| 435 | public function setCompiler(Twig_CompilerInterface $compiler) |
---|
| 436 | { |
---|
| 437 | $this->compiler = $compiler; |
---|
| 438 | } |
---|
| 439 | |
---|
| 440 | /** |
---|
| 441 | * Compiles a Node. |
---|
| 442 | * |
---|
| 443 | * @param Twig_NodeInterface $node A Twig_NodeInterface instance |
---|
| 444 | * |
---|
| 445 | * @return string The compiled PHP source code |
---|
| 446 | */ |
---|
| 447 | public function compile(Twig_NodeInterface $node) |
---|
| 448 | { |
---|
| 449 | return $this->getCompiler()->compile($node)->getSource(); |
---|
| 450 | } |
---|
| 451 | |
---|
| 452 | /** |
---|
| 453 | * Compiles a template source code. |
---|
| 454 | * |
---|
| 455 | * @param string $source The template source code |
---|
| 456 | * @param string $name The template name |
---|
| 457 | * |
---|
| 458 | * @return string The compiled PHP source code |
---|
| 459 | */ |
---|
| 460 | public function compileSource($source, $name = null) |
---|
| 461 | { |
---|
| 462 | return $this->compile($this->parse($this->tokenize($source, $name))); |
---|
| 463 | } |
---|
| 464 | |
---|
| 465 | /** |
---|
| 466 | * Sets the Loader instance. |
---|
| 467 | * |
---|
| 468 | * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance |
---|
| 469 | */ |
---|
| 470 | public function setLoader(Twig_LoaderInterface $loader) |
---|
| 471 | { |
---|
| 472 | $this->loader = $loader; |
---|
| 473 | } |
---|
| 474 | |
---|
| 475 | /** |
---|
| 476 | * Gets the Loader instance. |
---|
| 477 | * |
---|
| 478 | * @return Twig_LoaderInterface A Twig_LoaderInterface instance |
---|
| 479 | */ |
---|
| 480 | public function getLoader() |
---|
| 481 | { |
---|
| 482 | return $this->loader; |
---|
| 483 | } |
---|
| 484 | |
---|
| 485 | /** |
---|
| 486 | * Sets the default template charset. |
---|
| 487 | * |
---|
| 488 | * @param string $charset The default charset |
---|
| 489 | */ |
---|
| 490 | public function setCharset($charset) |
---|
| 491 | { |
---|
| 492 | $this->charset = $charset; |
---|
| 493 | } |
---|
| 494 | |
---|
| 495 | /** |
---|
| 496 | * Gets the default template charset. |
---|
| 497 | * |
---|
| 498 | * @return string The default charset |
---|
| 499 | */ |
---|
| 500 | public function getCharset() |
---|
| 501 | { |
---|
| 502 | return $this->charset; |
---|
| 503 | } |
---|
| 504 | |
---|
| 505 | /** |
---|
| 506 | * Initializes the runtime environment. |
---|
| 507 | */ |
---|
| 508 | public function initRuntime() |
---|
| 509 | { |
---|
| 510 | $this->runtimeInitialized = true; |
---|
| 511 | |
---|
| 512 | foreach ($this->getExtensions() as $extension) { |
---|
| 513 | $extension->initRuntime($this); |
---|
| 514 | } |
---|
| 515 | } |
---|
| 516 | |
---|
| 517 | /** |
---|
| 518 | * Returns true if the given extension is registered. |
---|
| 519 | * |
---|
| 520 | * @param string $name The extension name |
---|
| 521 | * |
---|
| 522 | * @return Boolean Whether the extension is registered or not |
---|
| 523 | */ |
---|
| 524 | public function hasExtension($name) |
---|
| 525 | { |
---|
| 526 | return isset($this->extensions[$name]); |
---|
| 527 | } |
---|
| 528 | |
---|
| 529 | /** |
---|
| 530 | * Gets an extension by name. |
---|
| 531 | * |
---|
| 532 | * @param string $name The extension name |
---|
| 533 | * |
---|
| 534 | * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance |
---|
| 535 | */ |
---|
| 536 | public function getExtension($name) |
---|
| 537 | { |
---|
| 538 | if (!isset($this->extensions[$name])) { |
---|
| 539 | throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name)); |
---|
| 540 | } |
---|
| 541 | |
---|
| 542 | return $this->extensions[$name]; |
---|
| 543 | } |
---|
| 544 | |
---|
| 545 | /** |
---|
| 546 | * Registers an extension. |
---|
| 547 | * |
---|
| 548 | * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance |
---|
| 549 | */ |
---|
| 550 | public function addExtension(Twig_ExtensionInterface $extension) |
---|
| 551 | { |
---|
| 552 | $this->extensions[$extension->getName()] = $extension; |
---|
| 553 | } |
---|
| 554 | |
---|
| 555 | /** |
---|
| 556 | * Removes an extension by name. |
---|
| 557 | * |
---|
| 558 | * @param string $name The extension name |
---|
| 559 | */ |
---|
| 560 | public function removeExtension($name) |
---|
| 561 | { |
---|
| 562 | unset($this->extensions[$name]); |
---|
| 563 | } |
---|
| 564 | |
---|
| 565 | /** |
---|
| 566 | * Registers an array of extensions. |
---|
| 567 | * |
---|
| 568 | * @param array $extensions An array of extensions |
---|
| 569 | */ |
---|
| 570 | public function setExtensions(array $extensions) |
---|
| 571 | { |
---|
| 572 | foreach ($extensions as $extension) { |
---|
| 573 | $this->addExtension($extension); |
---|
| 574 | } |
---|
| 575 | } |
---|
| 576 | |
---|
| 577 | /** |
---|
| 578 | * Returns all registered extensions. |
---|
| 579 | * |
---|
| 580 | * @return array An array of extensions |
---|
| 581 | */ |
---|
| 582 | public function getExtensions() |
---|
| 583 | { |
---|
| 584 | return $this->extensions; |
---|
| 585 | } |
---|
| 586 | |
---|
| 587 | /** |
---|
| 588 | * Registers a Token Parser. |
---|
| 589 | * |
---|
| 590 | * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance |
---|
| 591 | */ |
---|
| 592 | public function addTokenParser(Twig_TokenParserInterface $parser) |
---|
| 593 | { |
---|
| 594 | if (null === $this->parsers) { |
---|
| 595 | $this->getTokenParsers(); |
---|
| 596 | } |
---|
| 597 | |
---|
| 598 | $this->parsers->addTokenParser($parser); |
---|
| 599 | } |
---|
| 600 | |
---|
| 601 | /** |
---|
| 602 | * Gets the registered Token Parsers. |
---|
| 603 | * |
---|
| 604 | * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances |
---|
| 605 | */ |
---|
| 606 | public function getTokenParsers() |
---|
| 607 | { |
---|
| 608 | if (null === $this->parsers) { |
---|
| 609 | $this->parsers = new Twig_TokenParserBroker; |
---|
| 610 | foreach ($this->getExtensions() as $extension) { |
---|
| 611 | $parsers = $extension->getTokenParsers(); |
---|
| 612 | foreach($parsers as $parser) { |
---|
| 613 | if ($parser instanceof Twig_TokenParserInterface) { |
---|
| 614 | $this->parsers->addTokenParser($parser); |
---|
| 615 | } else if ($parser instanceof Twig_TokenParserBrokerInterface) { |
---|
| 616 | $this->parsers->addTokenParserBroker($parser); |
---|
| 617 | } else { |
---|
| 618 | throw new Twig_Error_Runtime('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances'); |
---|
| 619 | } |
---|
| 620 | } |
---|
| 621 | } |
---|
| 622 | } |
---|
| 623 | |
---|
| 624 | return $this->parsers; |
---|
| 625 | } |
---|
| 626 | |
---|
| 627 | /** |
---|
| 628 | * Registers a Node Visitor. |
---|
| 629 | * |
---|
| 630 | * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance |
---|
| 631 | */ |
---|
| 632 | public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) |
---|
| 633 | { |
---|
| 634 | if (null === $this->visitors) { |
---|
| 635 | $this->getNodeVisitors(); |
---|
| 636 | } |
---|
| 637 | |
---|
| 638 | $this->visitors[] = $visitor; |
---|
| 639 | } |
---|
| 640 | |
---|
| 641 | /** |
---|
| 642 | * Gets the registered Node Visitors. |
---|
| 643 | * |
---|
| 644 | * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances |
---|
| 645 | */ |
---|
| 646 | public function getNodeVisitors() |
---|
| 647 | { |
---|
| 648 | if (null === $this->visitors) { |
---|
| 649 | $this->visitors = array(); |
---|
| 650 | foreach ($this->getExtensions() as $extension) { |
---|
| 651 | $this->visitors = array_merge($this->visitors, $extension->getNodeVisitors()); |
---|
| 652 | } |
---|
| 653 | } |
---|
| 654 | |
---|
| 655 | return $this->visitors; |
---|
| 656 | } |
---|
| 657 | |
---|
| 658 | /** |
---|
| 659 | * Registers a Filter. |
---|
| 660 | * |
---|
| 661 | * @param string $name The filter name |
---|
| 662 | * @param Twig_FilterInterface $visitor A Twig_FilterInterface instance |
---|
| 663 | */ |
---|
| 664 | public function addFilter($name, Twig_FilterInterface $filter) |
---|
| 665 | { |
---|
| 666 | if (null === $this->filters) { |
---|
| 667 | $this->loadFilters(); |
---|
| 668 | } |
---|
| 669 | |
---|
| 670 | $this->filters[$name] = $filter; |
---|
| 671 | } |
---|
| 672 | |
---|
| 673 | /** |
---|
| 674 | * Get a filter by name. |
---|
| 675 | * |
---|
| 676 | * Subclasses may override this method and load filters differently; |
---|
| 677 | * so no list of filters is available. |
---|
| 678 | * |
---|
| 679 | * @param string $name The filter name |
---|
| 680 | * |
---|
| 681 | * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exists |
---|
| 682 | */ |
---|
| 683 | public function getFilter($name) |
---|
| 684 | { |
---|
| 685 | if (null === $this->filters) { |
---|
| 686 | $this->loadFilters(); |
---|
| 687 | } |
---|
| 688 | |
---|
| 689 | if (isset($this->filters[$name])) { |
---|
| 690 | return $this->filters[$name]; |
---|
| 691 | } |
---|
| 692 | |
---|
| 693 | foreach ($this->filterCallbacks as $callback) { |
---|
| 694 | if (false !== $filter = call_user_func($callback, $name)) { |
---|
| 695 | return $filter; |
---|
| 696 | } |
---|
| 697 | } |
---|
| 698 | |
---|
| 699 | return false; |
---|
| 700 | } |
---|
| 701 | |
---|
| 702 | public function registerUndefinedFilterCallback($callable) |
---|
| 703 | { |
---|
| 704 | $this->filterCallbacks[] = $callable; |
---|
| 705 | } |
---|
| 706 | |
---|
| 707 | /** |
---|
| 708 | * Gets the registered Filters. |
---|
| 709 | * |
---|
| 710 | * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances |
---|
| 711 | */ |
---|
| 712 | protected function loadFilters() |
---|
| 713 | { |
---|
| 714 | $this->filters = array(); |
---|
| 715 | foreach ($this->getExtensions() as $extension) { |
---|
| 716 | $this->filters = array_merge($this->filters, $extension->getFilters()); |
---|
| 717 | } |
---|
| 718 | } |
---|
| 719 | |
---|
| 720 | /** |
---|
| 721 | * Registers a Test. |
---|
| 722 | * |
---|
| 723 | * @param string $name The test name |
---|
| 724 | * @param Twig_TestInterface $visitor A Twig_TestInterface instance |
---|
| 725 | */ |
---|
| 726 | public function addTest($name, Twig_TestInterface $test) |
---|
| 727 | { |
---|
| 728 | if (null === $this->tests) { |
---|
| 729 | $this->getTests(); |
---|
| 730 | } |
---|
| 731 | |
---|
| 732 | $this->tests[$name] = $test; |
---|
| 733 | } |
---|
| 734 | |
---|
| 735 | /** |
---|
| 736 | * Gets the registered Tests. |
---|
| 737 | * |
---|
| 738 | * @return Twig_TestInterface[] An array of Twig_TestInterface instances |
---|
| 739 | */ |
---|
| 740 | public function getTests() |
---|
| 741 | { |
---|
| 742 | if (null === $this->tests) { |
---|
| 743 | $this->tests = array(); |
---|
| 744 | foreach ($this->getExtensions() as $extension) { |
---|
| 745 | $this->tests = array_merge($this->tests, $extension->getTests()); |
---|
| 746 | } |
---|
| 747 | } |
---|
| 748 | |
---|
| 749 | return $this->tests; |
---|
| 750 | } |
---|
| 751 | |
---|
| 752 | /** |
---|
| 753 | * Registers a Function. |
---|
| 754 | * |
---|
| 755 | * @param string $name The function name |
---|
| 756 | * @param Twig_FunctionInterface $function A Twig_FunctionInterface instance |
---|
| 757 | */ |
---|
| 758 | public function addFunction($name, Twig_FunctionInterface $function) |
---|
| 759 | { |
---|
| 760 | if (null === $this->functions) { |
---|
| 761 | $this->loadFunctions(); |
---|
| 762 | } |
---|
| 763 | |
---|
| 764 | $this->functions[$name] = $function; |
---|
| 765 | } |
---|
| 766 | |
---|
| 767 | /** |
---|
| 768 | * Get a function by name. |
---|
| 769 | * |
---|
| 770 | * Subclasses may override this method and load functions differently; |
---|
| 771 | * so no list of functions is available. |
---|
| 772 | * |
---|
| 773 | * @param string $name function name |
---|
| 774 | * |
---|
| 775 | * @return Twig_Function|false A Twig_Function instance or false if the function does not exists |
---|
| 776 | */ |
---|
| 777 | public function getFunction($name) |
---|
| 778 | { |
---|
| 779 | if (null === $this->functions) { |
---|
| 780 | $this->loadFunctions(); |
---|
| 781 | } |
---|
| 782 | |
---|
| 783 | if (isset($this->functions[$name])) { |
---|
| 784 | return $this->functions[$name]; |
---|
| 785 | } |
---|
| 786 | |
---|
| 787 | foreach ($this->functionCallbacks as $callback) { |
---|
| 788 | if (false !== $function = call_user_func($callback, $name)) { |
---|
| 789 | return $function; |
---|
| 790 | } |
---|
| 791 | } |
---|
| 792 | |
---|
| 793 | return false; |
---|
| 794 | } |
---|
| 795 | |
---|
| 796 | public function registerUndefinedFunctionCallback($callable) |
---|
| 797 | { |
---|
| 798 | $this->functionCallbacks[] = $callable; |
---|
| 799 | } |
---|
| 800 | |
---|
| 801 | protected function loadFunctions() { |
---|
| 802 | $this->functions = array(); |
---|
| 803 | foreach ($this->getExtensions() as $extension) { |
---|
| 804 | $this->functions = array_merge($this->functions, $extension->getFunctions()); |
---|
| 805 | } |
---|
| 806 | } |
---|
| 807 | |
---|
| 808 | /** |
---|
| 809 | * Registers a Global. |
---|
| 810 | * |
---|
| 811 | * @param string $name The global name |
---|
| 812 | * @param mixed $value The global value |
---|
| 813 | */ |
---|
| 814 | public function addGlobal($name, $value) |
---|
| 815 | { |
---|
| 816 | if (null === $this->globals) { |
---|
| 817 | $this->getGlobals(); |
---|
| 818 | } |
---|
| 819 | |
---|
| 820 | $this->globals[$name] = $value; |
---|
| 821 | } |
---|
| 822 | |
---|
| 823 | /** |
---|
| 824 | * Gets the registered Globals. |
---|
| 825 | * |
---|
| 826 | * @return array An array of globals |
---|
| 827 | */ |
---|
| 828 | public function getGlobals() |
---|
| 829 | { |
---|
| 830 | if (null === $this->globals) { |
---|
| 831 | $this->globals = array(); |
---|
| 832 | foreach ($this->getExtensions() as $extension) { |
---|
| 833 | $this->globals = array_merge($this->globals, $extension->getGlobals()); |
---|
| 834 | } |
---|
| 835 | } |
---|
| 836 | |
---|
| 837 | return $this->globals; |
---|
| 838 | } |
---|
| 839 | |
---|
| 840 | /** |
---|
| 841 | * Gets the registered unary Operators. |
---|
| 842 | * |
---|
| 843 | * @return array An array of unary operators |
---|
| 844 | */ |
---|
| 845 | public function getUnaryOperators() |
---|
| 846 | { |
---|
| 847 | if (null === $this->unaryOperators) { |
---|
| 848 | $this->initOperators(); |
---|
| 849 | } |
---|
| 850 | |
---|
| 851 | return $this->unaryOperators; |
---|
| 852 | } |
---|
| 853 | |
---|
| 854 | /** |
---|
| 855 | * Gets the registered binary Operators. |
---|
| 856 | * |
---|
| 857 | * @return array An array of binary operators |
---|
| 858 | */ |
---|
| 859 | public function getBinaryOperators() |
---|
| 860 | { |
---|
| 861 | if (null === $this->binaryOperators) { |
---|
| 862 | $this->initOperators(); |
---|
| 863 | } |
---|
| 864 | |
---|
| 865 | return $this->binaryOperators; |
---|
| 866 | } |
---|
| 867 | |
---|
| 868 | protected function initOperators() |
---|
| 869 | { |
---|
| 870 | $this->unaryOperators = array(); |
---|
| 871 | $this->binaryOperators = array(); |
---|
| 872 | foreach ($this->getExtensions() as $extension) { |
---|
| 873 | $operators = $extension->getOperators(); |
---|
| 874 | |
---|
| 875 | if (!$operators) { |
---|
| 876 | continue; |
---|
| 877 | } |
---|
| 878 | |
---|
| 879 | if (2 !== count($operators)) { |
---|
| 880 | throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension))); |
---|
| 881 | } |
---|
| 882 | |
---|
| 883 | $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); |
---|
| 884 | $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]); |
---|
| 885 | } |
---|
| 886 | } |
---|
| 887 | |
---|
| 888 | protected function writeCacheFile($file, $content) |
---|
| 889 | { |
---|
| 890 | if (!is_dir(dirname($file))) { |
---|
| 891 | mkdir(dirname($file), 0777, true); |
---|
| 892 | } |
---|
| 893 | |
---|
| 894 | $tmpFile = tempnam(dirname($file), basename($file)); |
---|
| 895 | if (false !== @file_put_contents($tmpFile, $content)) { |
---|
| 896 | // rename does not work on Win32 before 5.2.6 |
---|
| 897 | if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) { |
---|
| 898 | chmod($file, 0644); |
---|
| 899 | |
---|
| 900 | return; |
---|
| 901 | } |
---|
| 902 | } |
---|
| 903 | |
---|
| 904 | throw new Twig_Error_Runtime(sprintf('Failed to write cache file "%s".', $file)); |
---|
| 905 | } |
---|
| 906 | } |
---|