shop.balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

Template.php (21231B)


      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  * @author Fabien Potencier <fabien@symfony.com>
     17  */
     18 abstract class Twig_Template implements Twig_TemplateInterface
     19 {
     20     protected static $cache = array();
     21 
     22     protected $parent;
     23     protected $parents = array();
     24     protected $env;
     25     protected $blocks = array();
     26     protected $traits = array();
     27 
     28     /**
     29      * Constructor.
     30      *
     31      * @param Twig_Environment $env A Twig_Environment instance
     32      */
     33     public function __construct(Twig_Environment $env)
     34     {
     35         $this->env = $env;
     36     }
     37 
     38     /**
     39      * Returns the template name.
     40      *
     41      * @return string The template name
     42      */
     43     abstract public function getTemplateName();
     44 
     45     /**
     46      * @deprecated since 1.20 (to be removed in 2.0)
     47      */
     48     public function getEnvironment()
     49     {
     50         @trigger_error('The '.__METHOD__.' method is deprecated since version 1.20 and will be removed in 2.0.', E_USER_DEPRECATED);
     51 
     52         return $this->env;
     53     }
     54 
     55     /**
     56      * Returns the parent template.
     57      *
     58      * This method is for internal use only and should never be called
     59      * directly.
     60      *
     61      * @param array $context
     62      *
     63      * @return Twig_TemplateInterface|false The parent template or false if there is no parent
     64      *
     65      * @internal
     66      */
     67     public function getParent(array $context)
     68     {
     69         if (null !== $this->parent) {
     70             return $this->parent;
     71         }
     72 
     73         try {
     74             $parent = $this->doGetParent($context);
     75 
     76             if (false === $parent) {
     77                 return false;
     78             }
     79 
     80             if ($parent instanceof self) {
     81                 return $this->parents[$parent->getTemplateName()] = $parent;
     82             }
     83 
     84             if (!isset($this->parents[$parent])) {
     85                 $this->parents[$parent] = $this->loadTemplate($parent);
     86             }
     87         } catch (Twig_Error_Loader $e) {
     88             $e->setTemplateFile(null);
     89             $e->guess();
     90 
     91             throw $e;
     92         }
     93 
     94         return $this->parents[$parent];
     95     }
     96 
     97     protected function doGetParent(array $context)
     98     {
     99         return false;
    100     }
    101 
    102     public function isTraitable()
    103     {
    104         return true;
    105     }
    106 
    107     /**
    108      * Displays a parent block.
    109      *
    110      * This method is for internal use only and should never be called
    111      * directly.
    112      *
    113      * @param string $name    The block name to display from the parent
    114      * @param array  $context The context
    115      * @param array  $blocks  The current set of blocks
    116      *
    117      * @internal
    118      */
    119     public function displayParentBlock($name, array $context, array $blocks = array())
    120     {
    121         $name = (string) $name;
    122 
    123         if (isset($this->traits[$name])) {
    124             $this->traits[$name][0]->displayBlock($name, $context, $blocks, false);
    125         } elseif (false !== $parent = $this->getParent($context)) {
    126             $parent->displayBlock($name, $context, $blocks, false);
    127         } else {
    128             throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
    129         }
    130     }
    131 
    132     /**
    133      * Displays a block.
    134      *
    135      * This method is for internal use only and should never be called
    136      * directly.
    137      *
    138      * @param string $name      The block name to display
    139      * @param array  $context   The context
    140      * @param array  $blocks    The current set of blocks
    141      * @param bool   $useBlocks Whether to use the current set of blocks
    142      *
    143      * @internal
    144      */
    145     public function displayBlock($name, array $context, array $blocks = array(), $useBlocks = true)
    146     {
    147         $name = (string) $name;
    148 
    149         if ($useBlocks && isset($blocks[$name])) {
    150             $template = $blocks[$name][0];
    151             $block = $blocks[$name][1];
    152         } elseif (isset($this->blocks[$name])) {
    153             $template = $this->blocks[$name][0];
    154             $block = $this->blocks[$name][1];
    155         } else {
    156             $template = null;
    157             $block = null;
    158         }
    159 
    160         if (null !== $template) {
    161             // avoid RCEs when sandbox is enabled
    162             if (!$template instanceof self) {
    163                 throw new LogicException('A block must be a method on a Twig_Template instance.');
    164             }
    165 
    166             try {
    167                 $template->$block($context, $blocks);
    168             } catch (Twig_Error $e) {
    169                 if (!$e->getTemplateFile()) {
    170                     $e->setTemplateFile($template->getTemplateName());
    171                 }
    172 
    173                 // this is mostly useful for Twig_Error_Loader exceptions
    174                 // see Twig_Error_Loader
    175                 if (false === $e->getTemplateLine()) {
    176                     $e->setTemplateLine(-1);
    177                     $e->guess();
    178                 }
    179 
    180                 throw $e;
    181             } catch (Exception $e) {
    182                 throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getTemplateName(), $e);
    183             }
    184         } elseif (false !== $parent = $this->getParent($context)) {
    185             $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
    186         }
    187     }
    188 
    189     /**
    190      * Renders a parent block.
    191      *
    192      * This method is for internal use only and should never be called
    193      * directly.
    194      *
    195      * @param string $name    The block name to render from the parent
    196      * @param array  $context The context
    197      * @param array  $blocks  The current set of blocks
    198      *
    199      * @return string The rendered block
    200      *
    201      * @internal
    202      */
    203     public function renderParentBlock($name, array $context, array $blocks = array())
    204     {
    205         ob_start();
    206         $this->displayParentBlock($name, $context, $blocks);
    207 
    208         return ob_get_clean();
    209     }
    210 
    211     /**
    212      * Renders a block.
    213      *
    214      * This method is for internal use only and should never be called
    215      * directly.
    216      *
    217      * @param string $name      The block name to render
    218      * @param array  $context   The context
    219      * @param array  $blocks    The current set of blocks
    220      * @param bool   $useBlocks Whether to use the current set of blocks
    221      *
    222      * @return string The rendered block
    223      *
    224      * @internal
    225      */
    226     public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true)
    227     {
    228         ob_start();
    229         $this->displayBlock($name, $context, $blocks, $useBlocks);
    230 
    231         return ob_get_clean();
    232     }
    233 
    234     /**
    235      * Returns whether a block exists or not.
    236      *
    237      * This method is for internal use only and should never be called
    238      * directly.
    239      *
    240      * This method does only return blocks defined in the current template
    241      * or defined in "used" traits.
    242      *
    243      * It does not return blocks from parent templates as the parent
    244      * template name can be dynamic, which is only known based on the
    245      * current context.
    246      *
    247      * @param string $name The block name
    248      *
    249      * @return bool true if the block exists, false otherwise
    250      *
    251      * @internal
    252      */
    253     public function hasBlock($name)
    254     {
    255         return isset($this->blocks[(string) $name]);
    256     }
    257 
    258     /**
    259      * Returns all block names.
    260      *
    261      * This method is for internal use only and should never be called
    262      * directly.
    263      *
    264      * @return array An array of block names
    265      *
    266      * @see hasBlock
    267      *
    268      * @internal
    269      */
    270     public function getBlockNames()
    271     {
    272         return array_keys($this->blocks);
    273     }
    274 
    275     protected function loadTemplate($template, $templateName = null, $line = null, $index = null)
    276     {
    277         try {
    278             if (is_array($template)) {
    279                 return $this->env->resolveTemplate($template);
    280             }
    281 
    282             if ($template instanceof self) {
    283                 return $template;
    284             }
    285 
    286             return $this->env->loadTemplate($template, $index);
    287         } catch (Twig_Error $e) {
    288             if (!$e->getTemplateFile()) {
    289                 $e->setTemplateFile($templateName ? $templateName : $this->getTemplateName());
    290             }
    291 
    292             if ($e->getTemplateLine()) {
    293                 throw $e;
    294             }
    295 
    296             if (!$line) {
    297                 $e->guess();
    298             } else {
    299                 $e->setTemplateLine($line);
    300             }
    301 
    302             throw $e;
    303         }
    304     }
    305 
    306     /**
    307      * Returns all blocks.
    308      *
    309      * This method is for internal use only and should never be called
    310      * directly.
    311      *
    312      * @return array An array of blocks
    313      *
    314      * @see hasBlock
    315      *
    316      * @internal
    317      */
    318     public function getBlocks()
    319     {
    320         return $this->blocks;
    321     }
    322 
    323     /**
    324      * Returns the template source code.
    325      *
    326      * @return string|null The template source code or null if it is not available
    327      */
    328     public function getSource()
    329     {
    330         $reflector = new ReflectionClass($this);
    331         $file = $reflector->getFileName();
    332 
    333         if (!file_exists($file)) {
    334             return;
    335         }
    336 
    337         $source = file($file, FILE_IGNORE_NEW_LINES);
    338         array_splice($source, 0, $reflector->getEndLine());
    339 
    340         $i = 0;
    341         while (isset($source[$i]) && '/* */' === substr_replace($source[$i], '', 3, -2)) {
    342             $source[$i] = str_replace('*//* ', '*/', substr($source[$i], 3, -2));
    343             ++$i;
    344         }
    345         array_splice($source, $i);
    346 
    347         return implode("\n", $source);
    348     }
    349 
    350     /**
    351      * {@inheritdoc}
    352      */
    353     public function display(array $context, array $blocks = array())
    354     {
    355         $this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks));
    356     }
    357 
    358     /**
    359      * {@inheritdoc}
    360      */
    361     public function render(array $context)
    362     {
    363         $level = ob_get_level();
    364         ob_start();
    365         try {
    366             $this->display($context);
    367         } catch (Exception $e) {
    368             while (ob_get_level() > $level) {
    369                 ob_end_clean();
    370             }
    371 
    372             throw $e;
    373         } catch (Throwable $e) {
    374             while (ob_get_level() > $level) {
    375                 ob_end_clean();
    376             }
    377 
    378             throw $e;
    379         }
    380 
    381         return ob_get_clean();
    382     }
    383 
    384     protected function displayWithErrorHandling(array $context, array $blocks = array())
    385     {
    386         try {
    387             $this->doDisplay($context, $blocks);
    388         } catch (Twig_Error $e) {
    389             if (!$e->getTemplateFile()) {
    390                 $e->setTemplateFile($this->getTemplateName());
    391             }
    392 
    393             // this is mostly useful for Twig_Error_Loader exceptions
    394             // see Twig_Error_Loader
    395             if (false === $e->getTemplateLine()) {
    396                 $e->setTemplateLine(-1);
    397                 $e->guess();
    398             }
    399 
    400             throw $e;
    401         } catch (Exception $e) {
    402             throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getTemplateName(), $e);
    403         }
    404     }
    405 
    406     /**
    407      * Auto-generated method to display the template with the given context.
    408      *
    409      * @param array $context An array of parameters to pass to the template
    410      * @param array $blocks  An array of blocks to pass to the template
    411      */
    412     abstract protected function doDisplay(array $context, array $blocks = array());
    413 
    414     /**
    415      * Returns a variable from the context.
    416      *
    417      * This method is for internal use only and should never be called
    418      * directly.
    419      *
    420      * This method should not be overridden in a sub-class as this is an
    421      * implementation detail that has been introduced to optimize variable
    422      * access for versions of PHP before 5.4. This is not a way to override
    423      * the way to get a variable value.
    424      *
    425      * @param array  $context           The context
    426      * @param string $item              The variable to return from the context
    427      * @param bool   $ignoreStrictCheck Whether to ignore the strict variable check or not
    428      *
    429      * @return mixed The content of the context variable
    430      *
    431      * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
    432      *
    433      * @internal
    434      */
    435     final protected function getContext($context, $item, $ignoreStrictCheck = false)
    436     {
    437         if (!array_key_exists($item, $context)) {
    438             if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
    439                 return;
    440             }
    441 
    442             throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName());
    443         }
    444 
    445         return $context[$item];
    446     }
    447 
    448     /**
    449      * Returns the attribute value for a given array/object.
    450      *
    451      * @param mixed  $object            The object or array from where to get the item
    452      * @param mixed  $item              The item to get from the array or object
    453      * @param array  $arguments         An array of arguments to pass if the item is an object method
    454      * @param string $type              The type of attribute (@see Twig_Template constants)
    455      * @param bool   $isDefinedTest     Whether this is only a defined check
    456      * @param bool   $ignoreStrictCheck Whether to ignore the strict attribute check or not
    457      *
    458      * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
    459      *
    460      * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
    461      */
    462     protected function getAttribute($object, $item, array $arguments = array(), $type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
    463     {
    464         // array
    465         if (self::METHOD_CALL !== $type) {
    466             $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
    467 
    468             if ((is_array($object) && array_key_exists($arrayItem, $object))
    469                 || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
    470             ) {
    471                 if ($isDefinedTest) {
    472                     return true;
    473                 }
    474 
    475                 return $object[$arrayItem];
    476             }
    477 
    478             if (self::ARRAY_CALL === $type || !is_object($object)) {
    479                 if ($isDefinedTest) {
    480                     return false;
    481                 }
    482 
    483                 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
    484                     return;
    485                 }
    486 
    487                 if ($object instanceof ArrayAccess) {
    488                     $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist', $arrayItem, get_class($object));
    489                 } elseif (is_object($object)) {
    490                     $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object));
    491                 } elseif (is_array($object)) {
    492                     if (empty($object)) {
    493                         $message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
    494                     } else {
    495                         $message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
    496                     }
    497                 } elseif (self::ARRAY_CALL === $type) {
    498                     if (null === $object) {
    499                         $message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
    500                     } else {
    501                         $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
    502                     }
    503                 } elseif (null === $object) {
    504                     $message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
    505                 } else {
    506                     $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
    507                 }
    508 
    509                 throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
    510             }
    511         }
    512 
    513         if (!is_object($object)) {
    514             if ($isDefinedTest) {
    515                 return false;
    516             }
    517 
    518             if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
    519                 return;
    520             }
    521 
    522             if (null === $object) {
    523                 $message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
    524             } else {
    525                 $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
    526             }
    527 
    528             throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
    529         }
    530 
    531         // object property
    532         if (self::METHOD_CALL !== $type && !$object instanceof self) { // Twig_Template does not have public properties, and we don't want to allow access to internal ones
    533             if (isset($object->$item) || array_key_exists((string) $item, $object)) {
    534                 if ($isDefinedTest) {
    535                     return true;
    536                 }
    537 
    538                 if ($this->env->hasExtension('sandbox')) {
    539                     $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
    540                 }
    541 
    542                 return $object->$item;
    543             }
    544         }
    545 
    546         $class = get_class($object);
    547 
    548         // object method
    549         if (!isset(self::$cache[$class]['methods'])) {
    550             // get_class_methods returns all methods accessible in the scope, but we only want public ones to be accessible in templates
    551             if ($object instanceof self) {
    552                 $ref = new ReflectionClass($class);
    553                 $methods = array();
    554 
    555                 foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) {
    556                     $methodName = strtolower($refMethod->name);
    557 
    558                     // Accessing the environment from templates is forbidden to prevent untrusted changes to the environment
    559                     if ('getenvironment' !== $methodName) {
    560                         $methods[$methodName] = true;
    561                     }
    562                 }
    563 
    564                 self::$cache[$class]['methods'] = $methods;
    565             } else {
    566                 self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
    567             }
    568         }
    569 
    570         $call = false;
    571         $lcItem = strtolower($item);
    572         if (isset(self::$cache[$class]['methods'][$lcItem])) {
    573             $method = (string) $item;
    574         } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
    575             $method = 'get'.$item;
    576         } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
    577             $method = 'is'.$item;
    578         } elseif (isset(self::$cache[$class]['methods']['__call'])) {
    579             $method = (string) $item;
    580             $call = true;
    581         } else {
    582             if ($isDefinedTest) {
    583                 return false;
    584             }
    585 
    586             if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
    587                 return;
    588             }
    589 
    590             throw new Twig_Error_Runtime(sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()" or "__call()" exist and have public access in class "%2$s"', $item, get_class($object)), -1, $this->getTemplateName());
    591         }
    592 
    593         if ($isDefinedTest) {
    594             return true;
    595         }
    596 
    597         if ($this->env->hasExtension('sandbox')) {
    598             $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
    599         }
    600 
    601         // Some objects throw exceptions when they have __call, and the method we try
    602         // to call is not supported. If ignoreStrictCheck is true, we should return null.
    603         try {
    604             $ret = call_user_func_array(array($object, $method), $arguments);
    605         } catch (BadMethodCallException $e) {
    606             if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
    607                 return;
    608             }
    609             throw $e;
    610         }
    611 
    612         // useful when calling a template method from a template
    613         // this is not supported but unfortunately heavily used in the Symfony profiler
    614         if ($object instanceof Twig_TemplateInterface) {
    615             return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
    616         }
    617 
    618         return $ret;
    619     }
    620 }