aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/twig/twig/test/Twig/Tests/TemplateTest.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/twig/twig/test/Twig/Tests/TemplateTest.php')
-rw-r--r--vendor/twig/twig/test/Twig/Tests/TemplateTest.php626
1 files changed, 626 insertions, 0 deletions
diff --git a/vendor/twig/twig/test/Twig/Tests/TemplateTest.php b/vendor/twig/twig/test/Twig/Tests/TemplateTest.php
new file mode 100644
index 00000000..823a9ce9
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/TemplateTest.php
@@ -0,0 +1,626 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
12{
13 /**
14 * @dataProvider getAttributeExceptions
15 */
16 public function testGetAttributeExceptions($template, $message, $useExt)
17 {
18 $name = 'index_'.($useExt ? 1 : 0);
19 $templates = array(
20 $name => $template.$useExt, // appending $useExt makes the template content unique
21 );
22
23 $env = new Twig_Environment(new Twig_Loader_Array($templates), array('strict_variables' => true));
24 if (!$useExt) {
25 $env->addNodeVisitor(new CExtDisablingNodeVisitor());
26 }
27 $template = $env->loadTemplate($name);
28
29 $context = array(
30 'string' => 'foo',
31 'array' => array('foo' => 'foo'),
32 'array_access' => new Twig_TemplateArrayAccessObject(),
33 'magic_exception' => new Twig_TemplateMagicPropertyObjectWithException(),
34 );
35
36 try {
37 $template->render($context);
38 $this->fail('Accessing an invalid attribute should throw an exception.');
39 } catch (Twig_Error_Runtime $e) {
40 $this->assertSame(sprintf($message, $name), $e->getMessage());
41 }
42 }
43
44 public function getAttributeExceptions()
45 {
46 $tests = array(
47 array('{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1', false),
48 array('{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false),
49 array('{{ array_access["a"] }}', 'Key "a" in object (with ArrayAccess) of type "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
50 array('{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1', false),
51 array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1', false),
52 array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false),
53 array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1', false),
54 array('{{ array_access.a }}', 'Method "a" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
55 array('{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ _self.foo(array_access) }}', 'Method "missing_method" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
56 array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.', false),
57 );
58
59 if (function_exists('twig_template_get_attributes')) {
60 foreach (array_slice($tests, 0) as $test) {
61 $test[2] = true;
62 $tests[] = $test;
63 }
64 }
65
66 return $tests;
67 }
68
69 /**
70 * @dataProvider getGetAttributeWithSandbox
71 */
72 public function testGetAttributeWithSandbox($object, $item, $allowed, $useExt)
73 {
74 $twig = new Twig_Environment();
75 $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(/*method*/), array(/*prop*/), array());
76 $twig->addExtension(new Twig_Extension_Sandbox($policy, !$allowed));
77 $template = new Twig_TemplateTest($twig, $useExt);
78
79 try {
80 $template->getAttribute($object, $item, array(), 'any');
81
82 if (!$allowed) {
83 $this->fail();
84 }
85 } catch (Twig_Sandbox_SecurityError $e) {
86 if ($allowed) {
87 $this->fail();
88 }
89
90 $this->assertContains('is not allowed', $e->getMessage());
91 }
92 }
93
94 public function getGetAttributeWithSandbox()
95 {
96 $tests = array(
97 array(new Twig_TemplatePropertyObject(), 'defined', false, false),
98 array(new Twig_TemplatePropertyObject(), 'defined', true, false),
99 array(new Twig_TemplateMethodObject(), 'defined', false, false),
100 array(new Twig_TemplateMethodObject(), 'defined', true, false),
101 );
102
103 if (function_exists('twig_template_get_attributes')) {
104 foreach (array_slice($tests, 0) as $test) {
105 $test[3] = true;
106 $tests[] = $test;
107 }
108 }
109
110 return $tests;
111 }
112
113 /**
114 * @dataProvider getGetAttributeWithTemplateAsObject
115 */
116 public function testGetAttributeWithTemplateAsObject($useExt)
117 {
118 $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
119 $template1 = new Twig_TemplateTest(new Twig_Environment(), false);
120
121 $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string'));
122 $this->assertEquals('some_string', $template->getAttribute($template1, 'string'));
123
124 $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'true'));
125 $this->assertEquals('1', $template->getAttribute($template1, 'true'));
126
127 $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'zero'));
128 $this->assertEquals('0', $template->getAttribute($template1, 'zero'));
129
130 $this->assertNotInstanceof('Twig_Markup', $template->getAttribute($template1, 'empty'));
131 $this->assertSame('', $template->getAttribute($template1, 'empty'));
132 }
133
134 public function getGetAttributeWithTemplateAsObject()
135 {
136 $bools = array(
137 array(false),
138 );
139
140 if (function_exists('twig_template_get_attributes')) {
141 $bools[] = array(true);
142 }
143
144 return $bools;
145 }
146
147 /**
148 * @dataProvider getTestsDependingOnExtensionAvailability
149 */
150 public function testGetAttributeOnArrayWithConfusableKey($useExt = false)
151 {
152 $template = new Twig_TemplateTest(
153 new Twig_Environment(),
154 $useExt
155 );
156
157 $array = array('Zero', 'One', -1 => 'MinusOne', '' => 'EmptyString', '1.5' => 'FloatButString', '01' => 'IntegerButStringWithLeadingZeros');
158
159 $this->assertSame('Zero', $array[false]);
160 $this->assertSame('One', $array[true]);
161 $this->assertSame('One', $array[1.5]);
162 $this->assertSame('One', $array['1']);
163 $this->assertSame('MinusOne', $array[-1.5]);
164 $this->assertSame('FloatButString', $array['1.5']);
165 $this->assertSame('IntegerButStringWithLeadingZeros', $array['01']);
166 $this->assertSame('EmptyString', $array[null]);
167
168 $this->assertSame('Zero', $template->getAttribute($array, false), 'false is treated as 0 when accessing an array (equals PHP behavior)');
169 $this->assertSame('One', $template->getAttribute($array, true), 'true is treated as 1 when accessing an array (equals PHP behavior)');
170 $this->assertSame('One', $template->getAttribute($array, 1.5), 'float is casted to int when accessing an array (equals PHP behavior)');
171 $this->assertSame('One', $template->getAttribute($array, '1'), '"1" is treated as integer 1 when accessing an array (equals PHP behavior)');
172 $this->assertSame('MinusOne', $template->getAttribute($array, -1.5), 'negative float is casted to int when accessing an array (equals PHP behavior)');
173 $this->assertSame('FloatButString', $template->getAttribute($array, '1.5'), '"1.5" is treated as-is when accessing an array (equals PHP behavior)');
174 $this->assertSame('IntegerButStringWithLeadingZeros', $template->getAttribute($array, '01'), '"01" is treated as-is when accessing an array (equals PHP behavior)');
175 $this->assertSame('EmptyString', $template->getAttribute($array, null), 'null is treated as "" when accessing an array (equals PHP behavior)');
176 }
177
178 public function getTestsDependingOnExtensionAvailability()
179 {
180 if (function_exists('twig_template_get_attributes')) {
181 return array(array(false), array(true));
182 }
183
184 return array(array(false));
185 }
186
187 /**
188 * @dataProvider getGetAttributeTests
189 */
190 public function testGetAttribute($defined, $value, $object, $item, $arguments, $type, $useExt = false)
191 {
192 $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
193
194 $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
195 }
196
197 /**
198 * @dataProvider getGetAttributeTests
199 */
200 public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false, $exceptionMessage = null)
201 {
202 $template = new Twig_TemplateTest(new Twig_Environment(null, array('strict_variables' => true)), $useExt);
203
204 if ($defined) {
205 $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
206 } else {
207 try {
208 $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
209
210 throw new Exception('Expected Twig_Error_Runtime exception.');
211 } catch (Twig_Error_Runtime $e) {
212 if (null !== $exceptionMessage) {
213 $this->assertSame($exceptionMessage, $e->getMessage());
214 }
215 }
216 }
217 }
218
219 /**
220 * @dataProvider getGetAttributeTests
221 */
222 public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type, $useExt = false)
223 {
224 $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
225
226 $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true));
227 }
228
229 /**
230 * @dataProvider getGetAttributeTests
231 */
232 public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false)
233 {
234 $template = new Twig_TemplateTest(new Twig_Environment(null, array('strict_variables' => true)), $useExt);
235
236 $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true));
237 }
238
239 public function getGetAttributeTests()
240 {
241 $array = array(
242 'defined' => 'defined',
243 'zero' => 0,
244 'null' => null,
245 '1' => 1,
246 'bar' => true,
247 '09' => '09',
248 '+4' => '+4',
249 );
250
251 $objectArray = new Twig_TemplateArrayAccessObject();
252 $stdObject = (object) $array;
253 $magicPropertyObject = new Twig_TemplateMagicPropertyObject();
254 $propertyObject = new Twig_TemplatePropertyObject();
255 $propertyObject1 = new Twig_TemplatePropertyObjectAndIterator();
256 $propertyObject2 = new Twig_TemplatePropertyObjectAndArrayAccess();
257 $methodObject = new Twig_TemplateMethodObject();
258 $magicMethodObject = new Twig_TemplateMagicMethodObject();
259
260 $anyType = Twig_TemplateInterface::ANY_CALL;
261 $methodType = Twig_TemplateInterface::METHOD_CALL;
262 $arrayType = Twig_TemplateInterface::ARRAY_CALL;
263
264 $basicTests = array(
265 // array(defined, value, property to fetch)
266 array(true, 'defined', 'defined'),
267 array(false, null, 'undefined'),
268 array(false, null, 'protected'),
269 array(true, 0, 'zero'),
270 array(true, 1, 1),
271 array(true, 1, 1.0),
272 array(true, null, 'null'),
273 array(true, true, 'bar'),
274 array(true, '09', '09'),
275 array(true, '+4', '+4'),
276 );
277 $testObjects = array(
278 // array(object, type of fetch)
279 array($array, $arrayType),
280 array($objectArray, $arrayType),
281 array($stdObject, $anyType),
282 array($magicPropertyObject, $anyType),
283 array($methodObject, $methodType),
284 array($methodObject, $anyType),
285 array($propertyObject, $anyType),
286 array($propertyObject1, $anyType),
287 array($propertyObject2, $anyType),
288 );
289
290 $tests = array();
291 foreach ($testObjects as $testObject) {
292 foreach ($basicTests as $test) {
293 // properties cannot be numbers
294 if (($testObject[0] instanceof stdClass || $testObject[0] instanceof Twig_TemplatePropertyObject) && is_numeric($test[2])) {
295 continue;
296 }
297
298 if ('+4' === $test[2] && $methodObject === $testObject[0]) {
299 continue;
300 }
301
302 $tests[] = array($test[0], $test[1], $testObject[0], $test[2], array(), $testObject[1]);
303 }
304 }
305
306 // additional method tests
307 $tests = array_merge($tests, array(
308 array(true, 'defined', $methodObject, 'defined', array(), $methodType),
309 array(true, 'defined', $methodObject, 'DEFINED', array(), $methodType),
310 array(true, 'defined', $methodObject, 'getDefined', array(), $methodType),
311 array(true, 'defined', $methodObject, 'GETDEFINED', array(), $methodType),
312 array(true, 'static', $methodObject, 'static', array(), $methodType),
313 array(true, 'static', $methodObject, 'getStatic', array(), $methodType),
314
315 array(true, '__call_undefined', $magicMethodObject, 'undefined', array(), $methodType),
316 array(true, '__call_UNDEFINED', $magicMethodObject, 'UNDEFINED', array(), $methodType),
317 ));
318
319 // add the same tests for the any type
320 foreach ($tests as $test) {
321 if ($anyType !== $test[5]) {
322 $test[5] = $anyType;
323 $tests[] = $test;
324 }
325 }
326
327 $methodAndPropObject = new Twig_TemplateMethodAndPropObject;
328
329 // additional method tests
330 $tests = array_merge($tests, array(
331 array(true, 'a', $methodAndPropObject, 'a', array(), $anyType),
332 array(true, 'a', $methodAndPropObject, 'a', array(), $methodType),
333 array(false, null, $methodAndPropObject, 'a', array(), $arrayType),
334
335 array(true, 'b_prop', $methodAndPropObject, 'b', array(), $anyType),
336 array(true, 'b', $methodAndPropObject, 'B', array(), $anyType),
337 array(true, 'b', $methodAndPropObject, 'b', array(), $methodType),
338 array(true, 'b', $methodAndPropObject, 'B', array(), $methodType),
339 array(false, null, $methodAndPropObject, 'b', array(), $arrayType),
340
341 array(false, null, $methodAndPropObject, 'c', array(), $anyType),
342 array(false, null, $methodAndPropObject, 'c', array(), $methodType),
343 array(false, null, $methodAndPropObject, 'c', array(), $arrayType),
344
345 ));
346
347 // tests when input is not an array or object
348 $tests = array_merge($tests, array(
349 array(false, null, 42, 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a integer variable ("42")'),
350 array(false, null, "string", 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a string variable ("string")'),
351 array(false, null, array(), 'a', array(), $anyType, false, 'Key "a" for array with keys "" does not exist'),
352 ));
353
354 // add twig_template_get_attributes tests
355
356 if (function_exists('twig_template_get_attributes')) {
357 foreach (array_slice($tests, 0) as $test) {
358 $test = array_pad($test, 7, null);
359 $test[6] = true;
360 $tests[] = $test;
361 }
362 }
363
364 return $tests;
365 }
366}
367
368class Twig_TemplateTest extends Twig_Template
369{
370 protected $useExtGetAttribute = false;
371
372 public function __construct(Twig_Environment $env, $useExtGetAttribute = false)
373 {
374 parent::__construct($env);
375 $this->useExtGetAttribute = $useExtGetAttribute;
376 Twig_Template::clearCache();
377 }
378
379 public function getZero()
380 {
381 return 0;
382 }
383
384 public function getEmpty()
385 {
386 return '';
387 }
388
389 public function getString()
390 {
391 return 'some_string';
392 }
393
394 public function getTrue()
395 {
396 return true;
397 }
398
399 public function getTemplateName()
400 {
401 }
402
403 public function getDebugInfo()
404 {
405 return array();
406 }
407
408 protected function doGetParent(array $context)
409 {
410 }
411
412 protected function doDisplay(array $context, array $blocks = array())
413 {
414 }
415
416 public function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
417 {
418 if ($this->useExtGetAttribute) {
419 return twig_template_get_attributes($this, $object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck);
420 } else {
421 return parent::getAttribute($object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck);
422 }
423 }
424}
425
426class Twig_TemplateArrayAccessObject implements ArrayAccess
427{
428 protected $protected = 'protected';
429
430 public $attributes = array(
431 'defined' => 'defined',
432 'zero' => 0,
433 'null' => null,
434 '1' => 1,
435 'bar' => true,
436 '09' => '09',
437 '+4' => '+4',
438 );
439
440 public function offsetExists($name)
441 {
442 return array_key_exists($name, $this->attributes);
443 }
444
445 public function offsetGet($name)
446 {
447 return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null;
448 }
449
450 public function offsetSet($name, $value)
451 {
452 }
453
454 public function offsetUnset($name)
455 {
456 }
457}
458
459class Twig_TemplateMagicPropertyObject
460{
461 public $defined = 'defined';
462
463 public $attributes = array(
464 'zero' => 0,
465 'null' => null,
466 '1' => 1,
467 'bar' => true,
468 '09' => '09',
469 '+4' => '+4',
470 );
471
472 protected $protected = 'protected';
473
474 public function __isset($name)
475 {
476 return array_key_exists($name, $this->attributes);
477 }
478
479 public function __get($name)
480 {
481 return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null;
482 }
483}
484
485class Twig_TemplateMagicPropertyObjectWithException
486{
487 public function __isset($key)
488 {
489 throw new Exception("Hey! Don't try to isset me!");
490 }
491}
492
493class Twig_TemplatePropertyObject
494{
495 public $defined = 'defined';
496 public $zero = 0;
497 public $null = null;
498 public $bar = true;
499
500 protected $protected = 'protected';
501}
502
503class Twig_TemplatePropertyObjectAndIterator extends Twig_TemplatePropertyObject implements IteratorAggregate
504{
505 public function getIterator()
506 {
507 return new ArrayIterator(array('foo', 'bar'));
508 }
509}
510
511class Twig_TemplatePropertyObjectAndArrayAccess extends Twig_TemplatePropertyObject implements ArrayAccess
512{
513 private $data = array();
514
515 public function offsetExists($offset)
516 {
517 return array_key_exists($offset, $this->data);
518 }
519
520 public function offsetGet($offset)
521 {
522 return $this->offsetExists($offset) ? $this->data[$offset] : 'n/a';
523 }
524
525 public function offsetSet($offset, $value)
526 {
527 }
528
529 public function offsetUnset($offset)
530 {
531 }
532}
533
534class Twig_TemplateMethodObject
535{
536 public function getDefined()
537 {
538 return 'defined';
539 }
540
541 public function get1()
542 {
543 return 1;
544 }
545
546 public function get09()
547 {
548 return '09';
549 }
550
551 public function getZero()
552 {
553 return 0;
554 }
555
556 public function getNull()
557 {
558 return null;
559 }
560
561 public function isBar()
562 {
563 return true;
564 }
565
566 protected function getProtected()
567 {
568 return 'protected';
569 }
570
571 public static function getStatic()
572 {
573 return 'static';
574 }
575}
576
577class Twig_TemplateMethodAndPropObject
578{
579 private $a = 'a_prop';
580 public function getA()
581 {
582 return 'a';
583 }
584
585 public $b = 'b_prop';
586 public function getB()
587 {
588 return 'b';
589 }
590
591 private $c = 'c_prop';
592 private function getC()
593 {
594 return 'c';
595 }
596}
597
598class Twig_TemplateMagicMethodObject
599{
600 public function __call($method, $arguments)
601 {
602 return '__call_'.$method;
603 }
604}
605
606class CExtDisablingNodeVisitor implements Twig_NodeVisitorInterface
607{
608 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
609 {
610 if ($node instanceof Twig_Node_Expression_GetAttr) {
611 $node->setAttribute('disable_c_ext', true);
612 }
613
614 return $node;
615 }
616
617 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
618 {
619 return $node;
620 }
621
622 public function getPriority()
623 {
624 return 0;
625 }
626}