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