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