source: pro-violet-viettel/sourcecode/application/third_party/Twig/Environment.php @ 345

Last change on this file since 345 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 23.8 KB
Line 
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 */
18class 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}
Note: See TracBrowser for help on using the repository browser.