diff options
Diffstat (limited to 'inc/Twig/Template.php')
-rw-r--r-- | inc/Twig/Template.php | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/inc/Twig/Template.php b/inc/Twig/Template.php new file mode 100644 index 00000000..a001ca03 --- /dev/null +++ b/inc/Twig/Template.php | |||
@@ -0,0 +1,455 @@ | |||
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 | } | ||