Dotclear

source: inc/libs/Twig/Template.php @ 1149:1657e862089c

Revision 1149:1657e862089c, 14.2 KB checked in by dsls, 12 years ago (diff)

Fixed unix case-sensitive twig directory

Line 
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Default base class for compiled templates.
15 *
16 * @package twig
17 * @author  Fabien Potencier <fabien@symfony.com>
18 */
19abstract class Twig_Template implements Twig_TemplateInterface
20{
21    protected static $cache = array();
22
23    protected $parent;
24    protected $parents;
25    protected $env;
26    protected $blocks;
27    protected $traits;
28
29    /**
30     * Constructor.
31     *
32     * @param Twig_Environment $env A Twig_Environment instance
33     */
34    public function __construct(Twig_Environment $env)
35    {
36        $this->env = $env;
37        $this->blocks = array();
38        $this->traits = array();
39    }
40
41    /**
42     * Returns the template name.
43     *
44     * @return string The template name
45     */
46    abstract public function getTemplateName();
47
48    /**
49     * {@inheritdoc}
50     */
51    public function getEnvironment()
52    {
53        return $this->env;
54    }
55
56    /**
57     * Returns the parent template.
58     *
59     * This method is for internal use only and should never be called
60     * directly.
61     *
62     * @return Twig_TemplateInterface|false The parent template or false if there is no parent
63     */
64    public function getParent(array $context)
65    {
66        if (null !== $this->parent) {
67            return $this->parent;
68        }
69
70        $parent = $this->doGetParent($context);
71        if (false === $parent) {
72            return false;
73        } elseif ($parent instanceof Twig_Template) {
74            $name = $parent->getTemplateName();
75            $this->parents[$name] = $parent;
76            $parent = $name;
77        } elseif (!isset($this->parents[$parent])) {
78            $this->parents[$parent] = $this->env->loadTemplate($parent);
79        }
80
81        return $this->parents[$parent];
82    }
83
84    protected function doGetParent(array $context)
85    {
86        return false;
87    }
88
89    public function isTraitable()
90    {
91        return true;
92    }
93
94    /**
95     * Displays a parent block.
96     *
97     * This method is for internal use only and should never be called
98     * directly.
99     *
100     * @param string $name    The block name to display from the parent
101     * @param array  $context The context
102     * @param array  $blocks  The current set of blocks
103     */
104    public function displayParentBlock($name, array $context, array $blocks = array())
105    {
106        $name = (string) $name;
107
108        if (isset($this->traits[$name])) {
109            $this->traits[$name][0]->displayBlock($name, $context, $blocks);
110        } elseif (false !== $parent = $this->getParent($context)) {
111            $parent->displayBlock($name, $context, $blocks);
112        } else {
113            throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
114        }
115    }
116
117    /**
118     * Displays a block.
119     *
120     * This method is for internal use only and should never be called
121     * directly.
122     *
123     * @param string $name    The block name to display
124     * @param array  $context The context
125     * @param array  $blocks  The current set of blocks
126     */
127    public function displayBlock($name, array $context, array $blocks = array())
128    {
129        $name = (string) $name;
130
131        if (isset($blocks[$name])) {
132            $b = $blocks;
133            unset($b[$name]);
134            call_user_func($blocks[$name], $context, $b);
135        } elseif (isset($this->blocks[$name])) {
136            call_user_func($this->blocks[$name], $context, $blocks);
137        } elseif (false !== $parent = $this->getParent($context)) {
138            $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks));
139        }
140    }
141
142    /**
143     * Renders a parent block.
144     *
145     * This method is for internal use only and should never be called
146     * directly.
147     *
148     * @param string $name    The block name to render from the parent
149     * @param array  $context The context
150     * @param array  $blocks  The current set of blocks
151     *
152     * @return string The rendered block
153     */
154    public function renderParentBlock($name, array $context, array $blocks = array())
155    {
156        ob_start();
157        $this->displayParentBlock($name, $context, $blocks);
158
159        return ob_get_clean();
160    }
161
162    /**
163     * Renders a block.
164     *
165     * This method is for internal use only and should never be called
166     * directly.
167     *
168     * @param string $name    The block name to render
169     * @param array  $context The context
170     * @param array  $blocks  The current set of blocks
171     *
172     * @return string The rendered block
173     */
174    public function renderBlock($name, array $context, array $blocks = array())
175    {
176        ob_start();
177        $this->displayBlock($name, $context, $blocks);
178
179        return ob_get_clean();
180    }
181
182    /**
183     * Returns whether a block exists or not.
184     *
185     * This method is for internal use only and should never be called
186     * directly.
187     *
188     * This method does only return blocks defined in the current template
189     * or defined in "used" traits.
190     *
191     * It does not return blocks from parent templates as the parent
192     * template name can be dynamic, which is only known based on the
193     * current context.
194     *
195     * @param string $name The block name
196     *
197     * @return Boolean true if the block exists, false otherwise
198     */
199    public function hasBlock($name)
200    {
201        return isset($this->blocks[(string) $name]);
202    }
203
204    /**
205     * Returns all block names.
206     *
207     * This method is for internal use only and should never be called
208     * directly.
209     *
210     * @return array An array of block names
211     *
212     * @see hasBlock
213     */
214    public function getBlockNames()
215    {
216        return array_keys($this->blocks);
217    }
218
219    /**
220     * Returns all blocks.
221     *
222     * This method is for internal use only and should never be called
223     * directly.
224     *
225     * @return array An array of blocks
226     *
227     * @see hasBlock
228     */
229    public function getBlocks()
230    {
231        return $this->blocks;
232    }
233
234    /**
235     * {@inheritdoc}
236     */
237    public function display(array $context, array $blocks = array())
238    {
239        $this->displayWithErrorHandling($this->env->mergeGlobals($context), $blocks);
240    }
241
242    /**
243     * {@inheritdoc}
244     */
245    public function render(array $context)
246    {
247        $level = ob_get_level();
248        ob_start();
249        try {
250            $this->display($context);
251        } catch (Exception $e) {
252            while (ob_get_level() > $level) {
253                ob_end_clean();
254            }
255
256            throw $e;
257        }
258
259        return ob_get_clean();
260    }
261
262    protected function displayWithErrorHandling(array $context, array $blocks = array())
263    {
264        try {
265            $this->doDisplay($context, $blocks);
266        } catch (Twig_Error $e) {
267            if (!$e->getTemplateFile()) {
268                $e->setTemplateFile($this->getTemplateName());
269            }
270
271            // this is mostly useful for Twig_Error_Loader exceptions
272            // see Twig_Error_Loader
273            if (false === $e->getTemplateLine()) {
274                $e->setTemplateLine(-1);
275                $e->guess();
276            }
277
278            throw $e;
279        } catch (Exception $e) {
280            throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e);
281        }
282    }
283
284    /**
285     * Auto-generated method to display the template with the given context.
286     *
287     * @param array $context An array of parameters to pass to the template
288     * @param array $blocks  An array of blocks to pass to the template
289     */
290    abstract protected function doDisplay(array $context, array $blocks = array());
291
292    /**
293     * Returns a variable from the context.
294     *
295     * This method is for internal use only and should never be called
296     * directly.
297     *
298     * This method should not be overridden in a sub-class as this is an
299     * implementation detail that has been introduced to optimize variable
300     * access for versions of PHP before 5.4. This is not a way to override
301     * the way to get a variable value.
302     *
303     * @param array   $context           The context
304     * @param string  $item              The variable to return from the context
305     * @param Boolean $ignoreStrictCheck Whether to ignore the strict variable check or not
306     *
307     * @return The content of the context variable
308     *
309     * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
310     */
311    final protected function getContext($context, $item, $ignoreStrictCheck = false)
312    {
313        if (!array_key_exists($item, $context)) {
314            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
315                return null;
316            }
317
318            throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName());
319        }
320
321        return $context[$item];
322    }
323
324    /**
325     * Returns the attribute value for a given array/object.
326     *
327     * @param mixed   $object            The object or array from where to get the item
328     * @param mixed   $item              The item to get from the array or object
329     * @param array   $arguments         An array of arguments to pass if the item is an object method
330     * @param string  $type              The type of attribute (@see Twig_TemplateInterface)
331     * @param Boolean $isDefinedTest     Whether this is only a defined check
332     * @param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not
333     *
334     * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
335     *
336     * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
337     */
338    protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
339    {
340        $item = ctype_digit((string) $item) ? (int) $item : (string) $item;
341
342        // array
343        if (Twig_TemplateInterface::METHOD_CALL !== $type) {
344            if ((is_array($object) && array_key_exists($item, $object))
345                || ($object instanceof ArrayAccess && isset($object[$item]))
346            ) {
347                if ($isDefinedTest) {
348                    return true;
349                }
350
351                return $object[$item];
352            }
353
354            if (Twig_TemplateInterface::ARRAY_CALL === $type) {
355                if ($isDefinedTest) {
356                    return false;
357                }
358
359                if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
360                    return null;
361                }
362
363                if (is_object($object)) {
364                    throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
365                } elseif (is_array($object)) {
366                    throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))), -1, $this->getTemplateName());
367                } else {
368                    throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a "%s" variable', $item, gettype($object)), -1, $this->getTemplateName());
369                }
370            }
371        }
372
373        if (!is_object($object)) {
374            if ($isDefinedTest) {
375                return false;
376            }
377
378            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
379                return null;
380            }
381
382            throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, is_array($object) ? 'Array' : $object), -1, $this->getTemplateName());
383        }
384
385        $class = get_class($object);
386
387        // object property
388        if (Twig_TemplateInterface::METHOD_CALL !== $type) {
389            if (isset($object->$item) || array_key_exists($item, $object)) {
390                if ($isDefinedTest) {
391                    return true;
392                }
393
394                if ($this->env->hasExtension('sandbox')) {
395                    $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
396                }
397
398                return $object->$item;
399            }
400        }
401
402        // object method
403        if (!isset(self::$cache[$class]['methods'])) {
404            self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
405        }
406
407        $lcItem = strtolower($item);
408        if (isset(self::$cache[$class]['methods'][$lcItem])) {
409            $method = $item;
410        } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
411            $method = 'get'.$item;
412        } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
413            $method = 'is'.$item;
414        } elseif (isset(self::$cache[$class]['methods']['__call'])) {
415            $method = $item;
416        } else {
417            if ($isDefinedTest) {
418                return false;
419            }
420
421            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
422                return null;
423            }
424
425            throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
426        }
427
428        if ($isDefinedTest) {
429            return true;
430        }
431
432        if ($this->env->hasExtension('sandbox')) {
433            $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
434        }
435
436        $ret = call_user_func_array(array($object, $method), $arguments);
437
438        // useful when calling a template method from a template
439        // this is not supported but unfortunately heavily used in the Symfony profiler
440        if ($object instanceof Twig_TemplateInterface) {
441            return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
442        }
443
444        return $ret;
445    }
446
447    /**
448     * This method is only useful when testing Twig. Do not use it.
449     */
450    public static function clearCache()
451    {
452        self::$cache = array();
453    }
454}
Note: See TracBrowser for help on using the repository browser.

Sites map