PageRenderTime 125ms CodeModel.GetById 60ms app.highlight 17ms RepoModel.GetById 19ms app.codeStats 5ms

/symfony2/vendor/twig/lib/Twig/Template.php

http://github.com/eryx/php-framework-benchmark
PHP | 442 lines | 219 code | 54 blank | 169 comment | 39 complexity | 58b1546f36edec85eb61bb3d35085c09 MD5 | raw file
  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    static protected $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            throw $e;
268        } catch (Exception $e) {
269            throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e);
270        }
271    }
272
273    /**
274     * Auto-generated method to display the template with the given context.
275     *
276     * @param array $context An array of parameters to pass to the template
277     * @param array $blocks  An array of blocks to pass to the template
278     */
279    abstract protected function doDisplay(array $context, array $blocks = array());
280
281    /**
282     * Returns a variable from the context.
283     *
284     * This method is for internal use only and should never be called
285     * directly.
286     *
287     * This method should not be overriden in a sub-class as this is an
288     * implementation detail that has been introduced to optimize variable
289     * access for versions of PHP before 5.4. This is not a way to override
290     * the way to get a variable value.
291     *
292     * @param array   $context           The context
293     * @param string  $item              The variable to return from the context
294     * @param Boolean $ignoreStrictCheck Whether to ignore the strict variable check or not
295     *
296     * @return The content of the context variable
297     *
298     * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
299     */
300    final protected function getContext($context, $item, $ignoreStrictCheck = false)
301    {
302        if (!array_key_exists($item, $context)) {
303            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
304                return null;
305            }
306
307            throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item));
308        }
309
310        return $context[$item];
311    }
312
313    /**
314     * Returns the attribute value for a given array/object.
315     *
316     * @param mixed   $object            The object or array from where to get the item
317     * @param mixed   $item              The item to get from the array or object
318     * @param array   $arguments         An array of arguments to pass if the item is an object method
319     * @param string  $type              The type of attribute (@see Twig_TemplateInterface)
320     * @param Boolean $isDefinedTest     Whether this is only a defined check
321     * @param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not
322     *
323     * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
324     *
325     * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
326     */
327    protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
328    {
329        $item = ctype_digit((string) $item) ? (int) $item : (string) $item;
330
331        // array
332        if (Twig_TemplateInterface::METHOD_CALL !== $type) {
333            if ((is_array($object) && array_key_exists($item, $object))
334                || ($object instanceof ArrayAccess && isset($object[$item]))
335            ) {
336                if ($isDefinedTest) {
337                    return true;
338                }
339
340                return $object[$item];
341            }
342
343            if (Twig_TemplateInterface::ARRAY_CALL === $type) {
344                if ($isDefinedTest) {
345                    return false;
346                }
347
348                if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
349                    return null;
350                }
351
352                if (is_object($object)) {
353                    throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)));
354                } elseif (is_array($object)) {
355                    throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))));
356                } else {
357                    throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a "%s" variable', $item, gettype($object)));
358                }
359            }
360        }
361
362        if (!is_object($object)) {
363            if ($isDefinedTest) {
364                return false;
365            }
366
367            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
368                return null;
369            }
370
371            throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, is_array($object) ? 'Array' : $object));
372        }
373
374        $class = get_class($object);
375
376        // object property
377        if (Twig_TemplateInterface::METHOD_CALL !== $type) {
378            if (isset($object->$item) || array_key_exists($item, $object)) {
379                if ($isDefinedTest) {
380                    return true;
381                }
382
383                if ($this->env->hasExtension('sandbox')) {
384                    $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
385                }
386
387                return $object->$item;
388            }
389        }
390
391        // object method
392        if (!isset(self::$cache[$class]['methods'])) {
393            self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
394        }
395
396        $lcItem = strtolower($item);
397        if (isset(self::$cache[$class]['methods'][$lcItem])) {
398            $method = $item;
399        } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
400            $method = 'get'.$item;
401        } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
402            $method = 'is'.$item;
403        } elseif (isset(self::$cache[$class]['methods']['__call'])) {
404            $method = $item;
405        } else {
406            if ($isDefinedTest) {
407                return false;
408            }
409
410            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
411                return null;
412            }
413
414            throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)));
415        }
416
417        if ($isDefinedTest) {
418            return true;
419        }
420
421        if ($this->env->hasExtension('sandbox')) {
422            $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
423        }
424
425        $ret = call_user_func_array(array($object, $method), $arguments);
426
427        // hack to be removed when macro calls are refactored
428        if ($object instanceof Twig_TemplateInterface) {
429            return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
430        }
431
432        return $ret;
433    }
434
435    /**
436     * This method is only useful when testing Twig. Do not use it.
437     */
438    static public function clearCache()
439    {
440        self::$cache = array();
441    }
442}