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