1From the Twig test suite, https://github.com/fabpot/Twig, available under BSD license.
2
3--TEST--
4Exception for an unclosed tag
5--TEMPLATE--
6{% block foo %}
7     {% if foo %}
8
9
10
11
12         {% for i in fo %}
13
14
15
16         {% endfor %}
17
18
19
20{% endblock %}
21--EXCEPTION--
22Twig_Error_Syntax: Unexpected tag name "endblock" (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16
23--TEST--
24Exception for an undefined trait
25--TEMPLATE--
26{% use 'foo' with foobar as bar %}
27--TEMPLATE(foo)--
28{% block bar %}
29{% endblock %}
30--EXCEPTION--
31Twig_Error_Runtime: Block "foobar" is not defined in trait "foo" in "index.twig".
32--TEST--
33Twig supports method calls
34--TEMPLATE--
35{{ items.foo }}
36{{ items['foo'] }}
37{{ items[foo] }}
38{{ items[items[foo]] }}
39--DATA--
40return array('foo' => 'bar', 'items' => array('foo' => 'bar', 'bar' => 'foo'))
41--EXPECT--
42bar
43bar
44foo
45bar
46--TEST--
47Twig supports array notation
48--TEMPLATE--
49{# empty array #}
50{{ []|join(',') }}
51
52{{ [1, 2]|join(',') }}
53{{ ['foo', "bar"]|join(',') }}
54{{ {0: 1, 'foo': 'bar'}|join(',') }}
55{{ {0: 1, 'foo': 'bar'}|keys|join(',') }}
56
57{{ {0: 1, foo: 'bar'}|join(',') }}
58{{ {0: 1, foo: 'bar'}|keys|join(',') }}
59
60{# nested arrays #}
61{% set a = [1, 2, [1, 2], {'foo': {'foo': 'bar'}}] %}
62{{ a[2]|join(',') }}
63{{ a[3]["foo"]|join(',') }}
64
65{# works even if [] is used inside the array #}
66{{ [foo[bar]]|join(',') }}
67
68{# elements can be any expression #}
69{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }}
70
71{# arrays can have a trailing , like in PHP #}
72{{
73  [
74    1,
75    2,
76  ]|join(',')
77}}
78
79{# keys can be any expression #}
80{% set a = 1 %}
81{% set b = "foo" %}
82{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %}
83{{ ary|keys|join(',') }}
84{{ ary|join(',') }}
85--DATA--
86return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
87--EXPECT--
881,2
89foo,bar
901,bar
910,foo
92
931,bar
940,foo
95
961,2
97bar
98
99bar
100
101FOO,BAR,
102
1031,2
104
1051,foo,c,1foo
106a,b,c,d
107--TEST--
108Twig supports binary operations (+, -, *, /, ~, %, and, or)
109--TEMPLATE--
110{{ 1 + 1 }}
111{{ 2 - 1 }}
112{{ 2 * 2 }}
113{{ 2 / 2 }}
114{{ 3 % 2 }}
115{{ 1 and 1 }}
116{{ 1 and 0 }}
117{{ 0 and 1 }}
118{{ 0 and 0 }}
119{{ 1 or 1 }}
120{{ 1 or 0 }}
121{{ 0 or 1 }}
122{{ 0 or 0 }}
123{{ 0 or 1 and 0 }}
124{{ 1 or 0 and 1 }}
125{{ "foo" ~ "bar" }}
126{{ foo ~ "bar" }}
127{{ "foo" ~ bar }}
128{{ foo ~ bar }}
129{{ 20 // 7 }}
130--DATA--
131return array('foo' => 'bar', 'bar' => 'foo')
132--EXPECT--
1332
1341
1354
1361
1371
1381
139
140
141
1421
1431
1441
145
146
1471
148foobar
149barbar
150foofoo
151barfoo
1522
153--TEST--
154Twig supports bitwise operations
155--TEMPLATE--
156{{ 1 b-and 5 }}
157{{ 1 b-or 5 }}
158{{ 1 b-xor 5 }}
159{{ (1 and 0 b-or 0) is same as(1 and (0 b-or 0)) ? 'ok' : 'ko' }}
160--DATA--
161return array()
162--EXPECT--
1631
1645
1654
166ok
167--TEST--
168Twig supports comparison operators (==, !=, <, >, >=, <=)
169--TEMPLATE--
170{{ 1 > 2 }}/{{ 1 > 1 }}/{{ 1 >= 2 }}/{{ 1 >= 1 }}
171{{ 1 < 2 }}/{{ 1 < 1 }}/{{ 1 <= 2 }}/{{ 1 <= 1 }}
172{{ 1 == 1 }}/{{ 1 == 2 }}
173{{ 1 != 1 }}/{{ 1 != 2 }}
174--DATA--
175return array()
176--EXPECT--
177///1
1781//1/1
1791/
180/1
181--TEST--
182Twig supports the "divisible by" operator
183--TEMPLATE--
184{{ 8 is divisible by(2) ? 'OK' }}
185{{ 8 is not divisible by(3) ? 'OK' }}
186{{ 8 is    divisible   by   (2) ? 'OK' }}
187{{ 8 is not
188   divisible
189   by
190   (3) ? 'OK' }}
191--DATA--
192return array()
193--EXPECT--
194OK
195OK
196OK
197OK
198--TEST--
199Twig supports the .. operator
200--TEMPLATE--
201{% for i in 0..10 %}{{ i }} {% endfor %}
202
203{% for letter in 'a'..'z' %}{{ letter }} {% endfor %}
204
205{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %}
206
207{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %}
208
209{% for i in 0 + 1 .. 10 - 1 %}{{ i }} {% endfor %}
210--DATA--
211return array('foo' => array(1, 10))
212--EXPECT--
2130 1 2 3 4 5 6 7 8 9 10
214a b c d e f g h i j k l m n o p q r s t u v w x y z
215A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
2161 2 3 4 5 6 7 8 9 10
2171 2 3 4 5 6 7 8 9
218--TEST--
219Twig supports the "ends with" operator
220--TEMPLATE--
221{{ 'foo' ends with 'o' ? 'OK' : 'KO' }}
222{{ not ('foo' ends with 'f') ? 'OK' : 'KO' }}
223{{ not ('foo' ends with 'foowaytoolong') ? 'OK' : 'KO' }}
224{{ 'foo' ends with '' ? 'OK' : 'KO' }}
225{{ '1' ends with true ? 'OK' : 'KO' }}
226{{ 1 ends with true ? 'OK' : 'KO' }}
227{{ 0 ends with false ? 'OK' : 'KO' }}
228{{ '' ends with false ? 'OK' : 'KO' }}
229{{ false ends with false ? 'OK' : 'KO' }}
230{{ false ends with '' ? 'OK' : 'KO' }}
231--DATA--
232return array()
233--EXPECT--
234OK
235OK
236OK
237OK
238KO
239KO
240KO
241KO
242KO
243KO
244--TEST--
245Twig supports grouping of expressions
246--TEMPLATE--
247{{ (2 + 2) / 2 }}
248--DATA--
249return array()
250--EXPECT--
2512
252--TEST--
253Twig supports literals
254--TEMPLATE--
2551 {{ true }}
2562 {{ TRUE }}
2573 {{ false }}
2584 {{ FALSE }}
2595 {{ none }}
2606 {{ NONE }}
2617 {{ null }}
2628 {{ NULL }}
263--DATA--
264return array()
265--EXPECT--
2661 1
2672 1
2683
2694
2705
2716
2727
2738
274--TEST--
275Twig supports __call() for attributes
276--TEMPLATE--
277{{ foo.foo }}
278{{ foo.bar }}
279--EXPECT--
280foo_from_call
281bar_from_getbar
282--TEST--
283Twig supports the "matches" operator
284--TEMPLATE--
285{{ 'foo' matches '/o/' ? 'OK' : 'KO' }}
286{{ 'foo' matches '/^fo/' ? 'OK' : 'KO' }}
287{{ 'foo' matches '/O/i' ? 'OK' : 'KO' }}
288--DATA--
289return array()
290--EXPECT--
291OK
292OK
293OK
294--TEST--
295Twig supports method calls
296--TEMPLATE--
297{{ items.foo.foo }}
298{{ items.foo.getFoo() }}
299{{ items.foo.bar }}
300{{ items.foo['bar'] }}
301{{ items.foo.bar('a', 43) }}
302{{ items.foo.bar(foo) }}
303{{ items.foo.self.foo() }}
304{{ items.foo.is }}
305{{ items.foo.in }}
306{{ items.foo.not }}
307--DATA--
308return array('foo' => 'bar', 'items' => array('foo' => new TwigTestFoo(), 'bar' => 'foo'))
309--CONFIG--
310return array('strict_variables' => false)
311--EXPECT--
312foo
313foo
314bar
315
316bar_a-43
317bar_bar
318foo
319is
320in
321not
322--TEST--
323Twig allows to use named operators as variable names
324--TEMPLATE--
325{% for match in matches %}
326    {{- match }}
327{% endfor %}
328{{ in }}
329{{ is }}
330--DATA--
331return array('matches' => array(1, 2, 3), 'in' => 'in', 'is' => 'is')
332--EXPECT--
3331
3342
3353
336in
337is
338--TEST--
339Twig parses postfix expressions
340--TEMPLATE--
341{% import _self as macros %}
342
343{% macro foo() %}foo{% endmacro %}
344
345{{ 'a' }}
346{{ 'a'|upper }}
347{{ ('a')|upper }}
348{{ -1|upper }}
349{{ macros.foo() }}
350{{ (macros).foo() }}
351--DATA--
352return array();
353--EXPECT--
354a
355A
356A
357-1
358foo
359foo
360--TEST--
361Twig supports the "same as" operator
362--TEMPLATE--
363{{ 1 is same as(1) ? 'OK' }}
364{{ 1 is not same as(true) ? 'OK' }}
365{{ 1 is same as(1) ? 'OK' }}
366{{ 1 is not same as(true) ? 'OK' }}
367{{ 1 is   same    as   (1) ? 'OK' }}
368{{ 1 is not
369    same
370    as
371    (true) ? 'OK' }}
372--DATA--
373return array()
374--EXPECT--
375OK
376OK
377OK
378OK
379OK
380OK
381--TEST--
382Twig supports the "starts with" operator
383--TEMPLATE--
384{{ 'foo' starts with 'f' ? 'OK' : 'KO' }}
385{{ not ('foo' starts with 'oo') ? 'OK' : 'KO' }}
386{{ not ('foo' starts with 'foowaytoolong') ? 'OK' : 'KO' }}
387{{ 'foo' starts      with 'f' ? 'OK' : 'KO' }}
388{{ 'foo' starts
389with 'f' ? 'OK' : 'KO' }}
390{{ 'foo' starts with '' ? 'OK' : 'KO' }}
391{{ '1' starts with true ? 'OK' : 'KO' }}
392{{ '' starts with false ? 'OK' : 'KO' }}
393{{ 'a' starts with false ? 'OK' : 'KO' }}
394{{ false starts with '' ? 'OK' : 'KO' }}
395--DATA--
396return array()
397--EXPECT--
398OK
399OK
400OK
401OK
402OK
403OK
404KO
405KO
406KO
407KO
408--TEST--
409Twig supports string interpolation
410--TEMPLATE--
411{# "foo #{"foo #{bar} baz"} baz" #}
412{# "foo #{bar}#{bar} baz" #}
413--DATA--
414return array('bar' => 'BAR');
415--EXPECT--
416foo foo BAR baz baz
417foo BARBAR baz
418--TEST--
419Twig supports the ternary operator
420--TEMPLATE--
421{{ 1 ? 'YES' }}
422{{ 0 ? 'YES' }}
423--DATA--
424return array()
425--EXPECT--
426YES
427
428--TEST--
429Twig supports the ternary operator
430--TEMPLATE--
431{{ 'YES' ?: 'NO' }}
432{{ 0 ?: 'NO' }}
433--DATA--
434return array()
435--EXPECT--
436YES
437NO
438--TEST--
439Twig supports the ternary operator
440--TEMPLATE--
441{{ 1 ? 'YES' : 'NO' }}
442{{ 0 ? 'YES' : 'NO' }}
443{{ 0 ? 'YES' : (1 ? 'YES1' : 'NO1') }}
444{{ 0 ? 'YES' : (0 ? 'YES1' : 'NO1') }}
445{{ 1 == 1 ? 'foo<br />':'' }}
446{{ foo ~ (bar ? ('-' ~ bar) : '') }}
447--DATA--
448return array('foo' => 'foo', 'bar' => 'bar')
449--EXPECT--
450YES
451NO
452YES1
453NO1
454foo<br />
455foo-bar
456--TEST--
457Twig does not allow to use two-word named operators as variable names
458--TEMPLATE--
459{{ starts with }}
460--DATA--
461return array()
462--EXCEPTION--
463Twig_Error_Syntax: Unexpected token "operator" of value "starts with" in "index.twig" at line 2
464--TEST--
465Twig unary operators precedence
466--TEMPLATE--
467{{ -1 - 1 }}
468{{ -1 - -1 }}
469{{ -1 * -1 }}
470{{ 4 / -1 * 5 }}
471--DATA--
472return array()
473--EXPECT--
474-2
4750
4761
477-20
478--TEST--
479Twig supports unary operators (not, -, +)
480--TEMPLATE--
481{{ not 1 }}/{{ not 0 }}
482{{ +1 + 1 }}/{{ -1 - 1 }}
483{{ not (false or true) }}
484--DATA--
485return array()
486--EXPECT--
487/1
4882/-2
489
490--TEST--
491"abs" filter
492--TEMPLATE--
493{{ (-5.5)|abs }}
494{{ (-5)|abs }}
495{{ (-0)|abs }}
496{{ 0|abs }}
497{{ 5|abs }}
498{{ 5.5|abs }}
499{{ number1|abs }}
500{{ number2|abs }}
501{{ number3|abs }}
502{{ number4|abs }}
503{{ number5|abs }}
504{{ number6|abs }}
505--DATA--
506return array('number1' => -5.5, 'number2' => -5, 'number3' => -0, 'number4' => 0, 'number5' => 5, 'number6' => 5.5)
507--EXPECT--
5085.5
5095
5100
5110
5125
5135.5
5145.5
5155
5160
5170
5185
5195.5
520--TEST--
521"batch" filter
522--TEMPLATE--
523{% for row in items|batch(3.1) %}
524  <div class=row>
525  {% for column in row %}
526    <div class=item>{{ column }}</div>
527  {% endfor %}
528  </div>
529{% endfor %}
530--DATA--
531return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
532--EXPECT--
533<div class=row>
534      <div class=item>a</div>
535      <div class=item>b</div>
536      <div class=item>c</div>
537      <div class=item>d</div>
538    </div>
539  <div class=row>
540      <div class=item>e</div>
541      <div class=item>f</div>
542      <div class=item>g</div>
543      <div class=item>h</div>
544    </div>
545  <div class=row>
546      <div class=item>i</div>
547      <div class=item>j</div>
548    </div>
549--TEST--
550"batch" filter
551--TEMPLATE--
552{% for row in items|batch(3) %}
553  <div class=row>
554  {% for column in row %}
555    <div class=item>{{ column }}</div>
556  {% endfor %}
557  </div>
558{% endfor %}
559--DATA--
560return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
561--EXPECT--
562<div class=row>
563      <div class=item>a</div>
564      <div class=item>b</div>
565      <div class=item>c</div>
566    </div>
567  <div class=row>
568      <div class=item>d</div>
569      <div class=item>e</div>
570      <div class=item>f</div>
571    </div>
572  <div class=row>
573      <div class=item>g</div>
574      <div class=item>h</div>
575      <div class=item>i</div>
576    </div>
577  <div class=row>
578      <div class=item>j</div>
579    </div>
580--TEST--
581"batch" filter
582--TEMPLATE--
583<table>
584{% for row in items|batch(3, '') %}
585  <tr>
586  {% for column in row %}
587    <td>{{ column }}</td>
588  {% endfor %}
589  </tr>
590{% endfor %}
591</table>
592--DATA--
593return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
594--EXPECT--
595<table>
596  <tr>
597      <td>a</td>
598      <td>b</td>
599      <td>c</td>
600    </tr>
601  <tr>
602      <td>d</td>
603      <td>e</td>
604      <td>f</td>
605    </tr>
606  <tr>
607      <td>g</td>
608      <td>h</td>
609      <td>i</td>
610    </tr>
611  <tr>
612      <td>j</td>
613      <td></td>
614      <td></td>
615    </tr>
616</table>
617--TEST--
618"batch" filter
619--TEMPLATE--
620{% for row in items|batch(3, 'fill') %}
621  <div class=row>
622  {% for column in row %}
623    <div class=item>{{ column }}</div>
624  {% endfor %}
625  </div>
626{% endfor %}
627--DATA--
628return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'))
629--EXPECT--
630<div class=row>
631      <div class=item>a</div>
632      <div class=item>b</div>
633      <div class=item>c</div>
634    </div>
635  <div class=row>
636      <div class=item>d</div>
637      <div class=item>e</div>
638      <div class=item>f</div>
639    </div>
640  <div class=row>
641      <div class=item>g</div>
642      <div class=item>h</div>
643      <div class=item>i</div>
644    </div>
645  <div class=row>
646      <div class=item>j</div>
647      <div class=item>k</div>
648      <div class=item>l</div>
649    </div>
650--TEST--
651"batch" filter
652--TEMPLATE--
653<table>
654{% for row in items|batch(3, 'fill') %}
655  <tr>
656  {% for column in row %}
657    <td>{{ column }}</td>
658  {% endfor %}
659  </tr>
660{% endfor %}
661</table>
662--DATA--
663return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
664--EXPECT--
665<table>
666  <tr>
667      <td>a</td>
668      <td>b</td>
669      <td>c</td>
670    </tr>
671  <tr>
672      <td>d</td>
673      <td>e</td>
674      <td>f</td>
675    </tr>
676  <tr>
677      <td>g</td>
678      <td>h</td>
679      <td>i</td>
680    </tr>
681  <tr>
682      <td>j</td>
683      <td>fill</td>
684      <td>fill</td>
685    </tr>
686</table>
687--TEST--
688"convert_encoding" filter
689--CONDITION--
690function_exists('iconv') || function_exists('mb_convert_encoding')
691--TEMPLATE--
692{{ "愛していますか?"|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }}
693--DATA--
694return array()
695--EXPECT--
696愛していますか?
697--TEST--
698"date" filter (interval support as of PHP 5.3)
699--CONDITION--
700version_compare(phpversion(), '5.3.0', '>=')
701--TEMPLATE--
702{{ date2|date }}
703{{ date2|date('%d days') }}
704--DATA--
705date_default_timezone_set('UTC');
706$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
707return array(
708    'date2' => new DateInterval('P2D'),
709)
710--EXPECT--
7112 days 0 hours
7122 days
713--TEST--
714"date" filter
715--TEMPLATE--
716{{ date1|date }}
717{{ date1|date('d/m/Y') }}
718--DATA--
719date_default_timezone_set('UTC');
720$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
721return array(
722    'date1' => mktime(13, 45, 0, 10, 4, 2010),
723)
724--EXPECT--
7252010-10-04
72604/10/2010
727--TEST--
728"date" filter
729--CONDITION--
730version_compare(phpversion(), '5.5.0', '>=')
731--TEMPLATE--
732{{ date1|date }}
733{{ date1|date('d/m/Y') }}
734{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
735{{ date1|date('d/m/Y H:i:s', timezone1) }}
736{{ date1|date('d/m/Y H:i:s') }}
737
738{{ date2|date('d/m/Y H:i:s P', 'Europe/Paris') }}
739{{ date2|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
740{{ date2|date('d/m/Y H:i:s P', false) }}
741{{ date2|date('e', 'Europe/Paris') }}
742{{ date2|date('e', false) }}
743--DATA--
744date_default_timezone_set('Europe/Paris');
745return array(
746    'date1' => new DateTimeImmutable('2010-10-04 13:45'),
747    'date2' => new DateTimeImmutable('2010-10-04 13:45', new DateTimeZone('America/New_York')),
748    'timezone1' => new DateTimeZone('America/New_York'),
749)
750--EXPECT--
751October 4, 2010 13:45
75204/10/2010
75304/10/2010 19:45:00
75404/10/2010 07:45:00
75504/10/2010 13:45:00
756
75704/10/2010 19:45:00 +02:00
75805/10/2010 01:45:00 +08:00
75904/10/2010 13:45:00 -04:00
760Europe/Paris
761America/New_York
762--TEST--
763"date" filter (interval support as of PHP 5.3)
764--CONDITION--
765version_compare(phpversion(), '5.3.0', '>=')
766--TEMPLATE--
767{{ date1|date }}
768{{ date1|date('%d days %h hours') }}
769{{ date1|date('%d days %h hours', timezone1) }}
770--DATA--
771date_default_timezone_set('UTC');
772return array(
773    'date1' => new DateInterval('P2D'),
774    // This should have no effect on DateInterval formatting
775    'timezone1' => new DateTimeZone('America/New_York'),
776)
777--EXPECT--
7782 days
7792 days 0 hours
7802 days 0 hours
781--TEST--
782"date_modify" filter
783--TEMPLATE--
784{{ date1|date_modify('-1day')|date('Y-m-d H:i:s') }}
785{{ date2|date_modify('-1day')|date('Y-m-d H:i:s') }}
786--DATA--
787date_default_timezone_set('UTC');
788return array(
789    'date1' => '2010-10-04 13:45',
790    'date2' => new DateTime('2010-10-04 13:45'),
791)
792--EXPECT--
7932010-10-03 13:45:00
7942010-10-03 13:45:00
795--TEST--
796"date" filter
797--TEMPLATE--
798{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }}
799{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }}
800{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }}
801--DATA--
802date_default_timezone_set('UTC');
803return array('date' => mktime(13, 45, 0, 10, 4, 2010))
804--EXPECT--
80504/10/2010 08:45:00 -05:00
80604/10/2010 08:45:00 -05:00
80704/10/2010 08:45:00 -05:00
808--TEST--
809"date" filter
810--TEMPLATE--
811{{ date1|date }}
812{{ date1|date('d/m/Y') }}
813{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
814{{ date1|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
815{{ date1|date('d/m/Y H:i:s P', 'America/Chicago') }}
816{{ date1|date('e') }}
817{{ date1|date('d/m/Y H:i:s') }}
818
819{{ date2|date }}
820{{ date2|date('d/m/Y') }}
821{{ date2|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
822{{ date2|date('d/m/Y H:i:s', timezone1) }}
823{{ date2|date('d/m/Y H:i:s') }}
824
825{{ date3|date }}
826{{ date3|date('d/m/Y') }}
827
828{{ date4|date }}
829{{ date4|date('d/m/Y') }}
830
831{{ date5|date }}
832{{ date5|date('d/m/Y') }}
833
834{{ date6|date('d/m/Y H:i:s P', 'Europe/Paris') }}
835{{ date6|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
836{{ date6|date('d/m/Y H:i:s P', false) }}
837{{ date6|date('e', 'Europe/Paris') }}
838{{ date6|date('e', false) }}
839
840{{ date7|date }}
841--DATA--
842date_default_timezone_set('Europe/Paris');
843return array(
844    'date1' => mktime(13, 45, 0, 10, 4, 2010),
845    'date2' => new DateTime('2010-10-04 13:45'),
846    'date3' => '2010-10-04 13:45',
847    'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
848    'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
849    'date6' => new DateTime('2010-10-04 13:45', new DateTimeZone('America/New_York')),
850    'date7' => '2010-01-28T15:00:00+05:00',
851    'timezone1' => new DateTimeZone('America/New_York'),
852)
853--EXPECT--
854October 4, 2010 13:45
85504/10/2010
85604/10/2010 19:45:00
85704/10/2010 19:45:00 +08:00
85804/10/2010 06:45:00 -05:00
859Europe/Paris
86004/10/2010 13:45:00
861
862October 4, 2010 13:45
86304/10/2010
86404/10/2010 19:45:00
86504/10/2010 07:45:00
86604/10/2010 13:45:00
867
868October 4, 2010 13:45
86904/10/2010
870
871October 4, 2010 15:45
87204/10/2010
873
874January 2, 1964 04:04
87502/01/1964
876
87704/10/2010 19:45:00 +02:00
87805/10/2010 01:45:00 +08:00
87904/10/2010 13:45:00 -04:00
880Europe/Paris
881America/New_York
882
883January 28, 2010 11:00
884--TEST--
885"default" filter
886--TEMPLATE--
887Variable:
888{{ definedVar                  |default('default') is same as('default') ? 'ko' : 'ok' }}
889{{ zeroVar                     |default('default') is same as('default') ? 'ko' : 'ok' }}
890{{ emptyVar                    |default('default') is same as('default') ? 'ok' : 'ko' }}
891{{ nullVar                     |default('default') is same as('default') ? 'ok' : 'ko' }}
892{{ undefinedVar                |default('default') is same as('default') ? 'ok' : 'ko' }}
893Array access:
894{{ nested.definedVar           |default('default') is same as('default') ? 'ko' : 'ok' }}
895{{ nested['definedVar']        |default('default') is same as('default') ? 'ko' : 'ok' }}
896{{ nested.zeroVar              |default('default') is same as('default') ? 'ko' : 'ok' }}
897{{ nested.emptyVar             |default('default') is same as('default') ? 'ok' : 'ko' }}
898{{ nested.nullVar              |default('default') is same as('default') ? 'ok' : 'ko' }}
899{{ nested.undefinedVar         |default('default') is same as('default') ? 'ok' : 'ko' }}
900{{ nested['undefinedVar']      |default('default') is same as('default') ? 'ok' : 'ko' }}
901{{ undefinedVar.foo            |default('default') is same as('default') ? 'ok' : 'ko' }}
902Plain values:
903{{ 'defined'                   |default('default') is same as('default') ? 'ko' : 'ok' }}
904{{ 0                           |default('default') is same as('default') ? 'ko' : 'ok' }}
905{{ ''                          |default('default') is same as('default') ? 'ok' : 'ko' }}
906{{ null                        |default('default') is same as('default') ? 'ok' : 'ko' }}
907Precedence:
908{{ 'o' ~ nullVar               |default('k') }}
909{{ 'o' ~ nested.nullVar        |default('k') }}
910Object methods:
911{{ object.foo                  |default('default') is same as('default') ? 'ko' : 'ok' }}
912{{ object.undefinedMethod      |default('default') is same as('default') ? 'ok' : 'ko' }}
913{{ object.getFoo()             |default('default') is same as('default') ? 'ko' : 'ok' }}
914{{ object.getFoo('a')          |default('default') is same as('default') ? 'ko' : 'ok' }}
915{{ object.undefinedMethod()    |default('default') is same as('default') ? 'ok' : 'ko' }}
916{{ object.undefinedMethod('a') |default('default') is same as('default') ? 'ok' : 'ko' }}
917Deep nested:
918{{ nested.undefinedVar.foo.bar |default('default') is same as('default') ? 'ok' : 'ko' }}
919{{ nested.definedArray.0       |default('default') is same as('default') ? 'ko' : 'ok' }}
920{{ nested['definedArray'][0]   |default('default') is same as('default') ? 'ko' : 'ok' }}
921{{ object.self.foo             |default('default') is same as('default') ? 'ko' : 'ok' }}
922{{ object.self.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }}
923{{ object.undefinedMethod.self |default('default') is same as('default') ? 'ok' : 'ko' }}
924--DATA--
925return array(
926    'definedVar' => 'defined',
927    'zeroVar'    => 0,
928    'emptyVar'   => '',
929    'nullVar'    => null,
930    'nested'     => array(
931        'definedVar'   => 'defined',
932        'zeroVar'      => 0,
933        'emptyVar'     => '',
934        'nullVar'      => null,
935        'definedArray' => array(0),
936    ),
937    'object' => new TwigTestFoo(),
938)
939--CONFIG--
940return array('strict_variables' => false)
941--EXPECT--
942Variable:
943ok
944ok
945ok
946ok
947ok
948Array access:
949ok
950ok
951ok
952ok
953ok
954ok
955ok
956ok
957Plain values:
958ok
959ok
960ok
961ok
962Precedence:
963ok
964ok
965Object methods:
966ok
967ok
968ok
969ok
970ok
971ok
972Deep nested:
973ok
974ok
975ok
976ok
977ok
978ok
979--DATA--
980return array(
981    'definedVar' => 'defined',
982    'zeroVar'    => 0,
983    'emptyVar'   => '',
984    'nullVar'    => null,
985    'nested'     => array(
986        'definedVar'   => 'defined',
987        'zeroVar'      => 0,
988        'emptyVar'     => '',
989        'nullVar'      => null,
990        'definedArray' => array(0),
991    ),
992    'object' => new TwigTestFoo(),
993)
994--CONFIG--
995return array('strict_variables' => true)
996--EXPECT--
997Variable:
998ok
999ok
1000ok
1001ok
1002ok
1003Array access:
1004ok
1005ok
1006ok
1007ok
1008ok
1009ok
1010ok
1011ok
1012Plain values:
1013ok
1014ok
1015ok
1016ok
1017Precedence:
1018ok
1019ok
1020Object methods:
1021ok
1022ok
1023ok
1024ok
1025ok
1026ok
1027Deep nested:
1028ok
1029ok
1030ok
1031ok
1032ok
1033ok
1034--TEST--
1035dynamic filter
1036--TEMPLATE--
1037{{ 'bar'|foo_path }}
1038{{ 'bar'|a_foo_b_bar }}
1039--DATA--
1040return array()
1041--EXPECT--
1042foo/bar
1043a/b/bar
1044--TEST--
1045"escape" filter does not escape with the html strategy when using the html_attr strategy
1046--TEMPLATE--
1047{{ '<br />'|escape('html_attr') }}
1048--DATA--
1049return array()
1050--EXPECT--
1051&lt;br&#x20;&#x2F;&gt;
1052--TEST--
1053"escape" filter
1054--TEMPLATE--
1055{{ "愛していますか? <br />"|e }}
1056--DATA--
1057return array()
1058--EXPECT--
1059愛していますか? &lt;br /&gt;
1060--TEST--
1061"escape" filter
1062--TEMPLATE--
1063{{ "foo <br />"|e }}
1064--DATA--
1065return array()
1066--EXPECT--
1067foo &lt;br /&gt;
1068--TEST--
1069"first" filter
1070--TEMPLATE--
1071{{ [1, 2, 3, 4]|first }}
1072{{ {a: 1, b: 2, c: 3, d: 4}|first }}
1073{{ '1234'|first }}
1074{{ arr|first }}
1075{{ 'Ä€é'|first }}
1076{{ ''|first }}
1077--DATA--
1078return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
1079--EXPECT--
10801
10811
10821
10831
1084Ä
1085--TEST--
1086"escape" filter
1087--TEMPLATE--
1088{% set foo %}
1089    foo<br />
1090{% endset %}
1091
1092{{ foo|e('html') -}}
1093{{ foo|e('js') }}
1094{% autoescape true %}
1095    {{ foo }}
1096{% endautoescape %}
1097--DATA--
1098return array()
1099--EXPECT--
1100    foo&lt;br /&gt;
1101\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A
1102        foo<br />
1103--TEST--
1104"format" filter
1105--TEMPLATE--
1106{{ string|format(foo, 3) }}
1107--DATA--
1108return array('string' => '%s/%d', 'foo' => 'bar')
1109--EXPECT--
1110bar/3
1111--TEST--
1112"join" filter
1113--TEMPLATE--
1114{{ ["foo", "bar"]|join(', ') }}
1115{{ foo|join(', ') }}
1116{{ bar|join(', ') }}
1117--DATA--
1118return array('foo' => new TwigTestFoo(), 'bar' => new ArrayObject(array(3, 4)))
1119--EXPECT--
1120foo, bar
11211, 2
11223, 4
1123--TEST--
1124"json_encode" filter
1125--TEMPLATE--
1126{{ "foo"|json_encode|raw }}
1127{{ foo|json_encode|raw }}
1128{{ [foo, "foo"]|json_encode|raw }}
1129--DATA--
1130return array('foo' => new Twig_Markup('foo', 'UTF-8'))
1131--EXPECT--
1132"foo"
1133"foo"
1134["foo","foo"]
1135--TEST--
1136"last" filter
1137--TEMPLATE--
1138{{ [1, 2, 3, 4]|last }}
1139{{ {a: 1, b: 2, c: 3, d: 4}|last }}
1140{{ '1234'|last }}
1141{{ arr|last }}
1142{{ 'Ä€é'|last }}
1143{{ ''|last }}
1144--DATA--
1145return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
1146--EXPECT--
11474
11484
11494
11504
1151é
1152--TEST--
1153"length" filter
1154--TEMPLATE--
1155{{ array|length }}
1156{{ string|length }}
1157{{ number|length }}
1158{{ markup|length }}
1159--DATA--
1160return array('array' => array(1, 4), 'string' => 'foo', 'number' => 1000, 'markup' => new Twig_Markup('foo', 'UTF-8'))
1161--EXPECT--
11622
11633
11644
11653
1166--TEST--
1167"length" filter
1168--CONDITION--
1169function_exists('mb_get_info')
1170--TEMPLATE--
1171{{ string|length }}
1172{{ markup|length }}
1173--DATA--
1174return array('string' => 'été', 'markup' => new Twig_Markup('foo', 'UTF-8'))
1175--EXPECT--
11763
11773
1178--TEST--
1179"merge" filter
1180--TEMPLATE--
1181{{ items|merge({'bar': 'foo'})|join }}
1182{{ items|merge({'bar': 'foo'})|keys|join }}
1183{{ {'bar': 'foo'}|merge(items)|join }}
1184{{ {'bar': 'foo'}|merge(items)|keys|join }}
1185{{ numerics|merge([4, 5, 6])|join }}
1186--DATA--
1187return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3))
1188--EXPECT--
1189barfoo
1190foobar
1191foobar
1192barfoo
1193123456
1194--TEST--
1195"nl2br" filter
1196--TEMPLATE--
1197{{ "I like Twig.\nYou will like it too.\n\nEverybody like it!"|nl2br }}
1198{{ text|nl2br }}
1199--DATA--
1200return array('text' => "If you have some <strong>HTML</strong>\nit will be escaped.")
1201--EXPECT--
1202I like Twig.<br />
1203You will like it too.<br />
1204<br />
1205Everybody like it!
1206If you have some &lt;strong&gt;HTML&lt;/strong&gt;<br />
1207it will be escaped.
1208--TEST--
1209"number_format" filter with defaults.
1210--TEMPLATE--
1211{{ 20|number_format }}
1212{{ 20.25|number_format }}
1213{{ 20.25|number_format(1) }}
1214{{ 20.25|number_format(2, ',') }}
1215{{ 1020.25|number_format }}
1216{{ 1020.25|number_format(2, ',') }}
1217{{ 1020.25|number_format(2, ',', '.') }}
1218--DATA--
1219$twig->getExtension('core')->setNumberFormat(2, '!', '=');
1220return array();
1221--EXPECT--
122220!00
122320!25
122420!3
122520,25
12261=020!25
12271=020,25
12281.020,25
1229--TEST--
1230"number_format" filter
1231--TEMPLATE--
1232{{ 20|number_format }}
1233{{ 20.25|number_format }}
1234{{ 20.25|number_format(2) }}
1235{{ 20.25|number_format(2, ',') }}
1236{{ 1020.25|number_format(2, ',') }}
1237{{ 1020.25|number_format(2, ',', '.') }}
1238--DATA--
1239return array();
1240--EXPECT--
124120
124220
124320.25
124420,25
12451,020,25
12461.020,25
1247--TEST--
1248"replace" filter
1249--TEMPLATE--
1250{{ "I like %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }}
1251--DATA--
1252return array()
1253--EXPECT--
1254I like foo and bar.
1255--TEST--
1256"reverse" filter
1257--TEMPLATE--
1258{{ [1, 2, 3, 4]|reverse|join('') }}
1259{{ '1234évènement'|reverse }}
1260{{ arr|reverse|join('') }}
1261{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }}
1262{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }}
1263{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }}
1264--DATA--
1265return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
1266--EXPECT--
12674321
1268tnemenèvé4321
12694321
1270a,c
1271a,c
1272a,c
1273--TEST--
1274"round" filter
1275--TEMPLATE--
1276{{ 2.7|round }}
1277{{ 2.1|round }}
1278{{ 2.1234|round(3, 'floor') }}
1279{{ 2.1|round(0, 'ceil') }}
1280
1281{{ 21.3|round(-1)}}
1282{{ 21.3|round(-1, 'ceil')}}
1283{{ 21.3|round(-1, 'floor')}}
1284--DATA--
1285return array()
1286--EXPECT--
12873
12882
12892.123
12903
1291
129220
129330
129420
1295--TEST--
1296"slice" filter
1297--TEMPLATE--
1298{{ [1, 2, 3, 4][1:2]|join('') }}
1299{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }}
1300{{ [1, 2, 3, 4][start:length]|join('') }}
1301{{ [1, 2, 3, 4]|slice(1, 2)|join('') }}
1302{{ [1, 2, 3, 4]|slice(1, 2)|keys|join('') }}
1303{{ [1, 2, 3, 4]|slice(1, 2, true)|keys|join('') }}
1304{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }}
1305{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|keys|join('') }}
1306{{ '1234'|slice(1, 2) }}
1307{{ '1234'[1:2] }}
1308{{ arr|slice(1, 2)|join('') }}
1309{{ arr[1:2]|join('') }}
1310
1311{{ [1, 2, 3, 4]|slice(1)|join('') }}
1312{{ [1, 2, 3, 4][1:]|join('') }}
1313{{ '1234'|slice(1) }}
1314{{ '1234'[1:] }}
1315{{ '1234'[:1] }}
1316--DATA--
1317return array('start' => 1, 'length' => 2, 'arr' => new ArrayObject(array(1, 2, 3, 4)))
1318--EXPECT--
131923
132023
132123
132223
132301
132412
132523
1326bc
132723
132823
132923
133023
1331
1332234
1333234
1334234
1335234
13361
1337--TEST--
1338"sort" filter
1339--TEMPLATE--
1340{{ array1|sort|join }}
1341{{ array2|sort|join }}
1342--DATA--
1343return array('array1' => array(4, 1), 'array2' => array('foo', 'bar'))
1344--EXPECT--
134514
1346barfoo
1347--TEST--
1348"split" filter
1349--TEMPLATE--
1350{{ "one,two,three,four,five"|split(',')|join('-') }}
1351{{ foo|split(',')|join('-') }}
1352{{ foo|split(',', 3)|join('-') }}
1353{{ baz|split('')|join('-') }}
1354{{ baz|split('', 2)|join('-') }}
1355{{ foo|split(',', -2)|join('-') }}
1356--DATA--
1357return array('foo' => "one,two,three,four,five", 'baz' => '12345',)
1358--EXPECT--
1359one-two-three-four-five
1360one-two-three-four-five
1361one-two-three,four,five
13621-2-3-4-5
136312-34-5
1364one-two-three--TEST--
1365"trim" filter
1366--TEMPLATE--
1367{{ "  I like Twig.  "|trim }}
1368{{ text|trim }}
1369{{ "  foo/"|trim("/") }}
1370--DATA--
1371return array('text' => "  If you have some <strong>HTML</strong> it will be escaped.  ")
1372--EXPECT--
1373I like Twig.
1374If you have some &lt;strong&gt;HTML&lt;/strong&gt; it will be escaped.
1375  foo
1376--TEST--
1377"url_encode" filter for PHP < 5.4 and HHVM
1378--CONDITION--
1379defined('PHP_QUERY_RFC3986')
1380--TEMPLATE--
1381{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
1382{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
1383{{ {}|url_encode|default("default") }}
1384{{ 'spéßi%le%c0d@dspa ce'|url_encode }}
1385--DATA--
1386return array()
1387--EXPECT--
1388foo=bar&amp;number=3&amp;sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&amp;spa%20ce=
1389foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
1390default
1391sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce
1392--TEST--
1393"url_encode" filter
1394--CONDITION--
1395defined('PHP_QUERY_RFC3986')
1396--TEMPLATE--
1397{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
1398{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
1399{{ {}|url_encode|default("default") }}
1400{{ 'spéßi%le%c0d@dspa ce'|url_encode }}
1401--DATA--
1402return array()
1403--EXPECT--
1404foo=bar&amp;number=3&amp;sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&amp;spa%20ce=
1405foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
1406default
1407sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce
1408--TEST--
1409"attribute" function
1410--TEMPLATE--
1411{{ attribute(obj, method) }}
1412{{ attribute(array, item) }}
1413{{ attribute(obj, "bar", ["a", "b"]) }}
1414{{ attribute(obj, "bar", arguments) }}
1415{{ attribute(obj, method) is defined ? 'ok' : 'ko' }}
1416{{ attribute(obj, nonmethod) is defined ? 'ok' : 'ko' }}
1417--DATA--
1418return array('obj' => new TwigTestFoo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo', 'nonmethod' => 'xxx', 'arguments' => array('a', 'b'))
1419--EXPECT--
1420foo
1421bar
1422bar_a-b
1423bar_a-b
1424ok
1425ko
1426--TEST--
1427"block" function
1428--TEMPLATE--
1429{% extends 'base.twig' %}
1430{% block bar %}BAR{% endblock %}
1431--TEMPLATE(base.twig)--
1432{% block foo %}{{ block('bar') }}{% endblock %}
1433{% block bar %}BAR_BASE{% endblock %}
1434--DATA--
1435return array()
1436--EXPECT--
1437BARBAR
1438--TEST--
1439"constant" function
1440--TEMPLATE--
1441{{ constant('DATE_W3C') == expect ? 'true' : 'false' }}
1442{{ constant('ARRAY_AS_PROPS', object) }}
1443--DATA--
1444return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi')));
1445--EXPECT--
1446true
14472
1448--TEST--
1449"cycle" function
1450--TEMPLATE--
1451{% for i in 0..6 %}
1452{{ cycle(array1, i) }}-{{ cycle(array2, i) }}
1453{% endfor %}
1454--DATA--
1455return array('array1' => array('odd', 'even'), 'array2' => array('apple', 'orange', 'citrus'))
1456--EXPECT--
1457odd-apple
1458even-orange
1459odd-citrus
1460even-apple
1461odd-orange
1462even-citrus
1463odd-apple
1464--TEST--
1465"date" function
1466--TEMPLATE--
1467{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }}
1468{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }}
1469--DATA--
1470date_default_timezone_set('UTC');
1471return array('date' => mktime(13, 45, 0, 10, 4, 2010))
1472--EXPECT--
147304/10/2010 09:45:00 -04:00
147404/10/2010 09:45:00 -04:00
1475--TEST--
1476"date" function
1477--TEMPLATE--
1478{{ date() == date('now') ? 'OK' : 'KO' }}
1479{{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
1480{{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
1481{{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
1482{{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
1483{{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }}
1484--DATA--
1485date_default_timezone_set('UTC');
1486return array(
1487    'date1' => mktime(13, 45, 0, 10, 4, 2010),
1488    'date2' => new DateTime('2010-10-04 13:45'),
1489    'date3' => '2010-10-04 13:45',
1490    'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
1491    'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
1492)
1493--EXPECT--
1494OK
1495OK
1496OK
1497OK
1498OK
1499OK
1500--TEST--
1501"dump" function, xdebug is not loaded or xdebug <2.2-dev is loaded
1502--CONDITION--
1503!extension_loaded('xdebug') || (($r = new ReflectionExtension('xdebug')) && version_compare($r->getVersion(), '2.2-dev', '<'))
1504--TEMPLATE--
1505{{ dump() }}
1506--DATA--
1507return array('foo' => 'foo', 'bar' => 'bar')
1508--CONFIG--
1509return array('debug' => true, 'autoescape' => false);
1510--TEST--
1511"dump" function
1512--CONDITION--
1513!extension_loaded('xdebug')
1514--TEMPLATE--
1515{{ dump('foo') }}
1516{{ dump('foo', 'bar') }}
1517--DATA--
1518return array('foo' => 'foo', 'bar' => 'bar')
1519--CONFIG--
1520return array('debug' => true, 'autoescape' => false);
1521--EXPECT--
1522string(3) "foo"
1523
1524string(3) "foo"
1525string(3) "bar"
1526--TEST--
1527dynamic function
1528--TEMPLATE--
1529{{ foo_path('bar') }}
1530{{ a_foo_b_bar('bar') }}
1531--DATA--
1532return array()
1533--EXPECT--
1534foo/bar
1535a/b/bar
1536--TEST--
1537"include" function
1538--TEMPLATE--
1539{% set tmp = include("foo.twig") %}
1540
1541FOO{{ tmp }}BAR
1542--TEMPLATE(foo.twig)--
1543FOOBAR
1544--DATA--
1545return array()
1546--EXPECT--
1547FOO
1548FOOBARBAR
1549--TEST--
1550"include" function is safe for auto-escaping
1551--TEMPLATE--
1552{{ include("foo.twig") }}
1553--TEMPLATE(foo.twig)--
1554<p>Test</p>
1555--DATA--
1556return array()
1557--EXPECT--
1558<p>Test</p>
1559--TEST--
1560"include" function
1561--TEMPLATE--
1562FOO
1563{{ include("foo.twig") }}
1564
1565BAR
1566--TEMPLATE(foo.twig)--
1567FOOBAR
1568--DATA--
1569return array()
1570--EXPECT--
1571FOO
1572
1573FOOBAR
1574
1575BAR
1576--TEST--
1577"include" function allows expressions for the template to include
1578--TEMPLATE--
1579FOO
1580{{ include(foo) }}
1581
1582BAR
1583--TEMPLATE(foo.twig)--
1584FOOBAR
1585--DATA--
1586return array('foo' => 'foo.twig')
1587--EXPECT--
1588FOO
1589
1590FOOBAR
1591
1592BAR
1593--TEST--
1594"include" function
1595--TEMPLATE--
1596{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }}
1597{{ include("foo.twig", ignore_missing = true) }}
1598{{ include("foo.twig", ignore_missing = true, variables = {}) }}
1599{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }}
1600--DATA--
1601return array()
1602--EXPECT--
1603--TEST--
1604"include" function
1605--TEMPLATE--
1606{% extends "base.twig" %}
1607
1608{% block content %}
1609    {{ parent() }}
1610{% endblock %}
1611--TEMPLATE(base.twig)--
1612{% block content %}
1613    {{ include("foo.twig") }}
1614{% endblock %}
1615--DATA--
1616return array();
1617--EXCEPTION--
1618Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
1619--TEST--
1620"include" function
1621--TEMPLATE--
1622{{ include("foo.twig") }}
1623--DATA--
1624return array();
1625--EXCEPTION--
1626Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
1627--TEST--
1628"include" tag sandboxed
1629--TEMPLATE--
1630{{ include("foo.twig", sandboxed = true) }}
1631--TEMPLATE(foo.twig)--
1632{{ foo|e }}
1633--DATA--
1634return array()
1635--EXCEPTION--
1636Twig_Sandbox_SecurityError: Filter "e" is not allowed in "index.twig" at line 2.
1637--TEST--
1638"include" function accepts Twig_Template instance
1639--TEMPLATE--
1640{{ include(foo) }} FOO
1641--TEMPLATE(foo.twig)--
1642BAR
1643--DATA--
1644return array('foo' => $twig->loadTemplate('foo.twig'))
1645--EXPECT--
1646BAR FOO
1647--TEST--
1648"include" function
1649--TEMPLATE--
1650{{ include(["foo.twig", "bar.twig"]) }}
1651{{- include(["bar.twig", "foo.twig"]) }}
1652--TEMPLATE(foo.twig)--
1653foo
1654--DATA--
1655return array()
1656--EXPECT--
1657foo
1658foo
1659--TEST--
1660"include" function accept variables and with_context
1661--TEMPLATE--
1662{{ include("foo.twig") }}
1663{{- include("foo.twig", with_context = false) }}
1664{{- include("foo.twig", {'foo1': 'bar'}) }}
1665{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }}
1666--TEMPLATE(foo.twig)--
1667{% for k, v in _context %}{{ k }},{% endfor %}
1668--DATA--
1669return array('foo' => 'bar')
1670--EXPECT--
1671foo,global,_parent,
1672global,_parent,
1673foo,global,foo1,_parent,
1674foo1,global,_parent,
1675--TEST--
1676"include" function accept variables
1677--TEMPLATE--
1678{{ include("foo.twig", {'foo': 'bar'}) }}
1679{{- include("foo.twig", vars) }}
1680--TEMPLATE(foo.twig)--
1681{{ foo }}
1682--DATA--
1683return array('vars' => array('foo' => 'bar'))
1684--EXPECT--
1685bar
1686bar
1687--TEST--
1688"max" function
1689--TEMPLATE--
1690{{ max([2, 1, 3, 5, 4]) }}
1691{{ max(2, 1, 3, 5, 4) }}
1692{{ max({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }}
1693--DATA--
1694return array()
1695--EXPECT--
16965
16975
1698two
1699--TEST--
1700"min" function
1701--TEMPLATE--
1702{{ min(2, 1, 3, 5, 4) }}
1703{{ min([2, 1, 3, 5, 4]) }}
1704{{ min({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }}
1705--DATA--
1706return array()
1707--EXPECT--
17081
17091
1710five
1711--TEST--
1712"range" function
1713--TEMPLATE--
1714{{ range(low=0+1, high=10+0, step=2)|join(',') }}
1715--DATA--
1716return array()
1717--EXPECT--
17181,3,5,7,9
1719--TEST--
1720"block" function recursively called in a parent template
1721--TEMPLATE--
1722{% extends "ordered_menu.twig" %}
1723{% block label %}"{{ parent() }}"{% endblock %}
1724{% block list %}{% set class = 'b' %}{{ parent() }}{% endblock %}
1725--TEMPLATE(ordered_menu.twig)--
1726{% extends "menu.twig" %}
1727{% block list %}{% set class = class|default('a') %}<ol class="{{ class }}">{{ block('children') }}</ol>{% endblock %}
1728--TEMPLATE(menu.twig)--
1729{% extends "base.twig" %}
1730{% block list %}<ul>{{ block('children') }}</ul>{% endblock %}
1731{% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %}
1732{% block item %}<li>{% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %}</li>{% endblock %}
1733{% block label %}{{ item }}{{ block('unknown') }}{% endblock %}
1734--TEMPLATE(base.twig)--
1735{{ block('list') }}
1736--DATA--
1737return array('item' => array('1', '2', array('3.1', array('3.2.1', '3.2.2'), '3.4')))
1738--EXPECT--
1739<ol class="b"><li>"1"</li><li>"2"</li><li><ol class="b"><li>"3.1"</li><li><ol class="b"><li>"3.2.1"</li><li>"3.2.2"</li></ol></li><li>"3.4"</li></ol></li></ol>
1740--TEST--
1741"source" function
1742--TEMPLATE--
1743FOO
1744{{ source("foo.twig") }}
1745
1746BAR
1747--TEMPLATE(foo.twig)--
1748{{ foo }}<br />
1749--DATA--
1750return array()
1751--EXPECT--
1752FOO
1753
1754{{ foo }}<br />
1755
1756BAR
1757--TEST--
1758"template_from_string" function
1759--TEMPLATE--
1760{% include template_from_string(template) %}
1761
1762{% include template_from_string("Hello {{ name }}") %}
1763{% include template_from_string('{% extends "parent.twig" %}{% block content %}Hello {{ name }}{% endblock %}') %}
1764--TEMPLATE(parent.twig)--
1765{% block content %}{% endblock %}
1766--DATA--
1767return array('name' => 'Fabien', 'template' => "Hello {{ name }}")
1768--EXPECT--
1769Hello Fabien
1770Hello Fabien
1771Hello Fabien
1772--TEST--
1773macro
1774--TEMPLATE--
1775{% from _self import test %}
1776
1777{% macro test(a, b = 'bar') -%}
1778{{ a }}{{ b }}
1779{%- endmacro %}
1780
1781{{ test('foo') }}
1782{{ test('bar', 'foo') }}
1783--DATA--
1784return array();
1785--EXPECT--
1786foobar
1787barfoo
1788--TEST--
1789macro
1790--TEMPLATE--
1791{% import _self as macros %}
1792
1793{% macro foo(data) %}
1794    {{ data }}
1795{% endmacro %}
1796
1797{% macro bar() %}
1798    <br />
1799{% endmacro %}
1800
1801{{ macros.foo(macros.bar()) }}
1802--DATA--
1803return array();
1804--EXPECT--
1805<br />
1806--TEST--
1807macro
1808--TEMPLATE--
1809{% from _self import test %}
1810
1811{% macro test(this) -%}
1812    {{ this }}
1813{%- endmacro %}
1814
1815{{ test(this) }}
1816--DATA--
1817return array('this' => 'foo');
1818--EXPECT--
1819foo
1820--TEST--
1821macro
1822--TEMPLATE--
1823{% import _self as test %}
1824{% from _self import test %}
1825
1826{% macro test(a, b) -%}
1827    {{ a|default('a') }}<br />
1828    {{- b|default('b') }}<br />
1829{%- endmacro %}
1830
1831{{ test.test() }}
1832{{ test() }}
1833{{ test.test(1, "c") }}
1834{{ test(1, "c") }}
1835--DATA--
1836return array();
1837--EXPECT--
1838a<br />b<br />
1839a<br />b<br />
18401<br />c<br />
18411<br />c<br />
1842--TEST--
1843macro with a filter
1844--TEMPLATE--
1845{% import _self as test %}
1846
1847{% macro test() %}
1848    {% filter escape %}foo<br />{% endfilter %}
1849{% endmacro %}
1850
1851{{ test.test() }}
1852--DATA--
1853return array();
1854--EXPECT--
1855foo&lt;br /&gt;
1856--TEST--
1857Twig outputs 0 nodes correctly
1858--TEMPLATE--
1859{{ foo }}0{{ foo }}
1860--DATA--
1861return array('foo' => 'foo')
1862--EXPECT--
1863foo0foo
1864--TEST--
1865error in twig extension
1866--TEMPLATE--
1867{{ object.region is not null ? object.regionChoices[object.region] }}
1868--EXPECT--
1869house.region.s
1870--TEST--
1871Twig is able to deal with SimpleXMLElement instances as variables
1872--CONDITION--
1873version_compare(phpversion(), '5.3.0', '>=')
1874--TEMPLATE--
1875Hello '{{ images.image.0.group }}'!
1876{{ images.image.0.group.attributes.myattr }}
1877{{ images.children().image.count() }}
1878{% for image in images %}
1879    - {{ image.group }}
1880{% endfor %}
1881--DATA--
1882return array('images' => new SimpleXMLElement('<images><image><group myattr="example">foo</group></image><image><group>bar</group></image></images>'))
1883--EXPECT--
1884Hello 'foo'!
1885example
18862
1887    - foo
1888    - bar
1889--TEST--
1890Twig does not confuse strings with integers in getAttribute()
1891--TEMPLATE--
1892{{ hash['2e2'] }}
1893--DATA--
1894return array('hash' => array('2e2' => 'works'))
1895--EXPECT--
1896works
1897--TEST--
1898"autoescape" tag applies escaping on its children
1899--TEMPLATE--
1900{% autoescape %}
1901{{ var }}<br />
1902{% endautoescape %}
1903{% autoescape 'html' %}
1904{{ var }}<br />
1905{% endautoescape %}
1906{% autoescape false %}
1907{{ var }}<br />
1908{% endautoescape %}
1909{% autoescape true %}
1910{{ var }}<br />
1911{% endautoescape %}
1912{% autoescape false %}
1913{{ var }}<br />
1914{% endautoescape %}
1915--DATA--
1916return array('var' => '<br />')
1917--EXPECT--
1918&lt;br /&gt;<br />
1919&lt;br /&gt;<br />
1920<br /><br />
1921&lt;br /&gt;<br />
1922<br /><br />
1923--TEST--
1924"autoescape" tag applies escaping on embedded blocks
1925--TEMPLATE--
1926{% autoescape 'html' %}
1927  {% block foo %}
1928    {{ var }}
1929  {% endblock %}
1930{% endautoescape %}
1931--DATA--
1932return array('var' => '<br />')
1933--EXPECT--
1934&lt;br /&gt;
1935--TEST--
1936"autoescape" tag does not double-escape
1937--TEMPLATE--
1938{% autoescape 'html' %}
1939{{ var|escape }}
1940{% endautoescape %}
1941--DATA--
1942return array('var' => '<br />')
1943--EXPECT--
1944&lt;br /&gt;
1945--TEST--
1946"autoescape" tag applies escaping after calling functions
1947--TEMPLATE--
1948
1949autoescape false
1950{% autoescape false %}
1951
1952safe_br
1953{{ safe_br() }}
1954
1955unsafe_br
1956{{ unsafe_br() }}
1957
1958{% endautoescape %}
1959
1960autoescape 'html'
1961{% autoescape 'html' %}
1962
1963safe_br
1964{{ safe_br() }}
1965
1966unsafe_br
1967{{ unsafe_br() }}
1968
1969unsafe_br()|raw
1970{{ (unsafe_br())|raw }}
1971
1972safe_br()|escape
1973{{ (safe_br())|escape }}
1974
1975safe_br()|raw
1976{{ (safe_br())|raw }}
1977
1978unsafe_br()|escape
1979{{ (unsafe_br())|escape }}
1980
1981{% endautoescape %}
1982
1983autoescape js
1984{% autoescape 'js' %}
1985
1986safe_br
1987{{ safe_br() }}
1988
1989{% endautoescape %}
1990--DATA--
1991return array()
1992--EXPECT--
1993
1994autoescape false
1995
1996safe_br
1997<br />
1998
1999unsafe_br
2000<br />
2001
2002
2003autoescape 'html'
2004
2005safe_br
2006<br />
2007
2008unsafe_br
2009&lt;br /&gt;
2010
2011unsafe_br()|raw
2012<br />
2013
2014safe_br()|escape
2015&lt;br /&gt;
2016
2017safe_br()|raw
2018<br />
2019
2020unsafe_br()|escape
2021&lt;br /&gt;
2022
2023
2024autoescape js
2025
2026safe_br
2027\x3Cbr\x20\x2F\x3E
2028--TEST--
2029"autoescape" tag does not apply escaping on literals
2030--TEMPLATE--
2031{% autoescape 'html' %}
2032
20331. Simple literal
2034{{ "<br />" }}
2035
20362. Conditional expression with only literals
2037{{ true ? "<br />" : "<br>" }}
2038
20393. Conditional expression with a variable
2040{{ true ? "<br />" : someVar }}
2041
20424. Nested conditionals with only literals
2043{{ true ? (true ? "<br />" : "<br>") : "\n" }}
2044
20455. Nested conditionals with a variable
2046{{ true ? (true ? "<br />" : someVar) : "\n" }}
2047
20486. Nested conditionals with a variable marked safe
2049{{ true ? (true ? "<br />" : someVar|raw) : "\n" }}
2050
2051{% endautoescape %}
2052--DATA--
2053return array()
2054--EXPECT--
2055
20561. Simple literal
2057<br />
2058
20592. Conditional expression with only literals
2060<br />
2061
20623. Conditional expression with a variable
2063&lt;br /&gt;
2064
20654. Nested conditionals with only literals
2066<br />
2067
20685. Nested conditionals with a variable
2069&lt;br /&gt;
2070
20716. Nested conditionals with a variable marked safe
2072<br />
2073--TEST--
2074"autoescape" tags can be nested at will
2075--TEMPLATE--
2076{{ var }}
2077{% autoescape 'html' %}
2078  {{ var }}
2079  {% autoescape false %}
2080    {{ var }}
2081    {% autoescape 'html' %}
2082      {{ var }}
2083    {% endautoescape %}
2084    {{ var }}
2085  {% endautoescape %}
2086  {{ var }}
2087{% endautoescape %}
2088{{ var }}
2089--DATA--
2090return array('var' => '<br />')
2091--EXPECT--
2092&lt;br /&gt;
2093  &lt;br /&gt;
2094      <br />
2095          &lt;br /&gt;
2096        <br />
2097    &lt;br /&gt;
2098&lt;br /&gt;
2099--TEST--
2100"autoescape" tag applies escaping to object method calls
2101--TEMPLATE--
2102{% autoescape 'html' %}
2103{{ user.name }}
2104{{ user.name|lower }}
2105{{ user }}
2106{% endautoescape %}
2107--EXPECT--
2108Fabien&lt;br /&gt;
2109fabien&lt;br /&gt;
2110Fabien&lt;br /&gt;
2111--TEST--
2112"autoescape" tag does not escape when raw is used as a filter
2113--TEMPLATE--
2114{% autoescape 'html' %}
2115{{ var|raw }}
2116{% endautoescape %}
2117--DATA--
2118return array('var' => '<br />')
2119--EXPECT--
2120<br />
2121--TEST--
2122"autoescape" tag accepts an escaping strategy
2123--TEMPLATE--
2124{% autoescape true js %}{{ var }}{% endautoescape %}
2125
2126{% autoescape true html %}{{ var }}{% endautoescape %}
2127
2128{% autoescape 'js' %}{{ var }}{% endautoescape %}
2129
2130{% autoescape 'html' %}{{ var }}{% endautoescape %}
2131--DATA--
2132return array('var' => '<br />"')
2133--EXPECT--
2134\x3Cbr\x20\x2F\x3E\x22
2135&lt;br /&gt;&quot;
2136\x3Cbr\x20\x2F\x3E\x22
2137&lt;br /&gt;&quot;
2138--TEST--
2139escape types
2140--TEMPLATE--
2141
21421. autoescape 'html' |escape('js')
2143
2144{% autoescape 'html' %}
2145<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
2146{% endautoescape %}
2147
21482. autoescape 'html' |escape('js')
2149
2150{% autoescape 'html' %}
2151<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
2152{% endautoescape %}
2153
21543. autoescape 'js' |escape('js')
2155
2156{% autoescape 'js' %}
2157<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
2158{% endautoescape %}
2159
21604. no escape
2161
2162{% autoescape false %}
2163<a onclick="alert(&quot;{{ msg }}&quot;)"></a>
2164{% endautoescape %}
2165
21665. |escape('js')|escape('html')
2167
2168{% autoescape false %}
2169<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
2170{% endautoescape %}
2171
21726. autoescape 'html' |escape('js')|escape('html')
2173
2174{% autoescape 'html' %}
2175<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
2176{% endautoescape %}
2177
2178--DATA--
2179return array('msg' => "<>\n'\"")
2180--EXPECT--
2181
21821. autoescape 'html' |escape('js')
2183
2184<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
2185
21862. autoescape 'html' |escape('js')
2187
2188<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
2189
21903. autoescape 'js' |escape('js')
2191
2192<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
2193
21944. no escape
2195
2196<a onclick="alert(&quot;<>
2197'"&quot;)"></a>
2198
21995. |escape('js')|escape('html')
2200
2201<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
2202
22036. autoescape 'html' |escape('js')|escape('html')
2204
2205<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
2206
2207--TEST--
2208"autoescape" tag do not applies escaping on filter arguments
2209--TEMPLATE--
2210{% autoescape 'html' %}
2211{{ var|nl2br("<br />") }}
2212{{ var|nl2br("<br />"|escape) }}
2213{{ var|nl2br(sep) }}
2214{{ var|nl2br(sep|raw) }}
2215{{ var|nl2br(sep|escape) }}
2216{% endautoescape %}
2217--DATA--
2218return array('var' => "<Fabien>\nTwig", 'sep' => '<br />')
2219--EXPECT--
2220&lt;Fabien&gt;<br />
2221Twig
2222&lt;Fabien&gt;&lt;br /&gt;
2223Twig
2224&lt;Fabien&gt;<br />
2225Twig
2226&lt;Fabien&gt;<br />
2227Twig
2228&lt;Fabien&gt;&lt;br /&gt;
2229Twig
2230--TEST--
2231"autoescape" tag applies escaping after calling filters
2232--TEMPLATE--
2233{% autoescape 'html' %}
2234
2235(escape_and_nl2br is an escaper filter)
2236
22371. Don't escape escaper filter output
2238( var is escaped by |escape_and_nl2br, line-breaks are added,
2239  the output is not escaped )
2240{{ var|escape_and_nl2br }}
2241
22422. Don't escape escaper filter output
2243( var is escaped by |escape_and_nl2br, line-breaks are added,
2244  the output is not escaped, |raw is redundant )
2245{{ var|escape_and_nl2br|raw }}
2246
22473. Explicit escape
2248( var is escaped by |escape_and_nl2br, line-breaks are added,
2249  the output is explicitly escaped by |escape )
2250{{ var|escape_and_nl2br|escape }}
2251
22524. Escape non-escaper filter output
2253( var is upper-cased by |upper,
2254  the output is auto-escaped )
2255{{ var|upper }}
2256
22575. Escape if last filter is not an escaper
2258( var is escaped by |escape_and_nl2br, line-breaks are added,
2259  the output is upper-cased by |upper,
2260  the output is auto-escaped as |upper is not an escaper )
2261{{ var|escape_and_nl2br|upper }}
2262
22636. Don't escape escaper filter output
2264( var is upper cased by upper,
2265  the output is escaped by |escape_and_nl2br, line-breaks are added,
2266  the output is not escaped as |escape_and_nl2br is an escaper )
2267{{ var|upper|escape_and_nl2br }}
2268
22697. Escape if last filter is not an escaper
2270( the output of |format is "<b>" ~ var ~ "</b>",
2271  the output is auto-escaped )
2272{{ "<b>%s</b>"|format(var) }}
2273
22748. Escape if last filter is not an escaper
2275( the output of |format is "<b>" ~ var ~ "</b>",
2276  |raw is redundant,
2277  the output is auto-escaped )
2278{{ "<b>%s</b>"|raw|format(var) }}
2279
22809. Don't escape escaper filter output
2281( the output of |format is "<b>" ~ var ~ "</b>",
2282  the output is not escaped due to |raw filter at the end )
2283{{ "<b>%s</b>"|format(var)|raw }}
2284
228510. Don't escape escaper filter output
2286( the output of |format is "<b>" ~ var ~ "</b>",
2287  the output is not escaped due to |raw filter at the end,
2288  the |raw filter on var is redundant )
2289{{ "<b>%s</b>"|format(var|raw)|raw }}
2290
2291{% endautoescape %}
2292--DATA--
2293return array('var' => "<Fabien>\nTwig")
2294--EXPECT--
2295
2296(escape_and_nl2br is an escaper filter)
2297
22981. Don't escape escaper filter output
2299( var is escaped by |escape_and_nl2br, line-breaks are added,
2300  the output is not escaped )
2301&lt;Fabien&gt;<br />
2302Twig
2303
23042. Don't escape escaper filter output
2305( var is escaped by |escape_and_nl2br, line-breaks are added,
2306  the output is not escaped, |raw is redundant )
2307&lt;Fabien&gt;<br />
2308Twig
2309
23103. Explicit escape
2311( var is escaped by |escape_and_nl2br, line-breaks are added,
2312  the output is explicitly escaped by |escape )
2313&amp;lt;Fabien&amp;gt;&lt;br /&gt;
2314Twig
2315
23164. Escape non-escaper filter output
2317( var is upper-cased by |upper,
2318  the output is auto-escaped )
2319&lt;FABIEN&gt;
2320TWIG
2321
23225. Escape if last filter is not an escaper
2323( var is escaped by |escape_and_nl2br, line-breaks are added,
2324  the output is upper-cased by |upper,
2325  the output is auto-escaped as |upper is not an escaper )
2326&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
2327TWIG
2328
23296. Don't escape escaper filter output
2330( var is upper cased by upper,
2331  the output is escaped by |escape_and_nl2br, line-breaks are added,
2332  the output is not escaped as |escape_and_nl2br is an escaper )
2333&lt;FABIEN&gt;<br />
2334TWIG
2335
23367. Escape if last filter is not an escaper
2337( the output of |format is "<b>" ~ var ~ "</b>",
2338  the output is auto-escaped )
2339&lt;b&gt;&lt;Fabien&gt;
2340Twig&lt;/b&gt;
2341
23428. Escape if last filter is not an escaper
2343( the output of |format is "<b>" ~ var ~ "</b>",
2344  |raw is redundant,
2345  the output is auto-escaped )
2346&lt;b&gt;&lt;Fabien&gt;
2347Twig&lt;/b&gt;
2348
23499. Don't escape escaper filter output
2350( the output of |format is "<b>" ~ var ~ "</b>",
2351  the output is not escaped due to |raw filter at the end )
2352<b><Fabien>
2353Twig</b>
2354
235510. Don't escape escaper filter output
2356( the output of |format is "<b>" ~ var ~ "</b>",
2357  the output is not escaped due to |raw filter at the end,
2358  the |raw filter on var is redundant )
2359<b><Fabien>
2360Twig</b>
2361--TEST--
2362"autoescape" tag applies escaping after calling filters, and before calling pre_escape filters
2363--TEMPLATE--
2364{% autoescape 'html' %}
2365
2366(nl2br is pre_escaped for "html" and declared safe for "html")
2367
23681. Pre-escape and don't post-escape
2369( var|escape|nl2br )
2370{{ var|nl2br }}
2371
23722. Don't double-pre-escape
2373( var|escape|nl2br )
2374{{ var|escape|nl2br }}
2375
23763. Don't escape safe values
2377( var|raw|nl2br )
2378{{ var|raw|nl2br }}
2379
23804. Don't escape safe values
2381( var|escape|nl2br|nl2br )
2382{{ var|nl2br|nl2br }}
2383
23845. Re-escape values that are escaped for an other contexts
2385( var|escape_something|escape|nl2br )
2386{{ var|escape_something|nl2br }}
2387
23886. Still escape when using filters not declared safe
2389( var|escape|nl2br|upper|escape )
2390{{ var|nl2br|upper }}
2391
2392{% endautoescape %}
2393--DATA--
2394return array('var' => "<Fabien>\nTwig")
2395--EXPECT--
2396
2397(nl2br is pre_escaped for "html" and declared safe for "html")
2398
23991. Pre-escape and don't post-escape
2400( var|escape|nl2br )
2401&lt;Fabien&gt;<br />
2402Twig
2403
24042. Don't double-pre-escape
2405( var|escape|nl2br )
2406&lt;Fabien&gt;<br />
2407Twig
2408
24093. Don't escape safe values
2410( var|raw|nl2br )
2411<Fabien><br />
2412Twig
2413
24144. Don't escape safe values
2415( var|escape|nl2br|nl2br )
2416&lt;Fabien&gt;<br /><br />
2417Twig
2418
24195. Re-escape values that are escaped for an other contexts
2420( var|escape_something|escape|nl2br )
2421&lt;FABIEN&gt;<br />
2422TWIG
2423
24246. Still escape when using filters not declared safe
2425( var|escape|nl2br|upper|escape )
2426&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
2427TWIG
2428
2429--TEST--
2430"autoescape" tag handles filters preserving the safety
2431--TEMPLATE--
2432{% autoescape 'html' %}
2433
2434(preserves_safety is preserving safety for "html")
2435
24361. Unsafe values are still unsafe
2437( var|preserves_safety|escape )
2438{{ var|preserves_safety }}
2439
24402. Safe values are still safe
2441( var|escape|preserves_safety )
2442{{ var|escape|preserves_safety }}
2443
24443. Re-escape values that are escaped for an other contexts
2445( var|escape_something|preserves_safety|escape )
2446{{ var|escape_something|preserves_safety }}
2447
24484. Still escape when using filters not declared safe
2449( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
2450{{ var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'}) }}
2451
2452{% endautoescape %}
2453--DATA--
2454return array('var' => "<Fabien>\nTwig")
2455--EXPECT--
2456
2457(preserves_safety is preserving safety for "html")
2458
24591. Unsafe values are still unsafe
2460( var|preserves_safety|escape )
2461&lt;FABIEN&gt;
2462TWIG
2463
24642. Safe values are still safe
2465( var|escape|preserves_safety )
2466&LT;FABIEN&GT;
2467TWIG
2468
24693. Re-escape values that are escaped for an other contexts
2470( var|escape_something|preserves_safety|escape )
2471&lt;FABIEN&gt;
2472TWIG
2473
24744. Still escape when using filters not declared safe
2475( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
2476&amp;LT;FABPOT&amp;GT;
2477TWIG
2478
2479--TEST--
2480"block" tag
2481--TEMPLATE--
2482{% block title1 %}FOO{% endblock %}
2483{% block title2 foo|lower %}
2484--TEMPLATE(foo.twig)--
2485{% block content %}{% endblock %}
2486--DATA--
2487return array('foo' => 'bar')
2488--EXPECT--
2489FOObar
2490--TEST--
2491"block" tag
2492--TEMPLATE--
2493{% block content %}
2494    {% block content %}
2495    {% endblock %}
2496{% endblock %}
2497--DATA--
2498return array()
2499--EXCEPTION--
2500Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3
2501--TEST--
2502"§" special chars in a block name
2503--TEMPLATE--
2504{% block § %}
2505§
2506{% endblock § %}
2507--DATA--
2508return array()
2509--EXPECT--
2510§
2511--TEST--
2512"embed" tag
2513--TEMPLATE--
2514FOO
2515{% embed "foo.twig" %}
2516    {% block c1 %}
2517        {{ parent() }}
2518        block1extended
2519    {% endblock %}
2520{% endembed %}
2521
2522BAR
2523--TEMPLATE(foo.twig)--
2524A
2525{% block c1 %}
2526    block1
2527{% endblock %}
2528B
2529{% block c2 %}
2530    block2
2531{% endblock %}
2532C
2533--DATA--
2534return array()
2535--EXPECT--
2536FOO
2537
2538A
2539            block1
2540
2541        block1extended
2542    B
2543    block2
2544C
2545BAR
2546--TEST--
2547"embed" tag
2548--TEMPLATE(index.twig)--
2549FOO
2550{% embed "foo.twig" %}
2551    {% block c1 %}
2552        {{ nothing }}
2553    {% endblock %}
2554{% endembed %}
2555BAR
2556--TEMPLATE(foo.twig)--
2557{% block c1 %}{% endblock %}
2558--DATA--
2559return array()
2560--EXCEPTION--
2561Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5
2562--TEST--
2563"embed" tag
2564--TEMPLATE--
2565FOO
2566{% embed "foo.twig" %}
2567    {% block c1 %}
2568        {{ parent() }}
2569        block1extended
2570    {% endblock %}
2571{% endembed %}
2572
2573{% embed "foo.twig" %}
2574    {% block c1 %}
2575        {{ parent() }}
2576        block1extended
2577    {% endblock %}
2578{% endembed %}
2579
2580BAR
2581--TEMPLATE(foo.twig)--
2582A
2583{% block c1 %}
2584    block1
2585{% endblock %}
2586B
2587{% block c2 %}
2588    block2
2589{% endblock %}
2590C
2591--DATA--
2592return array()
2593--EXPECT--
2594FOO
2595
2596A
2597            block1
2598
2599        block1extended
2600    B
2601    block2
2602C
2603
2604A
2605            block1
2606
2607        block1extended
2608    B
2609    block2
2610C
2611BAR
2612--TEST--
2613"embed" tag
2614--TEMPLATE--
2615{% embed "foo.twig" %}
2616    {% block c1 %}
2617        {{ parent() }}
2618        {% embed "foo.twig" %}
2619            {% block c1 %}
2620                {{ parent() }}
2621                block1extended
2622            {% endblock %}
2623        {% endembed %}
2624
2625    {% endblock %}
2626{% endembed %}
2627--TEMPLATE(foo.twig)--
2628A
2629{% block c1 %}
2630    block1
2631{% endblock %}
2632B
2633{% block c2 %}
2634    block2
2635{% endblock %}
2636C
2637--DATA--
2638return array()
2639--EXPECT--
2640A
2641            block1
2642
2643
2644A
2645                    block1
2646
2647                block1extended
2648            B
2649    block2
2650C
2651    B
2652    block2
2653C
2654--TEST--
2655"embed" tag
2656--TEMPLATE--
2657{% extends "base.twig" %}
2658
2659{% block c1 %}
2660    {{ parent() }}
2661    blockc1baseextended
2662{% endblock %}
2663
2664{% block c2 %}
2665    {{ parent() }}
2666
2667    {% embed "foo.twig" %}
2668        {% block c1 %}
2669            {{ parent() }}
2670            block1extended
2671        {% endblock %}
2672    {% endembed %}
2673{% endblock %}
2674--TEMPLATE(base.twig)--
2675A
2676{% block c1 %}
2677    blockc1base
2678{% endblock %}
2679{% block c2 %}
2680    blockc2base
2681{% endblock %}
2682B
2683--TEMPLATE(foo.twig)--
2684A
2685{% block c1 %}
2686    block1
2687{% endblock %}
2688B
2689{% block c2 %}
2690    block2
2691{% endblock %}
2692C
2693--DATA--
2694return array()
2695--EXPECT--
2696A
2697        blockc1base
2698
2699    blockc1baseextended
2700        blockc2base
2701
2702
2703
2704A
2705                block1
2706
2707            block1extended
2708        B
2709    block2
2710CB--TEST--
2711"filter" tag applies a filter on its children
2712--TEMPLATE--
2713{% filter upper %}
2714Some text with a {{ var }}
2715{% endfilter %}
2716--DATA--
2717return array('var' => 'var')
2718--EXPECT--
2719SOME TEXT WITH A VAR
2720--TEST--
2721"filter" tag applies a filter on its children
2722--TEMPLATE--
2723{% filter json_encode|raw %}test{% endfilter %}
2724--DATA--
2725return array()
2726--EXPECT--
2727"test"
2728--TEST--
2729"filter" tags accept multiple chained filters
2730--TEMPLATE--
2731{% filter lower|title %}
2732  {{ var }}
2733{% endfilter %}
2734--DATA--
2735return array('var' => 'VAR')
2736--EXPECT--
2737    Var
2738--TEST--
2739"filter" tags can be nested at will
2740--TEMPLATE--
2741{% filter lower|title %}
2742  {{ var }}
2743  {% filter upper %}
2744    {{ var }}
2745  {% endfilter %}
2746  {{ var }}
2747{% endfilter %}
2748--DATA--
2749return array('var' => 'var')
2750--EXPECT--
2751  Var
2752      Var
2753    Var
2754--TEST--
2755"filter" tag applies the filter on "for" tags
2756--TEMPLATE--
2757{% filter upper %}
2758{% for item in items %}
2759{{ item }}
2760{% endfor %}
2761{% endfilter %}
2762--DATA--
2763return array('items' => array('a', 'b'))
2764--EXPECT--
2765A
2766B
2767--TEST--
2768"filter" tag applies the filter on "if" tags
2769--TEMPLATE--
2770{% filter upper %}
2771{% if items %}
2772{{ items|join(', ') }}
2773{% endif %}
2774
2775{% if items.3 is defined %}
2776FOO
2777{% else %}
2778{{ items.1 }}
2779{% endif %}
2780
2781{% if items.3 is defined %}
2782FOO
2783{% elseif items.1 %}
2784{{ items.0 }}
2785{% endif %}
2786
2787{% endfilter %}
2788--DATA--
2789return array('items' => array('a', 'b'))
2790--EXPECT--
2791A, B
2792
2793B
2794
2795A
2796--TEST--
2797"for" tag takes a condition
2798--TEMPLATE--
2799{% for i in 1..5 if i is odd -%}
2800    {{ loop.index }}.{{ i }}{{ foo.bar }}
2801{% endfor %}
2802--DATA--
2803return array('foo' => array('bar' => 'X'))
2804--CONFIG--
2805return array('strict_variables' => false)
2806--EXPECT--
28071.1X
28082.3X
28093.5X
2810--TEST--
2811"for" tag keeps the context safe
2812--TEMPLATE--
2813{% for item in items %}
2814  {% for item in items %}
2815    * {{ item }}
2816  {% endfor %}
2817  * {{ item }}
2818{% endfor %}
2819--DATA--
2820return array('items' => array('a', 'b'))
2821--EXPECT--
2822      * a
2823      * b
2824    * a
2825      * a
2826      * b
2827    * b
2828--TEST--
2829"for" tag can use an "else" clause
2830--TEMPLATE--
2831{% for item in items %}
2832  * {{ item }}
2833{% else %}
2834  no item
2835{% endfor %}
2836--DATA--
2837return array('items' => array('a', 'b'))
2838--EXPECT--
2839  * a
2840  * b
2841--DATA--
2842return array('items' => array())
2843--EXPECT--
2844  no item
2845--DATA--
2846return array()
2847--CONFIG--
2848return array('strict_variables' => false)
2849--EXPECT--
2850  no item
2851--TEST--
2852"for" tag does not reset inner variables
2853--TEMPLATE--
2854{% for i in 1..2 %}
2855  {% for j in 0..2 %}
2856    {{k}}{% set k = k+1 %} {{ loop.parent.loop.index }}
2857  {% endfor %}
2858{% endfor %}
2859--DATA--
2860return array('k' => 0)
2861--EXPECT--
2862      0 1
2863      1 1
2864      2 1
2865        3 2
2866      4 2
2867      5 2
2868--TEST--
2869"for" tag can iterate over keys and values
2870--TEMPLATE--
2871{% for key, item in items %}
2872  * {{ key }}/{{ item }}
2873{% endfor %}
2874--DATA--
2875return array('items' => array('a', 'b'))
2876--EXPECT--
2877  * 0/a
2878  * 1/b
2879--TEST--
2880"for" tag can iterate over keys
2881--TEMPLATE--
2882{% for key in items|keys %}
2883  * {{ key }}
2884{% endfor %}
2885--DATA--
2886return array('items' => array('a', 'b'))
2887--EXPECT--
2888  * 0
2889  * 1
2890--TEST--
2891"for" tag adds a loop variable to the context locally
2892--TEMPLATE--
2893{% for item in items %}
2894{% endfor %}
2895{% if loop is not defined %}WORKS{% endif %}
2896--DATA--
2897return array('items' => array())
2898--EXPECT--
2899WORKS
2900--TEST--
2901"for" tag adds a loop variable to the context
2902--TEMPLATE--
2903{% for item in items %}
2904  * {{ loop.index }}/{{ loop.index0 }}
2905  * {{ loop.revindex }}/{{ loop.revindex0 }}
2906  * {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
2907
2908{% endfor %}
2909--DATA--
2910return array('items' => array('a', 'b'))
2911--EXPECT--
2912  * 1/0
2913  * 2/1
2914  * 1//2
2915
2916  * 2/1
2917  * 1/0
2918  * /1/2
2919--TEST--
2920"for" tag
2921--TEMPLATE--
2922{% for i, item in items if loop.last > 0 %}
2923{% endfor %}
2924--DATA--
2925return array('items' => array('a', 'b'))
2926--EXCEPTION--
2927Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2
2928--TEST--
2929"for" tag
2930--TEMPLATE--
2931{% for i, item in items if i > 0 %}
2932    {{ loop.last }}
2933{% endfor %}
2934--DATA--
2935return array('items' => array('a', 'b'))
2936--EXCEPTION--
2937Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3
2938--TEST--
2939"for" tag can use an "else" clause
2940--TEMPLATE--
2941{% for item in items %}
2942  {% for item in items1 %}
2943    * {{ item }}
2944  {% else %}
2945    no {{ item }}
2946  {% endfor %}
2947{% else %}
2948  no item1
2949{% endfor %}
2950--DATA--
2951return array('items' => array('a', 'b'), 'items1' => array())
2952--EXPECT--
2953no a
2954        no b
2955--TEST--
2956"for" tag iterates over iterable and countable objects
2957--TEMPLATE--
2958{% for item in items %}
2959  * {{ item }}
2960  * {{ loop.index }}/{{ loop.index0 }}
2961  * {{ loop.revindex }}/{{ loop.revindex0 }}
2962  * {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
2963
2964{% endfor %}
2965
2966{% for key, value in items %}
2967  * {{ key }}/{{ value }}
2968{% endfor %}
2969
2970{% for key in items|keys %}
2971  * {{ key }}
2972{% endfor %}
2973--DATA--
2974class ItemsIteratorCountable implements Iterator, Countable
2975{
2976  protected $values = array('foo' => 'bar', 'bar' => 'foo');
2977  public function current() { return current($this->values); }
2978  public function key() { return key($this->values); }
2979  public function next() { return next($this->values); }
2980  public function rewind() { return reset($this->values); }
2981  public function valid() { return false !== current($this->values); }
2982  public function count() { return count($this->values); }
2983}
2984return array('items' => new ItemsIteratorCountable())
2985--EXPECT--
2986  * bar
2987  * 1/0
2988  * 2/1
2989  * 1//2
2990
2991  * foo
2992  * 2/1
2993  * 1/0
2994  * /1/2
2995
2996
2997  * foo/bar
2998  * bar/foo
2999
3000  * foo
3001  * bar
3002--TEST--
3003"for" tag iterates over iterable objects
3004--TEMPLATE--
3005{% for item in items %}
3006  * {{ item }}
3007  * {{ loop.index }}/{{ loop.index0 }}
3008  * {{ loop.first }}
3009
3010{% endfor %}
3011
3012{% for key, value in items %}
3013  * {{ key }}/{{ value }}
3014{% endfor %}
3015
3016{% for key in items|keys %}
3017  * {{ key }}
3018{% endfor %}
3019--DATA--
3020class ItemsIterator implements Iterator
3021{
3022  protected $values = array('foo' => 'bar', 'bar' => 'foo');
3023  public function current() { return current($this->values); }
3024  public function key() { return key($this->values); }
3025  public function next() { return next($this->values); }
3026  public function rewind() { return reset($this->values); }
3027  public function valid() { return false !== current($this->values); }
3028}
3029return array('items' => new ItemsIterator())
3030--EXPECT--
3031  * bar
3032  * 1/0
3033  * 1
3034
3035  * foo
3036  * 2/1
3037  *
3038
3039
3040  * foo/bar
3041  * bar/foo
3042
3043  * foo
3044  * bar
3045--TEST--
3046"for" tags can be nested
3047--TEMPLATE--
3048{% for key, item in items %}
3049* {{ key }} ({{ loop.length }}):
3050{% for value in item %}
3051  * {{ value }} ({{ loop.length }})
3052{% endfor %}
3053{% endfor %}
3054--DATA--
3055return array('items' => array('a' => array('a1', 'a2', 'a3'), 'b' => array('b1')))
3056--EXPECT--
3057* a (2):
3058  * a1 (3)
3059  * a2 (3)
3060  * a3 (3)
3061* b (2):
3062  * b1 (1)
3063--TEST--
3064"for" tag iterates over item values
3065--TEMPLATE--
3066{% for item in items %}
3067  * {{ item }}
3068{% endfor %}
3069--DATA--
3070return array('items' => array('a', 'b'))
3071--EXPECT--
3072  * a
3073  * b
3074--TEST--
3075global variables
3076--TEMPLATE--
3077{% include "included.twig" %}
3078{% from "included.twig" import foobar %}
3079{{ foobar() }}
3080--TEMPLATE(included.twig)--
3081{% macro foobar() %}
3082called foobar
3083{% endmacro %}
3084--DATA--
3085return array();
3086--EXPECT--
3087called foobar
3088--TEST--
3089"if" creates a condition
3090--TEMPLATE--
3091{% if a is defined %}
3092  {{ a }}
3093{% elseif b is defined %}
3094  {{ b }}
3095{% else %}
3096  NOTHING
3097{% endif %}
3098--DATA--
3099return array('a' => 'a')
3100--EXPECT--
3101  a
3102--DATA--
3103return array('b' => 'b')
3104--EXPECT--
3105  b
3106--DATA--
3107return array()
3108--EXPECT--
3109  NOTHING
3110--TEST--
3111"if" takes an expression as a test
3112--TEMPLATE--
3113{% if a < 2 %}
3114  A1
3115{% elseif a > 10 %}
3116  A2
3117{% else %}
3118  A3
3119{% endif %}
3120--DATA--
3121return array('a' => 1)
3122--EXPECT--
3123  A1
3124--DATA--
3125return array('a' => 12)
3126--EXPECT--
3127  A2
3128--DATA--
3129return array('a' => 7)
3130--EXPECT--
3131  A3
3132--TEST--
3133"include" tag
3134--TEMPLATE--
3135FOO
3136{% include "foo.twig" %}
3137
3138BAR
3139--TEMPLATE(foo.twig)--
3140FOOBAR
3141--DATA--
3142return array()
3143--EXPECT--
3144FOO
3145
3146FOOBAR
3147BAR
3148--TEST--
3149"include" tag allows expressions for the template to include
3150--TEMPLATE--
3151FOO
3152{% include foo %}
3153
3154BAR
3155--TEMPLATE(foo.twig)--
3156FOOBAR
3157--DATA--
3158return array('foo' => 'foo.twig')
3159--EXPECT--
3160FOO
3161
3162FOOBAR
3163BAR
3164--TEST--
3165"include" tag
3166--TEMPLATE--
3167{% include ["foo.twig", "bar.twig"] ignore missing %}
3168{% include "foo.twig" ignore missing %}
3169{% include "foo.twig" ignore missing with {} %}
3170{% include "foo.twig" ignore missing with {} only %}
3171--DATA--
3172return array()
3173--EXPECT--
3174--TEST--
3175"include" tag
3176--TEMPLATE--
3177{% extends "base.twig" %}
3178
3179{% block content %}
3180    {{ parent() }}
3181{% endblock %}
3182--TEMPLATE(base.twig)--
3183{% block content %}
3184    {% include "foo.twig" %}
3185{% endblock %}
3186--DATA--
3187return array();
3188--EXCEPTION--
3189Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
3190--TEST--
3191"include" tag
3192--TEMPLATE--
3193{% include "foo.twig" %}
3194--DATA--
3195return array();
3196--EXCEPTION--
3197Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
3198--TEST--
3199"include" tag accept variables and only
3200--TEMPLATE--
3201{% include "foo.twig" %}
3202{% include "foo.twig" only %}
3203{% include "foo.twig" with {'foo1': 'bar'} %}
3204{% include "foo.twig" with {'foo1': 'bar'} only %}
3205--TEMPLATE(foo.twig)--
3206{% for k, v in _context %}{{ k }},{% endfor %}
3207--DATA--
3208return array('foo' => 'bar')
3209--EXPECT--
3210foo,global,_parent,
3211global,_parent,
3212foo,global,foo1,_parent,
3213foo1,global,_parent,
3214--TEST--
3215"include" tag accepts Twig_Template instance
3216--TEMPLATE--
3217{% include foo %} FOO
3218--TEMPLATE(foo.twig)--
3219BAR
3220--DATA--
3221return array('foo' => $twig->loadTemplate('foo.twig'))
3222--EXPECT--
3223BAR FOO
3224--TEST--
3225"include" tag
3226--TEMPLATE--
3227{% include ["foo.twig", "bar.twig"] %}
3228{% include ["bar.twig", "foo.twig"] %}
3229--TEMPLATE(foo.twig)--
3230foo
3231--DATA--
3232return array()
3233--EXPECT--
3234foo
3235foo
3236--TEST--
3237"include" tag accept variables
3238--TEMPLATE--
3239{% include "foo.twig" with {'foo': 'bar'} %}
3240{% include "foo.twig" with vars %}
3241--TEMPLATE(foo.twig)--
3242{{ foo }}
3243--DATA--
3244return array('vars' => array('foo' => 'bar'))
3245--EXPECT--
3246bar
3247bar
3248--TEST--
3249"extends" tag
3250--TEMPLATE--
3251{% extends "foo.twig" %}
3252
3253{% block content %}
3254FOO
3255{% endblock %}
3256--TEMPLATE(foo.twig)--
3257{% block content %}{% endblock %}
3258--DATA--
3259return array()
3260--EXPECT--
3261FOO
3262--TEST--
3263block_expr2
3264--TEMPLATE--
3265{% extends "base2.twig" %}
3266
3267{% block element -%}
3268    Element:
3269    {{- parent() -}}
3270{% endblock %}
3271--TEMPLATE(base2.twig)--
3272{% extends "base.twig" %}
3273--TEMPLATE(base.twig)--
3274{% spaceless %}
3275{% block element -%}
3276    <div>
3277        {%- if item.children is defined %}
3278            {%- for item in item.children %}
3279                {{- block('element') -}}
3280            {% endfor %}
3281        {%- endif -%}
3282    </div>
3283{%- endblock %}
3284{% endspaceless %}
3285--DATA--
3286return array(
3287    'item' => array(
3288        'children' => array(
3289            null,
3290            null,
3291        )
3292    )
3293)
3294--EXPECT--
3295Element:<div>Element:<div></div>Element:<div></div></div>
3296--TEST--
3297block_expr
3298--TEMPLATE--
3299{% extends "base.twig" %}
3300
3301{% block element -%}
3302    Element:
3303    {{- parent() -}}
3304{% endblock %}
3305--TEMPLATE(base.twig)--
3306{% spaceless %}
3307{% block element -%}
3308    <div>
3309        {%- if item.children is defined %}
3310            {%- for item in item.children %}
3311                {{- block('element') -}}
3312            {% endfor %}
3313        {%- endif -%}
3314    </div>
3315{%- endblock %}
3316{% endspaceless %}
3317--DATA--
3318return array(
3319    'item' => array(
3320        'children' => array(
3321            null,
3322            null,
3323        )
3324    )
3325)
3326--EXPECT--
3327Element:<div>Element:<div></div>Element:<div></div></div>
3328--TEST--
3329"extends" tag
3330--TEMPLATE--
3331{% extends standalone ? foo : 'bar.twig' %}
3332
3333{% block content %}{{ parent() }}FOO{% endblock %}
3334--TEMPLATE(foo.twig)--
3335{% block content %}FOO{% endblock %}
3336--TEMPLATE(bar.twig)--
3337{% block content %}BAR{% endblock %}
3338--DATA--
3339return array('foo' => 'foo.twig', 'standalone' => true)
3340--EXPECT--
3341FOOFOO
3342--TEST--
3343"extends" tag
3344--TEMPLATE--
3345{% extends foo %}
3346
3347{% block content %}
3348FOO
3349{% endblock %}
3350--TEMPLATE(foo.twig)--
3351{% block content %}{% endblock %}
3352--DATA--
3353return array('foo' => 'foo.twig')
3354--EXPECT--
3355FOO
3356--TEST--
3357"extends" tag
3358--TEMPLATE--
3359{% extends "foo.twig" %}
3360--TEMPLATE(foo.twig)--
3361{% block content %}FOO{% endblock %}
3362--DATA--
3363return array()
3364--EXPECT--
3365FOO
3366--TEST--
3367"extends" tag
3368--TEMPLATE--
3369{% extends ["foo.twig", "bar.twig"] %}
3370--TEMPLATE(bar.twig)--
3371{% block content %}
3372foo
3373{% endblock %}
3374--DATA--
3375return array()
3376--EXPECT--
3377foo
3378--TEST--
3379"extends" tag
3380--TEMPLATE--
3381{% extends "layout.twig" %}{% block content %}{{ parent() }}index {% endblock %}
3382--TEMPLATE(layout.twig)--
3383{% extends "base.twig" %}{% block content %}{{ parent() }}layout {% endblock %}
3384--TEMPLATE(base.twig)--
3385{% block content %}base {% endblock %}
3386--DATA--
3387return array()
3388--EXPECT--
3389base layout index
3390--TEST--
3391"block" tag
3392--TEMPLATE--
3393{% block content %}
3394    CONTENT
3395    {%- block subcontent -%}
3396        SUBCONTENT
3397    {%- endblock -%}
3398    ENDCONTENT
3399{% endblock %}
3400--TEMPLATE(foo.twig)--
3401--DATA--
3402return array()
3403--EXPECT--
3404CONTENTSUBCONTENTENDCONTENT
3405--TEST--
3406"block" tag
3407--TEMPLATE--
3408{% extends "foo.twig" %}
3409
3410{% block content %}
3411    {% block subcontent %}
3412        {% block subsubcontent %}
3413            SUBSUBCONTENT
3414        {% endblock %}
3415    {% endblock %}
3416{% endblock %}
3417--TEMPLATE(foo.twig)--
3418{% block content %}
3419    {% block subcontent %}
3420        SUBCONTENT
3421    {% endblock %}
3422{% endblock %}
3423--DATA--
3424return array()
3425--EXPECT--
3426SUBSUBCONTENT
3427--TEST--
3428"extends" tag
3429--TEMPLATE--
3430{% extends "layout.twig" %}
3431{% block inside %}INSIDE{% endblock inside %}
3432--TEMPLATE(layout.twig)--
3433{% extends "base.twig" %}
3434{% block body %}
3435    {% block inside '' %}
3436{% endblock body %}
3437--TEMPLATE(base.twig)--
3438{% block body '' %}
3439--DATA--
3440return array()
3441--EXPECT--
3442INSIDE
3443--TEST--
3444"extends" tag
3445--TEMPLATE--
3446{% extends foo ? 'foo.twig' : 'bar.twig' %}
3447--TEMPLATE(foo.twig)--
3448FOO
3449--TEMPLATE(bar.twig)--
3450BAR
3451--DATA--
3452return array('foo' => true)
3453--EXPECT--
3454FOO
3455--DATA--
3456return array('foo' => false)
3457--EXPECT--
3458BAR
3459--TEST--
3460"extends" tag
3461--TEMPLATE--
3462{% block content %}
3463    {% extends "foo.twig" %}
3464{% endblock %}
3465--EXCEPTION--
3466Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3
3467--TEST--
3468"extends" tag
3469--TEMPLATE--
3470{% extends "base.twig" %}
3471{% block content %}{% include "included.twig" %}{% endblock %}
3472
3473{% block footer %}Footer{% endblock %}
3474--TEMPLATE(included.twig)--
3475{% extends "base.twig" %}
3476{% block content %}Included Content{% endblock %}
3477--TEMPLATE(base.twig)--
3478{% block content %}Default Content{% endblock %}
3479
3480{% block footer %}Default Footer{% endblock %}
3481--DATA--
3482return array()
3483--EXPECT--
3484Included Content
3485Default Footer
3486Footer
3487--TEST--
3488"extends" tag
3489--TEMPLATE--
3490{% extends "foo.twig" %}
3491
3492{% block content %}
3493  {% block inside %}
3494    INSIDE OVERRIDDEN
3495  {% endblock %}
3496
3497  BEFORE
3498  {{ parent() }}
3499  AFTER
3500{% endblock %}
3501--TEMPLATE(foo.twig)--
3502{% block content %}
3503  BAR
3504{% endblock %}
3505--DATA--
3506return array()
3507--EXPECT--
3508
3509INSIDE OVERRIDDEN
3510
3511  BEFORE
3512    BAR
3513
3514  AFTER
3515--TEST--
3516"extends" tag
3517--TEMPLATE--
3518{% extends "foo.twig" %}
3519
3520{% block content %}{{ parent() }}FOO{{ parent() }}{% endblock %}
3521--TEMPLATE(foo.twig)--
3522{% block content %}BAR{% endblock %}
3523--DATA--
3524return array()
3525--EXPECT--
3526BARFOOBAR
3527--TEST--
3528"parent" tag
3529--TEMPLATE--
3530{% use 'foo.twig' %}
3531
3532{% block content %}
3533    {{ parent() }}
3534{% endblock %}
3535--TEMPLATE(foo.twig)--
3536{% block content %}BAR{% endblock %}
3537--DATA--
3538return array()
3539--EXPECT--
3540BAR
3541--TEST--
3542"parent" tag
3543--TEMPLATE--
3544{% block content %}
3545    {{ parent() }}
3546{% endblock %}
3547--EXCEPTION--
3548Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3
3549--TEST--
3550"extends" tag accepts Twig_Template instance
3551--TEMPLATE--
3552{% extends foo %}
3553
3554{% block content %}
3555{{ parent() }}FOO
3556{% endblock %}
3557--TEMPLATE(foo.twig)--
3558{% block content %}BAR{% endblock %}
3559--DATA--
3560return array('foo' => $twig->loadTemplate('foo.twig'))
3561--EXPECT--
3562BARFOO
3563--TEST--
3564"parent" function
3565--TEMPLATE--
3566{% extends "parent.twig" %}
3567
3568{% use "use1.twig" %}
3569{% use "use2.twig" %}
3570
3571{% block content_parent %}
3572    {{ parent() }}
3573{% endblock %}
3574
3575{% block content_use1 %}
3576    {{ parent() }}
3577{% endblock %}
3578
3579{% block content_use2 %}
3580    {{ parent() }}
3581{% endblock %}
3582
3583{% block content %}
3584    {{ block('content_use1_only') }}
3585    {{ block('content_use2_only') }}
3586{% endblock %}
3587--TEMPLATE(parent.twig)--
3588{% block content_parent 'content_parent' %}
3589{% block content_use1 'content_parent' %}
3590{% block content_use2 'content_parent' %}
3591{% block content '' %}
3592--TEMPLATE(use1.twig)--
3593{% block content_use1 'content_use1' %}
3594{% block content_use2 'content_use1' %}
3595{% block content_use1_only 'content_use1_only' %}
3596--TEMPLATE(use2.twig)--
3597{% block content_use2 'content_use2' %}
3598{% block content_use2_only 'content_use2_only' %}
3599--DATA--
3600return array()
3601--EXPECT--
3602    content_parent
3603    content_use1
3604    content_use2
3605    content_use1_only
3606    content_use2_only
3607--TEST--
3608"macro" tag
3609--TEMPLATE--
3610{% import _self as macros %}
3611
3612{{ macros.input('username') }}
3613{{ macros.input('password', null, 'password', 1) }}
3614
3615{% macro input(name, value, type, size) %}
3616  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
3617{% endmacro %}
3618--DATA--
3619return array()
3620--EXPECT--
3621  <input type="text" name="username" value="" size="20">
3622
3623  <input type="password" name="password" value="" size="1">
3624--TEST--
3625"macro" tag supports name for endmacro
3626--TEMPLATE--
3627{% import _self as macros %}
3628
3629{{ macros.foo() }}
3630{{ macros.bar() }}
3631
3632{% macro foo() %}foo{% endmacro %}
3633{% macro bar() %}bar{% endmacro bar %}
3634--DATA--
3635return array()
3636--EXPECT--
3637foo
3638bar
3639
3640--TEST--
3641"macro" tag
3642--TEMPLATE--
3643{% import 'forms.twig' as forms %}
3644
3645{{ forms.input('username') }}
3646{{ forms.input('password', null, 'password', 1) }}
3647--TEMPLATE(forms.twig)--
3648{% macro input(name, value, type, size) %}
3649  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
3650{% endmacro %}
3651--DATA--
3652return array()
3653--EXPECT--
3654  <input type="text" name="username" value="" size="20">
3655
3656  <input type="password" name="password" value="" size="1">
3657--TEST--
3658"macro" tag
3659--TEMPLATE--
3660{% from 'forms.twig' import foo %}
3661{% from 'forms.twig' import foo as foobar, bar %}
3662
3663{{ foo('foo') }}
3664{{ foobar('foo') }}
3665{{ bar('foo') }}
3666--TEMPLATE(forms.twig)--
3667{% macro foo(name) %}foo{{ name }}{% endmacro %}
3668{% macro bar(name) %}bar{{ name }}{% endmacro %}
3669--DATA--
3670return array()
3671--EXPECT--
3672foofoo
3673foofoo
3674barfoo
3675--TEST--
3676"macro" tag
3677--TEMPLATE--
3678{% from 'forms.twig' import foo %}
3679
3680{{ foo('foo') }}
3681{{ foo() }}
3682--TEMPLATE(forms.twig)--
3683{% macro foo(name) %}{{ name|default('foo') }}{{ global }}{% endmacro %}
3684--DATA--
3685return array()
3686--EXPECT--
3687fooglobal
3688fooglobal
3689--TEST--
3690"macro" tag
3691--TEMPLATE--
3692{% import _self as forms %}
3693
3694{{ forms.input('username') }}
3695{{ forms.input('password', null, 'password', 1) }}
3696
3697{% macro input(name, value, type, size) %}
3698  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
3699{% endmacro %}
3700--DATA--
3701return array()
3702--EXPECT--
3703  <input type="text" name="username" value="" size="20">
3704
3705  <input type="password" name="password" value="" size="1">
3706--TEST--
3707"raw" tag
3708--TEMPLATE--
3709{% raw %}
3710{{ foo }}
3711{% endraw %}
3712--DATA--
3713return array()
3714--EXPECT--
3715{{ foo }}
3716--TEST--
3717"raw" tag
3718--TEMPLATE--
3719{% raw %}
3720{{ foo }}
3721{% endverbatim %}
3722--DATA--
3723return array()
3724--EXCEPTION--
3725Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2
3726--TEST--
3727"raw" tag
3728--TEMPLATE--
37291***
3730
3731{%- raw %}
3732    {{ 'bla' }}
3733{% endraw %}
3734
37351***
37362***
3737
3738{%- raw -%}
3739    {{ 'bla' }}
3740{% endraw %}
3741
37422***
37433***
3744
3745{%- raw -%}
3746    {{ 'bla' }}
3747{% endraw -%}
3748
37493***
37504***
3751
3752{%- raw -%}
3753    {{ 'bla' }}
3754{%- endraw %}
3755
37564***
37575***
3758
3759{%- raw -%}
3760    {{ 'bla' }}
3761{%- endraw -%}
3762
37635***
3764--DATA--
3765return array()
3766--EXPECT--
37671***
3768    {{ 'bla' }}
3769
3770
37711***
37722***{{ 'bla' }}
3773
3774
37752***
37763***{{ 'bla' }}
37773***
37784***{{ 'bla' }}
3779
37804***
37815***{{ 'bla' }}5***
3782--TEST--
3783sandbox tag
3784--TEMPLATE--
3785{%- sandbox %}
3786    {%- include "foo.twig" %}
3787    a
3788{%- endsandbox %}
3789--TEMPLATE(foo.twig)--
3790foo
3791--EXCEPTION--
3792Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4
3793--TEST--
3794sandbox tag
3795--TEMPLATE--
3796{%- sandbox %}
3797    {%- include "foo.twig" %}
3798
3799    {% if 1 %}
3800        {%- include "foo.twig" %}
3801    {% endif %}
3802{%- endsandbox %}
3803--TEMPLATE(foo.twig)--
3804foo
3805--EXCEPTION--
3806Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5
3807--TEST--
3808sandbox tag
3809--TEMPLATE--
3810{%- sandbox %}
3811    {%- include "foo.twig" %}
3812{%- endsandbox %}
3813
3814{%- sandbox %}
3815    {%- include "foo.twig" %}
3816    {%- include "foo.twig" %}
3817{%- endsandbox %}
3818
3819{%- sandbox %}{% include "foo.twig" %}{% endsandbox %}
3820--TEMPLATE(foo.twig)--
3821foo
3822--DATA--
3823return array()
3824--EXPECT--
3825foo
3826foo
3827foo
3828foo
3829--TEST--
3830"set" tag
3831--TEMPLATE--
3832{% set foo = 'foo' %}
3833{% set bar = 'foo<br />' %}
3834
3835{{ foo }}
3836{{ bar }}
3837
3838{% set foo, bar = 'foo', 'bar' %}
3839
3840{{ foo }}{{ bar }}
3841--DATA--
3842return array()
3843--EXPECT--
3844foo
3845foo&lt;br /&gt;
3846
3847
3848foobar
3849--TEST--
3850"set" tag block empty capture
3851--TEMPLATE--
3852{% set foo %}{% endset %}
3853
3854{% if foo %}FAIL{% endif %}
3855--DATA--
3856return array()
3857--EXPECT--
3858--TEST--
3859"set" tag block capture
3860--TEMPLATE--
3861{% set foo %}f<br />o<br />o{% endset %}
3862
3863{{ foo }}
3864--DATA--
3865return array()
3866--EXPECT--
3867f<br />o<br />o
3868--TEST--
3869"set" tag
3870--TEMPLATE--
3871{% set foo, bar = 'foo' ~ 'bar', 'bar' ~ 'foo' %}
3872
3873{{ foo }}
3874{{ bar }}
3875--DATA--
3876return array()
3877--EXPECT--
3878foobar
3879barfoo
3880--TEST--
3881"spaceless" tag removes whites between HTML tags
3882--TEMPLATE--
3883{% spaceless %}
3884
3885    <div>   <div>   foo   </div>   </div>
3886
3887{% endspaceless %}
3888--DATA--
3889return array()
3890--EXPECT--
3891<div><div>   foo   </div></div>
3892--TEST--
3893"§" custom tag
3894--TEMPLATE--
3895{% § %}
3896--DATA--
3897return array()
3898--EXPECT--
3899§
3900--TEST--
3901Whitespace trimming on tags.
3902--TEMPLATE--
3903{{ 5 * '{#-'|length }}
3904{{ '{{-'|length * 5 + '{%-'|length }}
3905
3906Trim on control tag:
3907{% for i in range(1, 9) -%}
3908	{{ i }}
3909{%- endfor %}
3910
3911
3912Trim on output tag:
3913{% for i in range(1, 9) %}
3914	{{- i -}}
3915{% endfor %}
3916
3917
3918Trim comments:
3919
3920{#- Invisible -#}
3921
3922After the comment.
3923
3924Trim leading space:
3925{% if leading %}
3926
3927		{{- leading }}
3928{% endif %}
3929
3930{%- if leading %}
3931	{{- leading }}
3932
3933{%- endif %}
3934
3935
3936Trim trailing space:
3937{% if trailing -%}
3938	{{ trailing -}}
3939
3940{% endif -%}
3941
3942Combined:
3943
3944{%- if both -%}
3945<ul>
3946	<li>    {{- both -}}   </li>
3947</ul>
3948
3949{%- endif -%}
3950
3951end
3952--DATA--
3953return array('leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both')
3954--EXPECT--
395515
395618
3957
3958Trim on control tag:
3959123456789
3960
3961Trim on output tag:
3962123456789
3963
3964Trim comments:After the comment.
3965
3966Trim leading space:
3967leading space
3968leading space
3969
3970Trim trailing space:
3971trailing spaceCombined:<ul>
3972	<li>both</li>
3973</ul>end
3974--TEST--
3975"use" tag
3976--TEMPLATE--
3977{% use "blocks.twig" with content as foo %}
3978
3979{{ block('foo') }}
3980--TEMPLATE(blocks.twig)--
3981{% block content 'foo' %}
3982--DATA--
3983return array()
3984--EXPECT--
3985foo
3986--TEST--
3987"use" tag
3988--TEMPLATE--
3989{% use "blocks.twig" %}
3990
3991{{ block('content') }}
3992--TEMPLATE(blocks.twig)--
3993{% block content 'foo' %}
3994--DATA--
3995return array()
3996--EXPECT--
3997foo
3998--TEST--
3999"use" tag
4000--TEMPLATE--
4001{% use "foo.twig" %}
4002--TEMPLATE(foo.twig)--
4003{% use "bar.twig" %}
4004--TEMPLATE(bar.twig)--
4005--DATA--
4006return array()
4007--EXPECT--
4008--TEST--
4009"use" tag
4010--TEMPLATE--
4011{% use "foo.twig" %}
4012
4013{{ block('content') }}
4014{{ block('foo') }}
4015{{ block('bar') }}
4016--TEMPLATE(foo.twig)--
4017{% use "bar.twig" %}
4018
4019{% block content 'foo' %}
4020{% block foo 'foo' %}
4021--TEMPLATE(bar.twig)--
4022{% block content 'bar' %}
4023{% block bar 'bar' %}
4024--DATA--
4025return array()
4026--EXPECT--
4027foo
4028foo
4029bar
4030--TEST--
4031"use" tag
4032--TEMPLATE--
4033{% use "ancestor.twig" %}
4034{% use "parent.twig" %}
4035
4036{{ block('container') }}
4037--TEMPLATE(parent.twig)--
4038{% block sub_container %}
4039    <div class="overriden_sub_container">overriden sub_container</div>
4040{% endblock %}
4041--TEMPLATE(ancestor.twig)--
4042{% block container %}
4043    <div class="container">{{ block('sub_container') }}</div>
4044{% endblock %}
4045
4046{% block sub_container %}
4047    <div class="sub_container">sub_container</div>
4048{% endblock %}
4049--DATA--
4050return array()
4051--EXPECT--
4052<div class="container">    <div class="overriden_sub_container">overriden sub_container</div>
4053</div>
4054--TEST--
4055"use" tag
4056--TEMPLATE--
4057{% use "parent.twig" %}
4058
4059{{ block('container') }}
4060--TEMPLATE(parent.twig)--
4061{% use "ancestor.twig" %}
4062
4063{% block sub_container %}
4064    <div class="overriden_sub_container">overriden sub_container</div>
4065{% endblock %}
4066--TEMPLATE(ancestor.twig)--
4067{% block container %}
4068    <div class="container">{{ block('sub_container') }}</div>
4069{% endblock %}
4070
4071{% block sub_container %}
4072    <div class="sub_container">sub_container</div>
4073{% endblock %}
4074--DATA--
4075return array()
4076--EXPECT--
4077<div class="container">    <div class="overriden_sub_container">overriden sub_container</div>
4078</div>
4079--TEST--
4080"use" tag
4081--TEMPLATE--
4082{% use "foo.twig" with content as foo_content %}
4083{% use "bar.twig" %}
4084
4085{{ block('content') }}
4086{{ block('foo') }}
4087{{ block('bar') }}
4088{{ block('foo_content') }}
4089--TEMPLATE(foo.twig)--
4090{% block content 'foo' %}
4091{% block foo 'foo' %}
4092--TEMPLATE(bar.twig)--
4093{% block content 'bar' %}
4094{% block bar 'bar' %}
4095--DATA--
4096return array()
4097--EXPECT--
4098bar
4099foo
4100bar
4101foo
4102--TEST--
4103"use" tag
4104--TEMPLATE--
4105{% use "foo.twig" %}
4106{% use "bar.twig" %}
4107
4108{{ block('content') }}
4109{{ block('foo') }}
4110{{ block('bar') }}
4111--TEMPLATE(foo.twig)--
4112{% block content 'foo' %}
4113{% block foo 'foo' %}
4114--TEMPLATE(bar.twig)--
4115{% block content 'bar' %}
4116{% block bar 'bar' %}
4117--DATA--
4118return array()
4119--EXPECT--
4120bar
4121foo
4122bar
4123--TEST--
4124"use" tag
4125--TEMPLATE--
4126{% use 'file2.html.twig'%}
4127{% block foobar %}
4128    {{- parent() -}}
4129    Content of block (second override)
4130{% endblock foobar %}
4131--TEMPLATE(file2.html.twig)--
4132{% use 'file1.html.twig' %}
4133{% block foobar %}
4134    {{- parent() -}}
4135    Content of block (first override)
4136{% endblock foobar %}
4137--TEMPLATE(file1.html.twig)--
4138{% block foobar -%}
4139    Content of block
4140{% endblock foobar %}
4141--DATA--
4142return array()
4143--EXPECT--
4144Content of block
4145Content of block (first override)
4146Content of block (second override)
4147--TEST--
4148"use" tag
4149--TEMPLATE--
4150{% use 'file2.html.twig' %}
4151{% use 'file1.html.twig' with foo %}
4152{% block foo %}
4153    {{- parent() -}}
4154    Content of foo (second override)
4155{% endblock foo %}
4156{% block bar %}
4157    {{- parent() -}}
4158    Content of bar (second override)
4159{% endblock bar %}
4160--TEMPLATE(file2.html.twig)--
4161{% use 'file1.html.twig' %}
4162{% block foo %}
4163    {{- parent() -}}
4164    Content of foo (first override)
4165{% endblock foo %}
4166{% block bar %}
4167    {{- parent() -}}
4168    Content of bar (first override)
4169{% endblock bar %}
4170--TEMPLATE(file1.html.twig)--
4171{% block foo -%}
4172    Content of foo
4173{% endblock foo %}
4174{% block bar -%}
4175    Content of bar
4176{% endblock bar %}
4177--DATA--
4178return array()
4179--EXPECT--
4180Content of foo
4181Content of foo (first override)
4182Content of foo (second override)
4183Content of bar
4184Content of bar (second override)
4185--TEST--
4186"use" tag
4187--TEMPLATE--
4188{% use 'file2.html.twig' with foobar as base_base_foobar %}
4189{% block foobar %}
4190    {{- block('base_base_foobar') -}}
4191    Content of block (second override)
4192{% endblock foobar %}
4193--TEMPLATE(file2.html.twig)--
4194{% use 'file1.html.twig' with foobar as base_foobar %}
4195{% block foobar %}
4196    {{- block('base_foobar') -}}
4197    Content of block (first override)
4198{% endblock foobar %}
4199--TEMPLATE(file1.html.twig)--
4200{% block foobar -%}
4201    Content of block
4202{% endblock foobar %}
4203--DATA--
4204return array()
4205--EXPECT--
4206Content of block
4207Content of block (first override)
4208Content of block (second override)
4209--TEST--
4210"verbatim" tag
4211--TEMPLATE--
4212{% verbatim %}
4213{{ foo }}
4214{% endverbatim %}
4215--DATA--
4216return array()
4217--EXPECT--
4218{{ foo }}
4219--TEST--
4220"verbatim" tag
4221--TEMPLATE--
4222{% verbatim %}
4223{{ foo }}
4224{% endraw %}
4225--DATA--
4226return array()
4227--EXCEPTION--
4228Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2
4229--TEST--
4230"verbatim" tag
4231--TEMPLATE--
42321***
4233
4234{%- verbatim %}
4235    {{ 'bla' }}
4236{% endverbatim %}
4237
42381***
42392***
4240
4241{%- verbatim -%}
4242    {{ 'bla' }}
4243{% endverbatim %}
4244
42452***
42463***
4247
4248{%- verbatim -%}
4249    {{ 'bla' }}
4250{% endverbatim -%}
4251
42523***
42534***
4254
4255{%- verbatim -%}
4256    {{ 'bla' }}
4257{%- endverbatim %}
4258
42594***
42605***
4261
4262{%- verbatim -%}
4263    {{ 'bla' }}
4264{%- endverbatim -%}
4265
42665***
4267--DATA--
4268return array()
4269--EXPECT--
42701***
4271    {{ 'bla' }}
4272
4273
42741***
42752***{{ 'bla' }}
4276
4277
42782***
42793***{{ 'bla' }}
42803***
42814***{{ 'bla' }}
4282
42834***
42845***{{ 'bla' }}5***
4285--TEST--
4286array index test
4287--TEMPLATE--
4288{% for key, value in days %}
4289{{ key }}
4290{% endfor %}
4291--DATA--
4292return array('days' => array(
4293    1  => array('money' => 9),
4294    2  => array('money' => 21),
4295    3  => array('money' => 38),
4296    4  => array('money' => 6),
4297    18 => array('money' => 6),
4298    19 => array('money' => 3),
4299    31 => array('money' => 11),
4300));
4301--EXPECT--
43021
43032
43043
43054
430618
430719
430831
4309--TEST--
4310"const" test
4311--TEMPLATE--
4312{{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }}
4313{{ 'bar' is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
4314{{ value is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
4315{{ 2 is constant('ARRAY_AS_PROPS', object) ? 'ok' : 'no' }}
4316--DATA--
4317return array('value' => 'bar', 'object' => new ArrayObject(array('hi')));
4318--EXPECT--
4319ok
4320ok
4321ok
4322ok--TEST--
4323"defined" test
4324--TEMPLATE--
4325{{ definedVar                     is     defined ? 'ok' : 'ko' }}
4326{{ definedVar                     is not defined ? 'ko' : 'ok' }}
4327{{ undefinedVar                   is     defined ? 'ko' : 'ok' }}
4328{{ undefinedVar                   is not defined ? 'ok' : 'ko' }}
4329{{ zeroVar                        is     defined ? 'ok' : 'ko' }}
4330{{ nullVar                        is     defined ? 'ok' : 'ko' }}
4331{{ nested.definedVar              is     defined ? 'ok' : 'ko' }}
4332{{ nested['definedVar']           is     defined ? 'ok' : 'ko' }}
4333{{ nested.definedVar              is not defined ? 'ko' : 'ok' }}
4334{{ nested.undefinedVar            is     defined ? 'ko' : 'ok' }}
4335{{ nested['undefinedVar']         is     defined ? 'ko' : 'ok' }}
4336{{ nested.undefinedVar            is not defined ? 'ok' : 'ko' }}
4337{{ nested.zeroVar                 is     defined ? 'ok' : 'ko' }}
4338{{ nested.nullVar                 is     defined ? 'ok' : 'ko' }}
4339{{ nested.definedArray.0          is     defined ? 'ok' : 'ko' }}
4340{{ nested['definedArray'][0]      is     defined ? 'ok' : 'ko' }}
4341{{ object.foo                     is     defined ? 'ok' : 'ko' }}
4342{{ object.undefinedMethod         is     defined ? 'ko' : 'ok' }}
4343{{ object.getFoo()                is     defined ? 'ok' : 'ko' }}
4344{{ object.getFoo('a')             is     defined ? 'ok' : 'ko' }}
4345{{ object.undefinedMethod()       is     defined ? 'ko' : 'ok' }}
4346{{ object.undefinedMethod('a')    is     defined ? 'ko' : 'ok' }}
4347{{ object.self.foo                is     defined ? 'ok' : 'ko' }}
4348{{ object.self.undefinedMethod    is     defined ? 'ko' : 'ok' }}
4349{{ object.undefinedMethod.self    is     defined ? 'ko' : 'ok' }}
4350--DATA--
4351return array(
4352    'definedVar' => 'defined',
4353    'zeroVar'    => 0,
4354    'nullVar'    => null,
4355    'nested'      => array(
4356        'definedVar'   => 'defined',
4357        'zeroVar'      => 0,
4358        'nullVar'      => null,
4359        'definedArray' => array(0),
4360    ),
4361    'object' => new TwigTestFoo(),
4362);
4363--EXPECT--
4364ok
4365ok
4366ok
4367ok
4368ok
4369ok
4370ok
4371ok
4372ok
4373ok
4374ok
4375ok
4376ok
4377ok
4378ok
4379ok
4380ok
4381ok
4382ok
4383ok
4384ok
4385ok
4386ok
4387ok
4388ok
4389--DATA--
4390return array(
4391    'definedVar' => 'defined',
4392    'zeroVar'    => 0,
4393    'nullVar'    => null,
4394    'nested'      => array(
4395        'definedVar'   => 'defined',
4396        'zeroVar'      => 0,
4397        'nullVar'      => null,
4398        'definedArray' => array(0),
4399    ),
4400    'object' => new TwigTestFoo(),
4401);
4402--CONFIG--
4403return array('strict_variables' => false)
4404--EXPECT--
4405ok
4406ok
4407ok
4408ok
4409ok
4410ok
4411ok
4412ok
4413ok
4414ok
4415ok
4416ok
4417ok
4418ok
4419ok
4420ok
4421ok
4422ok
4423ok
4424ok
4425ok
4426ok
4427ok
4428ok
4429ok
4430--TEST--
4431"empty" test
4432--TEMPLATE--
4433{{ foo is empty ? 'ok' : 'ko' }}
4434{{ bar is empty ? 'ok' : 'ko' }}
4435{{ foobar is empty ? 'ok' : 'ko' }}
4436{{ array is empty ? 'ok' : 'ko' }}
4437{{ zero is empty ? 'ok' : 'ko' }}
4438{{ string is empty ? 'ok' : 'ko' }}
4439{{ countable_empty is empty ? 'ok' : 'ko' }}
4440{{ countable_not_empty is empty ? 'ok' : 'ko' }}
4441{{ markup_empty is empty ? 'ok' : 'ko' }}
4442{{ markup_not_empty is empty ? 'ok' : 'ko' }}
4443--DATA--
4444
4445class CountableStub implements Countable
4446{
4447    private $items;
4448
4449    public function __construct(array $items)
4450    {
4451        $this->items = $items;
4452    }
4453
4454    public function count()
4455    {
4456        return count($this->items);
4457    }
4458}
4459return array(
4460    'foo' => '', 'bar' => null, 'foobar' => false, 'array' => array(), 'zero' => 0, 'string' => '0',
4461    'countable_empty' => new CountableStub(array()), 'countable_not_empty' => new CountableStub(array(1, 2)),
4462    'markup_empty' => new Twig_Markup('', 'UTF-8'), 'markup_not_empty' => new Twig_Markup('test', 'UTF-8'),
4463);
4464--EXPECT--
4465ok
4466ok
4467ok
4468ok
4469ko
4470ko
4471ok
4472ko
4473ok
4474ko
4475--TEST--
4476"even" test
4477--TEMPLATE--
4478{{ 1 is even ? 'ko' : 'ok' }}
4479{{ 2 is even ? 'ok' : 'ko' }}
4480{{ 1 is not even ? 'ok' : 'ko' }}
4481{{ 2 is not even ? 'ko' : 'ok' }}
4482--DATA--
4483return array()
4484--EXPECT--
4485ok
4486ok
4487ok
4488ok
4489--TEST--
4490Twig supports the in operator
4491--TEMPLATE--
4492{% if bar in foo %}
4493TRUE
4494{% endif %}
4495{% if not (bar in foo) %}
4496{% else %}
4497TRUE
4498{% endif %}
4499{% if bar not in foo %}
4500{% else %}
4501TRUE
4502{% endif %}
4503{% if 'a' in bar %}
4504TRUE
4505{% endif %}
4506{% if 'c' not in bar %}
4507TRUE
4508{% endif %}
4509{% if '' not in bar %}
4510TRUE
4511{% endif %}
4512{% if '' in '' %}
4513TRUE
4514{% endif %}
4515{% if '0' not in '' %}
4516TRUE
4517{% endif %}
4518{% if 'a' not in '0' %}
4519TRUE
4520{% endif %}
4521{% if '0' in '0' %}
4522TRUE
4523{% endif %}
4524{{ false in [0, 1] ? 'TRUE' : 'FALSE' }}
4525{{ true in [0, 1] ? 'TRUE' : 'FALSE' }}
4526{{ '0' in [0, 1] ? 'TRUE' : 'FALSE' }}
4527{{ '' in [0, 1] ? 'TRUE' : 'FALSE' }}
4528{{ 0 in ['', 1] ? 'TRUE' : 'FALSE' }}
4529{{ '' in 'foo' ? 'TRUE' : 'FALSE' }}
4530{{ 0 in 'foo' ? 'TRUE' : 'FALSE' }}
4531{{ false in 'foo' ? 'TRUE' : 'FALSE' }}
4532{{ true in '100' ? 'TRUE' : 'FALSE' }}
4533{{ [] in 'Array' ? 'TRUE' : 'FALSE' }}
4534{{ [] in [true, false] ? 'TRUE' : 'FALSE' }}
4535{{ [] in [true, ''] ? 'TRUE' : 'FALSE' }}
4536{{ [] in [true, []] ? 'TRUE' : 'FALSE' }}
4537{{ dir_object in 'foo'~dir_name ? 'TRUE' : 'FALSE' }}
4538{{ 5 in 125 ? 'TRUE' : 'FALSE' }}
4539--DATA--
4540return array('bar' => 'bar', 'foo' => array('bar' => 'bar'), 'dir_name' => dirname(__FILE__), 'dir_object' => new SplFileInfo(dirname(__FILE__)))
4541--EXPECT--
4542TRUE
4543TRUE
4544TRUE
4545TRUE
4546TRUE
4547TRUE
4548TRUE
4549TRUE
4550TRUE
4551FALSE
4552FALSE
4553FALSE
4554FALSE
4555FALSE
4556TRUE
4557FALSE
4558FALSE
4559FALSE
4560FALSE
4561FALSE
4562FALSE
4563TRUE
4564FALSE
4565FALSE
4566--TEST--
4567Twig supports the in operator when using objects
4568--TEMPLATE--
4569{% if object in object_list %}
4570TRUE
4571{% endif %}
4572--DATA--
4573$foo = new TwigTestFoo();
4574$foo1 = new TwigTestFoo();
4575
4576$foo->position = $foo1;
4577$foo1->position = $foo;
4578
4579return array(
4580    'object'      => $foo,
4581    'object_list' => array($foo1, $foo),
4582);
4583--EXPECT--
4584TRUE
4585--TEST--
4586"iterable" test
4587--TEMPLATE--
4588{{ foo is iterable ? 'ok' : 'ko' }}
4589{{ traversable is iterable ? 'ok' : 'ko' }}
4590{{ obj is iterable ? 'ok' : 'ko' }}
4591{{ val is iterable ? 'ok' : 'ko' }}
4592--DATA--
4593return array(
4594    'foo' => array(),
4595    'traversable' => new ArrayIterator(array()),
4596    'obj' => new stdClass(),
4597    'val' => 'test',
4598);
4599--EXPECT--
4600ok
4601ok
4602ko
4603ko--TEST--
4604"odd" test
4605--TEMPLATE--
4606{{ 1 is odd ? 'ok' : 'ko' }}
4607{{ 2 is odd ? 'ko' : 'ok' }}
4608--DATA--
4609return array()
4610--EXPECT--
4611ok
4612ok
4613