From the Twig test suite, https://github.com/fabpot/Twig, available under BSD license.

--TEST--
Exception for an unclosed tag
--TEMPLATE--
{% block foo %}
     {% if foo %}




         {% for i in fo %}



         {% endfor %}



{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Unexpected tag name "endblock" (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16
--TEST--
Exception for an undefined trait
--TEMPLATE--
{% use 'foo' with foobar as bar %}
--TEMPLATE(foo)--
{% block bar %}
{% endblock %}
--EXCEPTION--
Twig_Error_Runtime: Block "foobar" is not defined in trait "foo" in "index.twig".
--TEST--
Twig supports method calls
--TEMPLATE--
{{ items.foo }}
{{ items['foo'] }}
{{ items[foo] }}
{{ items[items[foo]] }}
--DATA--
return array('foo' => 'bar', 'items' => array('foo' => 'bar', 'bar' => 'foo'))
--EXPECT--
bar
bar
foo
bar
--TEST--
Twig supports array notation
--TEMPLATE--
{# empty array #}
{{ []|join(',') }}

{{ [1, 2]|join(',') }}
{{ ['foo', "bar"]|join(',') }}
{{ {0: 1, 'foo': 'bar'}|join(',') }}
{{ {0: 1, 'foo': 'bar'}|keys|join(',') }}

{{ {0: 1, foo: 'bar'}|join(',') }}
{{ {0: 1, foo: 'bar'}|keys|join(',') }}

{# nested arrays #}
{% set a = [1, 2, [1, 2], {'foo': {'foo': 'bar'}}] %}
{{ a[2]|join(',') }}
{{ a[3]["foo"]|join(',') }}

{# works even if [] is used inside the array #}
{{ [foo[bar]]|join(',') }}

{# elements can be any expression #}
{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }}

{# arrays can have a trailing , like in PHP #}
{{
  [
    1,
    2,
  ]|join(',')
}}

{# keys can be any expression #}
{% set a = 1 %}
{% set b = "foo" %}
{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %}
{{ ary|keys|join(',') }}
{{ ary|join(',') }}
--DATA--
return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
--EXPECT--
1,2
foo,bar
1,bar
0,foo

1,bar
0,foo

1,2
bar

bar

FOO,BAR,

1,2

1,foo,c,1foo
a,b,c,d
--TEST--
Twig supports binary operations (+, -, *, /, ~, %, and, or)
--TEMPLATE--
{{ 1 + 1 }}
{{ 2 - 1 }}
{{ 2 * 2 }}
{{ 2 / 2 }}
{{ 3 % 2 }}
{{ 1 and 1 }}
{{ 1 and 0 }}
{{ 0 and 1 }}
{{ 0 and 0 }}
{{ 1 or 1 }}
{{ 1 or 0 }}
{{ 0 or 1 }}
{{ 0 or 0 }}
{{ 0 or 1 and 0 }}
{{ 1 or 0 and 1 }}
{{ "foo" ~ "bar" }}
{{ foo ~ "bar" }}
{{ "foo" ~ bar }}
{{ foo ~ bar }}
{{ 20 // 7 }}
--DATA--
return array('foo' => 'bar', 'bar' => 'foo')
--EXPECT--
2
1
4
1
1
1



1
1
1


1
foobar
barbar
foofoo
barfoo
2
--TEST--
Twig supports bitwise operations
--TEMPLATE--
{{ 1 b-and 5 }}
{{ 1 b-or 5 }}
{{ 1 b-xor 5 }}
{{ (1 and 0 b-or 0) is same as(1 and (0 b-or 0)) ? 'ok' : 'ko' }}
--DATA--
return array()
--EXPECT--
1
5
4
ok
--TEST--
Twig supports comparison operators (==, !=, <, >, >=, <=)
--TEMPLATE--
{{ 1 > 2 }}/{{ 1 > 1 }}/{{ 1 >= 2 }}/{{ 1 >= 1 }}
{{ 1 < 2 }}/{{ 1 < 1 }}/{{ 1 <= 2 }}/{{ 1 <= 1 }}
{{ 1 == 1 }}/{{ 1 == 2 }}
{{ 1 != 1 }}/{{ 1 != 2 }}
--DATA--
return array()
--EXPECT--
///1
1//1/1
1/
/1
--TEST--
Twig supports the "divisible by" operator
--TEMPLATE--
{{ 8 is divisible by(2) ? 'OK' }}
{{ 8 is not divisible by(3) ? 'OK' }}
{{ 8 is    divisible   by   (2) ? 'OK' }}
{{ 8 is not
   divisible
   by
   (3) ? 'OK' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
--TEST--
Twig supports the .. operator
--TEMPLATE--
{% for i in 0..10 %}{{ i }} {% endfor %}

{% for letter in 'a'..'z' %}{{ letter }} {% endfor %}

{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %}

{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %}

{% for i in 0 + 1 .. 10 - 1 %}{{ i }} {% endfor %}
--DATA--
return array('foo' => array(1, 10))
--EXPECT--
0 1 2 3 4 5 6 7 8 9 10 
a 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 
A 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 
1 2 3 4 5 6 7 8 9 10 
1 2 3 4 5 6 7 8 9
--TEST--
Twig supports the "ends with" operator
--TEMPLATE--
{{ 'foo' ends with 'o' ? 'OK' : 'KO' }}
{{ not ('foo' ends with 'f') ? 'OK' : 'KO' }}
{{ not ('foo' ends with 'foowaytoolong') ? 'OK' : 'KO' }}
{{ 'foo' ends with '' ? 'OK' : 'KO' }}
{{ '1' ends with true ? 'OK' : 'KO' }}
{{ 1 ends with true ? 'OK' : 'KO' }}
{{ 0 ends with false ? 'OK' : 'KO' }}
{{ '' ends with false ? 'OK' : 'KO' }}
{{ false ends with false ? 'OK' : 'KO' }}
{{ false ends with '' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
KO
KO
KO
KO
KO
KO
--TEST--
Twig supports grouping of expressions
--TEMPLATE--
{{ (2 + 2) / 2 }}
--DATA--
return array()
--EXPECT--
2
--TEST--
Twig supports literals
--TEMPLATE--
1 {{ true }}
2 {{ TRUE }}
3 {{ false }}
4 {{ FALSE }}
5 {{ none }}
6 {{ NONE }}
7 {{ null }}
8 {{ NULL }}
--DATA--
return array()
--EXPECT--
1 1
2 1
3 
4 
5 
6 
7 
8 
--TEST--
Twig supports __call() for attributes
--TEMPLATE--
{{ foo.foo }}
{{ foo.bar }}
--EXPECT--
foo_from_call
bar_from_getbar
--TEST--
Twig supports the "matches" operator
--TEMPLATE--
{{ 'foo' matches '/o/' ? 'OK' : 'KO' }}
{{ 'foo' matches '/^fo/' ? 'OK' : 'KO' }}
{{ 'foo' matches '/O/i' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
--TEST--
Twig supports method calls
--TEMPLATE--
{{ items.foo.foo }}
{{ items.foo.getFoo() }}
{{ items.foo.bar }}
{{ items.foo['bar'] }}
{{ items.foo.bar('a', 43) }}
{{ items.foo.bar(foo) }}
{{ items.foo.self.foo() }}
{{ items.foo.is }}
{{ items.foo.in }}
{{ items.foo.not }}
--DATA--
return array('foo' => 'bar', 'items' => array('foo' => new TwigTestFoo(), 'bar' => 'foo'))
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
foo
foo
bar

bar_a-43
bar_bar
foo
is
in
not
--TEST--
Twig allows to use named operators as variable names
--TEMPLATE--
{% for match in matches %}
    {{- match }}
{% endfor %}
{{ in }}
{{ is }}
--DATA--
return array('matches' => array(1, 2, 3), 'in' => 'in', 'is' => 'is')
--EXPECT--
1
2
3
in
is
--TEST--
Twig parses postfix expressions
--TEMPLATE--
{% import _self as macros %}

{% macro foo() %}foo{% endmacro %}

{{ 'a' }}
{{ 'a'|upper }}
{{ ('a')|upper }}
{{ -1|upper }}
{{ macros.foo() }}
{{ (macros).foo() }}
--DATA--
return array();
--EXPECT--
a
A
A
-1
foo
foo
--TEST--
Twig supports the "same as" operator
--TEMPLATE--
{{ 1 is same as(1) ? 'OK' }}
{{ 1 is not same as(true) ? 'OK' }}
{{ 1 is same as(1) ? 'OK' }}
{{ 1 is not same as(true) ? 'OK' }}
{{ 1 is   same    as   (1) ? 'OK' }}
{{ 1 is not
    same
    as
    (true) ? 'OK' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
OK
OK
--TEST--
Twig supports the "starts with" operator
--TEMPLATE--
{{ 'foo' starts with 'f' ? 'OK' : 'KO' }}
{{ not ('foo' starts with 'oo') ? 'OK' : 'KO' }}
{{ not ('foo' starts with 'foowaytoolong') ? 'OK' : 'KO' }}
{{ 'foo' starts      with 'f' ? 'OK' : 'KO' }}
{{ 'foo' starts
with 'f' ? 'OK' : 'KO' }}
{{ 'foo' starts with '' ? 'OK' : 'KO' }}
{{ '1' starts with true ? 'OK' : 'KO' }}
{{ '' starts with false ? 'OK' : 'KO' }}
{{ 'a' starts with false ? 'OK' : 'KO' }}
{{ false starts with '' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
OK
OK
KO
KO
KO
KO
--TEST--
Twig supports string interpolation
--TEMPLATE--
{# "foo #{"foo #{bar} baz"} baz" #}
{# "foo #{bar}#{bar} baz" #}
--DATA--
return array('bar' => 'BAR');
--EXPECT--
foo foo BAR baz baz
foo BARBAR baz
--TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 1 ? 'YES' }}
{{ 0 ? 'YES' }}
--DATA--
return array()
--EXPECT--
YES

--TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 'YES' ?: 'NO' }}
{{ 0 ?: 'NO' }}
--DATA--
return array()
--EXPECT--
YES
NO
--TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 1 ? 'YES' : 'NO' }}
{{ 0 ? 'YES' : 'NO' }}
{{ 0 ? 'YES' : (1 ? 'YES1' : 'NO1') }}
{{ 0 ? 'YES' : (0 ? 'YES1' : 'NO1') }}
{{ 1 == 1 ? 'foo<br />':'' }}
{{ foo ~ (bar ? ('-' ~ bar) : '') }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--EXPECT--
YES
NO
YES1
NO1
foo<br />
foo-bar
--TEST--
Twig does not allow to use two-word named operators as variable names
--TEMPLATE--
{{ starts with }}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected token "operator" of value "starts with" in "index.twig" at line 2
--TEST--
Twig unary operators precedence
--TEMPLATE--
{{ -1 - 1 }}
{{ -1 - -1 }}
{{ -1 * -1 }}
{{ 4 / -1 * 5 }}
--DATA--
return array()
--EXPECT--
-2
0
1
-20
--TEST--
Twig supports unary operators (not, -, +)
--TEMPLATE--
{{ not 1 }}/{{ not 0 }}
{{ +1 + 1 }}/{{ -1 - 1 }}
{{ not (false or true) }}
--DATA--
return array()
--EXPECT--
/1
2/-2

--TEST--
"abs" filter
--TEMPLATE--
{{ (-5.5)|abs }}
{{ (-5)|abs }}
{{ (-0)|abs }}
{{ 0|abs }}
{{ 5|abs }}
{{ 5.5|abs }}
{{ number1|abs }}
{{ number2|abs }}
{{ number3|abs }}
{{ number4|abs }}
{{ number5|abs }}
{{ number6|abs }}
--DATA--
return array('number1' => -5.5, 'number2' => -5, 'number3' => -0, 'number4' => 0, 'number5' => 5, 'number6' => 5.5)
--EXPECT--
5.5
5
0
0
5
5.5
5.5
5
0
0
5
5.5
--TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3.1) %}
  <div class=row>
  {% for column in row %}
    <div class=item>{{ column }}</div>
  {% endfor %}
  </div>
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
<div class=row>
      <div class=item>a</div>
      <div class=item>b</div>
      <div class=item>c</div>
      <div class=item>d</div>
    </div>
  <div class=row>
      <div class=item>e</div>
      <div class=item>f</div>
      <div class=item>g</div>
      <div class=item>h</div>
    </div>
  <div class=row>
      <div class=item>i</div>
      <div class=item>j</div>
    </div>
--TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3) %}
  <div class=row>
  {% for column in row %}
    <div class=item>{{ column }}</div>
  {% endfor %}
  </div>
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
<div class=row>
      <div class=item>a</div>
      <div class=item>b</div>
      <div class=item>c</div>
    </div>
  <div class=row>
      <div class=item>d</div>
      <div class=item>e</div>
      <div class=item>f</div>
    </div>
  <div class=row>
      <div class=item>g</div>
      <div class=item>h</div>
      <div class=item>i</div>
    </div>
  <div class=row>
      <div class=item>j</div>
    </div>
--TEST--
"batch" filter
--TEMPLATE--
<table>
{% for row in items|batch(3, '') %}
  <tr>
  {% for column in row %}
    <td>{{ column }}</td>
  {% endfor %}
  </tr>
{% endfor %}
</table>
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
<table>
  <tr>
      <td>a</td>
      <td>b</td>
      <td>c</td>
    </tr>
  <tr>
      <td>d</td>
      <td>e</td>
      <td>f</td>
    </tr>
  <tr>
      <td>g</td>
      <td>h</td>
      <td>i</td>
    </tr>
  <tr>
      <td>j</td>
      <td></td>
      <td></td>
    </tr>
</table>
--TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3, 'fill') %}
  <div class=row>
  {% for column in row %}
    <div class=item>{{ column }}</div>
  {% endfor %}
  </div>
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'))
--EXPECT--
<div class=row>
      <div class=item>a</div>
      <div class=item>b</div>
      <div class=item>c</div>
    </div>
  <div class=row>
      <div class=item>d</div>
      <div class=item>e</div>
      <div class=item>f</div>
    </div>
  <div class=row>
      <div class=item>g</div>
      <div class=item>h</div>
      <div class=item>i</div>
    </div>
  <div class=row>
      <div class=item>j</div>
      <div class=item>k</div>
      <div class=item>l</div>
    </div>
--TEST--
"batch" filter
--TEMPLATE--
<table>
{% for row in items|batch(3, 'fill') %}
  <tr>
  {% for column in row %}
    <td>{{ column }}</td>
  {% endfor %}
  </tr>
{% endfor %}
</table>
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
<table>
  <tr>
      <td>a</td>
      <td>b</td>
      <td>c</td>
    </tr>
  <tr>
      <td>d</td>
      <td>e</td>
      <td>f</td>
    </tr>
  <tr>
      <td>g</td>
      <td>h</td>
      <td>i</td>
    </tr>
  <tr>
      <td>j</td>
      <td>fill</td>
      <td>fill</td>
    </tr>
</table>
--TEST--
"convert_encoding" filter
--CONDITION--
function_exists('iconv') || function_exists('mb_convert_encoding')
--TEMPLATE--
{{ "愛していますか？"|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }}
--DATA--
return array()
--EXPECT--
愛していますか？
--TEST--
"date" filter (interval support as of PHP 5.3)
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
{{ date2|date }}
{{ date2|date('%d days') }}
--DATA--
date_default_timezone_set('UTC');
$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
return array(
    'date2' => new DateInterval('P2D'),
)
--EXPECT--
2 days 0 hours
2 days
--TEST--
"date" filter
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
--DATA--
date_default_timezone_set('UTC');
$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
return array(
    'date1' => mktime(13, 45, 0, 10, 4, 2010),
)
--EXPECT--
2010-10-04
04/10/2010
--TEST--
"date" filter
--CONDITION--
version_compare(phpversion(), '5.5.0', '>=')
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s', timezone1) }}
{{ date1|date('d/m/Y H:i:s') }}

{{ date2|date('d/m/Y H:i:s P', 'Europe/Paris') }}
{{ date2|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date2|date('d/m/Y H:i:s P', false) }}
{{ date2|date('e', 'Europe/Paris') }}
{{ date2|date('e', false) }}
--DATA--
date_default_timezone_set('Europe/Paris');
return array(
    'date1' => new DateTimeImmutable('2010-10-04 13:45'),
    'date2' => new DateTimeImmutable('2010-10-04 13:45', new DateTimeZone('America/New_York')),
    'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 07:45:00
04/10/2010 13:45:00

04/10/2010 19:45:00 +02:00
05/10/2010 01:45:00 +08:00
04/10/2010 13:45:00 -04:00
Europe/Paris
America/New_York
--TEST--
"date" filter (interval support as of PHP 5.3)
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
{{ date1|date }}
{{ date1|date('%d days %h hours') }}
{{ date1|date('%d days %h hours', timezone1) }}
--DATA--
date_default_timezone_set('UTC');
return array(
    'date1' => new DateInterval('P2D'),
    // This should have no effect on DateInterval formatting
    'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
2 days
2 days 0 hours
2 days 0 hours
--TEST--
"date_modify" filter
--TEMPLATE--
{{ date1|date_modify('-1day')|date('Y-m-d H:i:s') }}
{{ date2|date_modify('-1day')|date('Y-m-d H:i:s') }}
--DATA--
date_default_timezone_set('UTC');
return array(
    'date1' => '2010-10-04 13:45',
    'date2' => new DateTime('2010-10-04 13:45'),
)
--EXPECT--
2010-10-03 13:45:00
2010-10-03 13:45:00
--TEST--
"date" filter
--TEMPLATE--
{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }}
{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }}
{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }}
--DATA--
date_default_timezone_set('UTC');
return array('date' => mktime(13, 45, 0, 10, 4, 2010))
--EXPECT--
04/10/2010 08:45:00 -05:00
04/10/2010 08:45:00 -05:00
04/10/2010 08:45:00 -05:00
--TEST--
"date" filter
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s P', 'America/Chicago') }}
{{ date1|date('e') }}
{{ date1|date('d/m/Y H:i:s') }}

{{ date2|date }}
{{ date2|date('d/m/Y') }}
{{ date2|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date2|date('d/m/Y H:i:s', timezone1) }}
{{ date2|date('d/m/Y H:i:s') }}

{{ date3|date }}
{{ date3|date('d/m/Y') }}

{{ date4|date }}
{{ date4|date('d/m/Y') }}

{{ date5|date }}
{{ date5|date('d/m/Y') }}

{{ date6|date('d/m/Y H:i:s P', 'Europe/Paris') }}
{{ date6|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date6|date('d/m/Y H:i:s P', false) }}
{{ date6|date('e', 'Europe/Paris') }}
{{ date6|date('e', false) }}

{{ date7|date }}
--DATA--
date_default_timezone_set('Europe/Paris');
return array(
    'date1' => mktime(13, 45, 0, 10, 4, 2010),
    'date2' => new DateTime('2010-10-04 13:45'),
    'date3' => '2010-10-04 13:45',
    'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
    'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
    'date6' => new DateTime('2010-10-04 13:45', new DateTimeZone('America/New_York')),
    'date7' => '2010-01-28T15:00:00+05:00',
    'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 19:45:00 +08:00
04/10/2010 06:45:00 -05:00
Europe/Paris
04/10/2010 13:45:00

October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 07:45:00
04/10/2010 13:45:00

October 4, 2010 13:45
04/10/2010

October 4, 2010 15:45
04/10/2010

January 2, 1964 04:04
02/01/1964

04/10/2010 19:45:00 +02:00
05/10/2010 01:45:00 +08:00
04/10/2010 13:45:00 -04:00
Europe/Paris
America/New_York

January 28, 2010 11:00
--TEST--
"default" filter
--TEMPLATE--
Variable:
{{ definedVar                  |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ zeroVar                     |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ emptyVar                    |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nullVar                     |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ undefinedVar                |default('default') is same as('default') ? 'ok' : 'ko' }}
Array access:
{{ nested.definedVar           |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested['definedVar']        |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested.zeroVar              |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested.emptyVar             |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.nullVar              |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.undefinedVar         |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested['undefinedVar']      |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ undefinedVar.foo            |default('default') is same as('default') ? 'ok' : 'ko' }}
Plain values:
{{ 'defined'                   |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ 0                           |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ ''                          |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ null                        |default('default') is same as('default') ? 'ok' : 'ko' }}
Precedence:
{{ 'o' ~ nullVar               |default('k') }}
{{ 'o' ~ nested.nullVar        |default('k') }}
Object methods:
{{ object.foo                  |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.undefinedMethod      |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.getFoo()             |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.getFoo('a')          |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.undefinedMethod()    |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.undefinedMethod('a') |default('default') is same as('default') ? 'ok' : 'ko' }}
Deep nested:
{{ nested.undefinedVar.foo.bar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.definedArray.0       |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested['definedArray'][0]   |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.self.foo             |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.self.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.undefinedMethod.self |default('default') is same as('default') ? 'ok' : 'ko' }}
--DATA--
return array(
    'definedVar' => 'defined',
    'zeroVar'    => 0,
    'emptyVar'   => '',
    'nullVar'    => null,
    'nested'     => array(
        'definedVar'   => 'defined',
        'zeroVar'      => 0,
        'emptyVar'     => '',
        'nullVar'      => null,
        'definedArray' => array(0),
    ),
    'object' => new TwigTestFoo(),
)
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
Variable:
ok
ok
ok
ok
ok
Array access:
ok
ok
ok
ok
ok
ok
ok
ok
Plain values:
ok
ok
ok
ok
Precedence:
ok
ok
Object methods:
ok
ok
ok
ok
ok
ok
Deep nested:
ok
ok
ok
ok
ok
ok
--DATA--
return array(
    'definedVar' => 'defined',
    'zeroVar'    => 0,
    'emptyVar'   => '',
    'nullVar'    => null,
    'nested'     => array(
        'definedVar'   => 'defined',
        'zeroVar'      => 0,
        'emptyVar'     => '',
        'nullVar'      => null,
        'definedArray' => array(0),
    ),
    'object' => new TwigTestFoo(),
)
--CONFIG--
return array('strict_variables' => true)
--EXPECT--
Variable:
ok
ok
ok
ok
ok
Array access:
ok
ok
ok
ok
ok
ok
ok
ok
Plain values:
ok
ok
ok
ok
Precedence:
ok
ok
Object methods:
ok
ok
ok
ok
ok
ok
Deep nested:
ok
ok
ok
ok
ok
ok
--TEST--
dynamic filter
--TEMPLATE--
{{ 'bar'|foo_path }}
{{ 'bar'|a_foo_b_bar }}
--DATA--
return array()
--EXPECT--
foo/bar
a/b/bar
--TEST--
"escape" filter does not escape with the html strategy when using the html_attr strategy
--TEMPLATE--
{{ '<br />'|escape('html_attr') }}
--DATA--
return array()
--EXPECT--
&lt;br&#x20;&#x2F;&gt;
--TEST--
"escape" filter
--TEMPLATE--
{{ "愛していますか？ <br />"|e }}
--DATA--
return array()
--EXPECT--
愛していますか？ &lt;br /&gt;
--TEST--
"escape" filter
--TEMPLATE--
{{ "foo <br />"|e }}
--DATA--
return array()
--EXPECT--
foo &lt;br /&gt;
--TEST--
"first" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|first }}
{{ {a: 1, b: 2, c: 3, d: 4}|first }}
{{ '1234'|first }}
{{ arr|first }}
{{ 'Ä€é'|first }}
{{ ''|first }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
1
1
1
1
Ä
--TEST--
"escape" filter
--TEMPLATE--
{% set foo %}
    foo<br />
{% endset %}

{{ foo|e('html') -}}
{{ foo|e('js') }}
{% autoescape true %}
    {{ foo }}
{% endautoescape %}
--DATA--
return array()
--EXPECT--
    foo&lt;br /&gt;
\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A
        foo<br />
--TEST--
"format" filter
--TEMPLATE--
{{ string|format(foo, 3) }}
--DATA--
return array('string' => '%s/%d', 'foo' => 'bar')
--EXPECT--
bar/3
--TEST--
"join" filter
--TEMPLATE--
{{ ["foo", "bar"]|join(', ') }}
{{ foo|join(', ') }}
{{ bar|join(', ') }}
--DATA--
return array('foo' => new TwigTestFoo(), 'bar' => new ArrayObject(array(3, 4)))
--EXPECT--
foo, bar
1, 2
3, 4
--TEST--
"json_encode" filter
--TEMPLATE--
{{ "foo"|json_encode|raw }}
{{ foo|json_encode|raw }}
{{ [foo, "foo"]|json_encode|raw }}
--DATA--
return array('foo' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
"foo"
"foo"
["foo","foo"]
--TEST--
"last" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|last }}
{{ {a: 1, b: 2, c: 3, d: 4}|last }}
{{ '1234'|last }}
{{ arr|last }}
{{ 'Ä€é'|last }}
{{ ''|last }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
4
4
4
4
é
--TEST--
"length" filter
--TEMPLATE--
{{ array|length }}
{{ string|length }}
{{ number|length }}
{{ markup|length }}
--DATA--
return array('array' => array(1, 4), 'string' => 'foo', 'number' => 1000, 'markup' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
2
3
4
3
--TEST--
"length" filter
--CONDITION--
function_exists('mb_get_info')
--TEMPLATE--
{{ string|length }}
{{ markup|length }}
--DATA--
return array('string' => 'été', 'markup' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
3
3
--TEST--
"merge" filter
--TEMPLATE--
{{ items|merge({'bar': 'foo'})|join }}
{{ items|merge({'bar': 'foo'})|keys|join }}
{{ {'bar': 'foo'}|merge(items)|join }}
{{ {'bar': 'foo'}|merge(items)|keys|join }}
{{ numerics|merge([4, 5, 6])|join }}
--DATA--
return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3))
--EXPECT--
barfoo
foobar
foobar
barfoo
123456
--TEST--
"nl2br" filter
--TEMPLATE--
{{ "I like Twig.\nYou will like it too.\n\nEverybody like it!"|nl2br }}
{{ text|nl2br }}
--DATA--
return array('text' => "If you have some <strong>HTML</strong>\nit will be escaped.")
--EXPECT--
I like Twig.<br />
You will like it too.<br />
<br />
Everybody like it!
If you have some &lt;strong&gt;HTML&lt;/strong&gt;<br />
it will be escaped.
--TEST--
"number_format" filter with defaults.
--TEMPLATE--
{{ 20|number_format }}
{{ 20.25|number_format }}
{{ 20.25|number_format(1) }}
{{ 20.25|number_format(2, ',') }}
{{ 1020.25|number_format }}
{{ 1020.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',', '.') }}
--DATA--
$twig->getExtension('core')->setNumberFormat(2, '!', '=');
return array();
--EXPECT--
20!00
20!25
20!3
20,25
1=020!25
1=020,25
1.020,25
--TEST--
"number_format" filter
--TEMPLATE--
{{ 20|number_format }}
{{ 20.25|number_format }}
{{ 20.25|number_format(2) }}
{{ 20.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',', '.') }}
--DATA--
return array();
--EXPECT--
20
20
20.25
20,25
1,020,25
1.020,25
--TEST--
"replace" filter
--TEMPLATE--
{{ "I like %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }}
--DATA--
return array()
--EXPECT--
I like foo and bar.
--TEST--
"reverse" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|reverse|join('') }}
{{ '1234évènement'|reverse }}
{{ arr|reverse|join('') }}
{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }}
{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }}
{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
4321
tnemenèvé4321
4321
a,c
a,c
a,c
--TEST--
"round" filter
--TEMPLATE--
{{ 2.7|round }}
{{ 2.1|round }}
{{ 2.1234|round(3, 'floor') }}
{{ 2.1|round(0, 'ceil') }}

{{ 21.3|round(-1)}}
{{ 21.3|round(-1, 'ceil')}}
{{ 21.3|round(-1, 'floor')}}
--DATA--
return array()
--EXPECT--
3
2
2.123
3

20
30
20
--TEST--
"slice" filter
--TEMPLATE--
{{ [1, 2, 3, 4][1:2]|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }}
{{ [1, 2, 3, 4][start:length]|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2)|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2)|keys|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2, true)|keys|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|keys|join('') }}
{{ '1234'|slice(1, 2) }}
{{ '1234'[1:2] }}
{{ arr|slice(1, 2)|join('') }}
{{ arr[1:2]|join('') }}

{{ [1, 2, 3, 4]|slice(1)|join('') }}
{{ [1, 2, 3, 4][1:]|join('') }}
{{ '1234'|slice(1) }}
{{ '1234'[1:] }}
{{ '1234'[:1] }}
--DATA--
return array('start' => 1, 'length' => 2, 'arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
23
23
23
23
01
12
23
bc
23
23
23
23

234
234
234
234
1
--TEST--
"sort" filter
--TEMPLATE--
{{ array1|sort|join }}
{{ array2|sort|join }}
--DATA--
return array('array1' => array(4, 1), 'array2' => array('foo', 'bar'))
--EXPECT--
14
barfoo
--TEST--
"split" filter
--TEMPLATE--
{{ "one,two,three,four,five"|split(',')|join('-') }}
{{ foo|split(',')|join('-') }}
{{ foo|split(',', 3)|join('-') }}
{{ baz|split('')|join('-') }}
{{ baz|split('', 2)|join('-') }}
{{ foo|split(',', -2)|join('-') }}
--DATA--
return array('foo' => "one,two,three,four,five", 'baz' => '12345',)
--EXPECT--
one-two-three-four-five
one-two-three-four-five
one-two-three,four,five
1-2-3-4-5
12-34-5
one-two-three--TEST--
"trim" filter
--TEMPLATE--
{{ "  I like Twig.  "|trim }}
{{ text|trim }}
{{ "  foo/"|trim("/") }}
--DATA--
return array('text' => "  If you have some <strong>HTML</strong> it will be escaped.  ")
--EXPECT--
I like Twig.
If you have some &lt;strong&gt;HTML&lt;/strong&gt; it will be escaped.
  foo
--TEST--
"url_encode" filter for PHP < 5.4 and HHVM
--CONDITION--
defined('PHP_QUERY_RFC3986')
--TEMPLATE--
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
{{ {}|url_encode|default("default") }}
{{ 'spéßi%le%c0d@dspa ce'|url_encode }}
--DATA--
return array()
--EXPECT--
foo=bar&amp;number=3&amp;sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&amp;spa%20ce=
foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
default
sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce
--TEST--
"url_encode" filter
--CONDITION--
defined('PHP_QUERY_RFC3986')
--TEMPLATE--
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
{{ {}|url_encode|default("default") }}
{{ 'spéßi%le%c0d@dspa ce'|url_encode }}
--DATA--
return array()
--EXPECT--
foo=bar&amp;number=3&amp;sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&amp;spa%20ce=
foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
default
sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce
--TEST--
"attribute" function
--TEMPLATE--
{{ attribute(obj, method) }}
{{ attribute(array, item) }}
{{ attribute(obj, "bar", ["a", "b"]) }}
{{ attribute(obj, "bar", arguments) }}
{{ attribute(obj, method) is defined ? 'ok' : 'ko' }}
{{ attribute(obj, nonmethod) is defined ? 'ok' : 'ko' }}
--DATA--
return array('obj' => new TwigTestFoo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo', 'nonmethod' => 'xxx', 'arguments' => array('a', 'b'))
--EXPECT--
foo
bar
bar_a-b
bar_a-b
ok
ko
--TEST--
"block" function
--TEMPLATE--
{% extends 'base.twig' %}
{% block bar %}BAR{% endblock %}
--TEMPLATE(base.twig)--
{% block foo %}{{ block('bar') }}{% endblock %}
{% block bar %}BAR_BASE{% endblock %}
--DATA--
return array()
--EXPECT--
BARBAR
--TEST--
"constant" function
--TEMPLATE--
{{ constant('DATE_W3C') == expect ? 'true' : 'false' }}
{{ constant('ARRAY_AS_PROPS', object) }}
--DATA--
return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi')));
--EXPECT--
true
2
--TEST--
"cycle" function
--TEMPLATE--
{% for i in 0..6 %}
{{ cycle(array1, i) }}-{{ cycle(array2, i) }}
{% endfor %}
--DATA--
return array('array1' => array('odd', 'even'), 'array2' => array('apple', 'orange', 'citrus'))
--EXPECT--
odd-apple
even-orange
odd-citrus
even-apple
odd-orange
even-citrus
odd-apple
--TEST--
"date" function
--TEMPLATE--
{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }}
{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }}
--DATA--
date_default_timezone_set('UTC');
return array('date' => mktime(13, 45, 0, 10, 4, 2010))
--EXPECT--
04/10/2010 09:45:00 -04:00
04/10/2010 09:45:00 -04:00
--TEST--
"date" function
--TEMPLATE--
{{ date() == date('now') ? 'OK' : 'KO' }}
{{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }}
--DATA--
date_default_timezone_set('UTC');
return array(
    'date1' => mktime(13, 45, 0, 10, 4, 2010),
    'date2' => new DateTime('2010-10-04 13:45'),
    'date3' => '2010-10-04 13:45',
    'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
    'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
)
--EXPECT--
OK
OK
OK
OK
OK
OK
--TEST--
"dump" function, xdebug is not loaded or xdebug <2.2-dev is loaded
--CONDITION--
!extension_loaded('xdebug') || (($r = new ReflectionExtension('xdebug')) && version_compare($r->getVersion(), '2.2-dev', '<'))
--TEMPLATE--
{{ dump() }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--CONFIG--
return array('debug' => true, 'autoescape' => false);
--TEST--
"dump" function
--CONDITION--
!extension_loaded('xdebug')
--TEMPLATE--
{{ dump('foo') }}
{{ dump('foo', 'bar') }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--CONFIG--
return array('debug' => true, 'autoescape' => false);
--EXPECT--
string(3) "foo"

string(3) "foo"
string(3) "bar"
--TEST--
dynamic function
--TEMPLATE--
{{ foo_path('bar') }}
{{ a_foo_b_bar('bar') }}
--DATA--
return array()
--EXPECT--
foo/bar
a/b/bar
--TEST--
"include" function
--TEMPLATE--
{% set tmp = include("foo.twig") %}

FOO{{ tmp }}BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO
FOOBARBAR
--TEST--
"include" function is safe for auto-escaping
--TEMPLATE--
{{ include("foo.twig") }}
--TEMPLATE(foo.twig)--
<p>Test</p>
--DATA--
return array()
--EXPECT--
<p>Test</p>
--TEST--
"include" function
--TEMPLATE--
FOO
{{ include("foo.twig") }}

BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO

FOOBAR

BAR
--TEST--
"include" function allows expressions for the template to include
--TEMPLATE--
FOO
{{ include(foo) }}

BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO

FOOBAR

BAR
--TEST--
"include" function
--TEMPLATE--
{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }}
{{ include("foo.twig", ignore_missing = true) }}
{{ include("foo.twig", ignore_missing = true, variables = {}) }}
{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }}
--DATA--
return array()
--EXPECT--
--TEST--
"include" function
--TEMPLATE--
{% extends "base.twig" %}

{% block content %}
    {{ parent() }}
{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}
    {{ include("foo.twig") }}
{% endblock %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
--TEST--
"include" function
--TEMPLATE--
{{ include("foo.twig") }}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
--TEST--
"include" tag sandboxed
--TEMPLATE--
{{ include("foo.twig", sandboxed = true) }}
--TEMPLATE(foo.twig)--
{{ foo|e }}
--DATA--
return array()
--EXCEPTION--
Twig_Sandbox_SecurityError: Filter "e" is not allowed in "index.twig" at line 2.
--TEST--
"include" function accepts Twig_Template instance
--TEMPLATE--
{{ include(foo) }} FOO
--TEMPLATE(foo.twig)--
BAR
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BAR FOO
--TEST--
"include" function
--TEMPLATE--
{{ include(["foo.twig", "bar.twig"]) }}
{{- include(["bar.twig", "foo.twig"]) }}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
--TEST--
"include" function accept variables and with_context
--TEMPLATE--
{{ include("foo.twig") }}
{{- include("foo.twig", with_context = false) }}
{{- include("foo.twig", {'foo1': 'bar'}) }}
{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }}
--TEMPLATE(foo.twig)--
{% for k, v in _context %}{{ k }},{% endfor %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
foo,global,_parent,
global,_parent,
foo,global,foo1,_parent,
foo1,global,_parent,
--TEST--
"include" function accept variables
--TEMPLATE--
{{ include("foo.twig", {'foo': 'bar'}) }}
{{- include("foo.twig", vars) }}
--TEMPLATE(foo.twig)--
{{ foo }}
--DATA--
return array('vars' => array('foo' => 'bar'))
--EXPECT--
bar
bar
--TEST--
"max" function
--TEMPLATE--
{{ max([2, 1, 3, 5, 4]) }}
{{ max(2, 1, 3, 5, 4) }}
{{ max({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }}
--DATA--
return array()
--EXPECT--
5
5
two
--TEST--
"min" function
--TEMPLATE--
{{ min(2, 1, 3, 5, 4) }}
{{ min([2, 1, 3, 5, 4]) }}
{{ min({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }}
--DATA--
return array()
--EXPECT--
1
1
five
--TEST--
"range" function
--TEMPLATE--
{{ range(low=0+1, high=10+0, step=2)|join(',') }}
--DATA--
return array()
--EXPECT--
1,3,5,7,9
--TEST--
"block" function recursively called in a parent template
--TEMPLATE--
{% extends "ordered_menu.twig" %}
{% block label %}"{{ parent() }}"{% endblock %}
{% block list %}{% set class = 'b' %}{{ parent() }}{% endblock %}
--TEMPLATE(ordered_menu.twig)--
{% extends "menu.twig" %}
{% block list %}{% set class = class|default('a') %}<ol class="{{ class }}">{{ block('children') }}</ol>{% endblock %}
--TEMPLATE(menu.twig)--
{% extends "base.twig" %}
{% block list %}<ul>{{ block('children') }}</ul>{% endblock %}
{% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %}
{% block item %}<li>{% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %}</li>{% endblock %}
{% block label %}{{ item }}{{ block('unknown') }}{% endblock %}
--TEMPLATE(base.twig)--
{{ block('list') }}
--DATA--
return array('item' => array('1', '2', array('3.1', array('3.2.1', '3.2.2'), '3.4')))
--EXPECT--
<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>
--TEST--
"source" function
--TEMPLATE--
FOO
{{ source("foo.twig") }}

BAR
--TEMPLATE(foo.twig)--
{{ foo }}<br />
--DATA--
return array()
--EXPECT--
FOO

{{ foo }}<br />

BAR
--TEST--
"template_from_string" function
--TEMPLATE--
{% include template_from_string(template) %}

{% include template_from_string("Hello {{ name }}") %}
{% include template_from_string('{% extends "parent.twig" %}{% block content %}Hello {{ name }}{% endblock %}') %}
--TEMPLATE(parent.twig)--
{% block content %}{% endblock %}
--DATA--
return array('name' => 'Fabien', 'template' => "Hello {{ name }}")
--EXPECT--
Hello Fabien
Hello Fabien
Hello Fabien
--TEST--
macro
--TEMPLATE--
{% from _self import test %}

{% macro test(a, b = 'bar') -%}
{{ a }}{{ b }}
{%- endmacro %}

{{ test('foo') }}
{{ test('bar', 'foo') }}
--DATA--
return array();
--EXPECT--
foobar
barfoo
--TEST--
macro
--TEMPLATE--
{% import _self as macros %}

{% macro foo(data) %}
    {{ data }}
{% endmacro %}

{% macro bar() %}
    <br />
{% endmacro %}

{{ macros.foo(macros.bar()) }}
--DATA--
return array();
--EXPECT--
<br />
--TEST--
macro
--TEMPLATE--
{% from _self import test %}

{% macro test(this) -%}
    {{ this }}
{%- endmacro %}

{{ test(this) }}
--DATA--
return array('this' => 'foo');
--EXPECT--
foo
--TEST--
macro
--TEMPLATE--
{% import _self as test %}
{% from _self import test %}

{% macro test(a, b) -%}
    {{ a|default('a') }}<br />
    {{- b|default('b') }}<br />
{%- endmacro %}

{{ test.test() }}
{{ test() }}
{{ test.test(1, "c") }}
{{ test(1, "c") }}
--DATA--
return array();
--EXPECT--
a<br />b<br />
a<br />b<br />
1<br />c<br />
1<br />c<br />
--TEST--
macro with a filter
--TEMPLATE--
{% import _self as test %}

{% macro test() %}
    {% filter escape %}foo<br />{% endfilter %}
{% endmacro %}

{{ test.test() }}
--DATA--
return array();
--EXPECT--
foo&lt;br /&gt;
--TEST--
Twig outputs 0 nodes correctly
--TEMPLATE--
{{ foo }}0{{ foo }}
--DATA--
return array('foo' => 'foo')
--EXPECT--
foo0foo
--TEST--
error in twig extension
--TEMPLATE--
{{ object.region is not null ? object.regionChoices[object.region] }}
--EXPECT--
house.region.s
--TEST--
Twig is able to deal with SimpleXMLElement instances as variables
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
Hello '{{ images.image.0.group }}'!
{{ images.image.0.group.attributes.myattr }}
{{ images.children().image.count() }}
{% for image in images %}
    - {{ image.group }}
{% endfor %}
--DATA--
return array('images' => new SimpleXMLElement('<images><image><group myattr="example">foo</group></image><image><group>bar</group></image></images>'))
--EXPECT--
Hello 'foo'!
example
2
    - foo
    - bar
--TEST--
Twig does not confuse strings with integers in getAttribute()
--TEMPLATE--
{{ hash['2e2'] }}
--DATA--
return array('hash' => array('2e2' => 'works'))
--EXPECT--
works
--TEST--
"autoescape" tag applies escaping on its children
--TEMPLATE--
{% autoescape %}
{{ var }}<br />
{% endautoescape %}
{% autoescape 'html' %}
{{ var }}<br />
{% endautoescape %}
{% autoescape false %}
{{ var }}<br />
{% endautoescape %}
{% autoescape true %}
{{ var }}<br />
{% endautoescape %}
{% autoescape false %}
{{ var }}<br />
{% endautoescape %}
--DATA--
return array('var' => '<br />')
--EXPECT--
&lt;br /&gt;<br />
&lt;br /&gt;<br />
<br /><br />
&lt;br /&gt;<br />
<br /><br />
--TEST--
"autoescape" tag applies escaping on embedded blocks
--TEMPLATE--
{% autoescape 'html' %}
  {% block foo %}
    {{ var }}
  {% endblock %}
{% endautoescape %}
--DATA--
return array('var' => '<br />')
--EXPECT--
&lt;br /&gt;
--TEST--
"autoescape" tag does not double-escape
--TEMPLATE--
{% autoescape 'html' %}
{{ var|escape }}
{% endautoescape %}
--DATA--
return array('var' => '<br />')
--EXPECT--
&lt;br /&gt;
--TEST--
"autoescape" tag applies escaping after calling functions
--TEMPLATE--

autoescape false
{% autoescape false %}

safe_br
{{ safe_br() }}

unsafe_br
{{ unsafe_br() }}

{% endautoescape %}

autoescape 'html'
{% autoescape 'html' %}

safe_br
{{ safe_br() }}

unsafe_br
{{ unsafe_br() }}

unsafe_br()|raw
{{ (unsafe_br())|raw }}

safe_br()|escape
{{ (safe_br())|escape }}

safe_br()|raw
{{ (safe_br())|raw }}

unsafe_br()|escape
{{ (unsafe_br())|escape }}

{% endautoescape %}

autoescape js
{% autoescape 'js' %}

safe_br
{{ safe_br() }}

{% endautoescape %}
--DATA--
return array()
--EXPECT--

autoescape false

safe_br
<br />

unsafe_br
<br />


autoescape 'html'

safe_br
<br />

unsafe_br
&lt;br /&gt;

unsafe_br()|raw
<br />

safe_br()|escape
&lt;br /&gt;

safe_br()|raw
<br />

unsafe_br()|escape
&lt;br /&gt;


autoescape js

safe_br
\x3Cbr\x20\x2F\x3E
--TEST--
"autoescape" tag does not apply escaping on literals
--TEMPLATE--
{% autoescape 'html' %}

1. Simple literal
{{ "<br />" }}

2. Conditional expression with only literals
{{ true ? "<br />" : "<br>" }}

3. Conditional expression with a variable
{{ true ? "<br />" : someVar }}

4. Nested conditionals with only literals
{{ true ? (true ? "<br />" : "<br>") : "\n" }}

5. Nested conditionals with a variable
{{ true ? (true ? "<br />" : someVar) : "\n" }}

6. Nested conditionals with a variable marked safe
{{ true ? (true ? "<br />" : someVar|raw) : "\n" }}

{% endautoescape %}
--DATA--
return array()
--EXPECT--

1. Simple literal
<br />

2. Conditional expression with only literals
<br />

3. Conditional expression with a variable
&lt;br /&gt;

4. Nested conditionals with only literals
<br />

5. Nested conditionals with a variable
&lt;br /&gt;

6. Nested conditionals with a variable marked safe
<br />
--TEST--
"autoescape" tags can be nested at will
--TEMPLATE--
{{ var }}
{% autoescape 'html' %}
  {{ var }}
  {% autoescape false %}
    {{ var }}
    {% autoescape 'html' %}
      {{ var }}
    {% endautoescape %}
    {{ var }}
  {% endautoescape %}
  {{ var }}
{% endautoescape %}
{{ var }}
--DATA--
return array('var' => '<br />')
--EXPECT--
&lt;br /&gt;
  &lt;br /&gt;
      <br />
          &lt;br /&gt;
        <br />
    &lt;br /&gt;
&lt;br /&gt;
--TEST--
"autoescape" tag applies escaping to object method calls
--TEMPLATE--
{% autoescape 'html' %}
{{ user.name }}
{{ user.name|lower }}
{{ user }}
{% endautoescape %}
--EXPECT--
Fabien&lt;br /&gt;
fabien&lt;br /&gt;
Fabien&lt;br /&gt;
--TEST--
"autoescape" tag does not escape when raw is used as a filter
--TEMPLATE--
{% autoescape 'html' %}
{{ var|raw }}
{% endautoescape %}
--DATA--
return array('var' => '<br />')
--EXPECT--
<br />
--TEST--
"autoescape" tag accepts an escaping strategy
--TEMPLATE--
{% autoescape true js %}{{ var }}{% endautoescape %}

{% autoescape true html %}{{ var }}{% endautoescape %}

{% autoescape 'js' %}{{ var }}{% endautoescape %}

{% autoescape 'html' %}{{ var }}{% endautoescape %}
--DATA--
return array('var' => '<br />"')
--EXPECT--
\x3Cbr\x20\x2F\x3E\x22
&lt;br /&gt;&quot;
\x3Cbr\x20\x2F\x3E\x22
&lt;br /&gt;&quot;
--TEST--
escape types
--TEMPLATE--

1. autoescape 'html' |escape('js')

{% autoescape 'html' %}
<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
{% endautoescape %}

2. autoescape 'html' |escape('js')

{% autoescape 'html' %}
<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
{% endautoescape %}

3. autoescape 'js' |escape('js')

{% autoescape 'js' %}
<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
{% endautoescape %}

4. no escape

{% autoescape false %}
<a onclick="alert(&quot;{{ msg }}&quot;)"></a>
{% endautoescape %}

5. |escape('js')|escape('html')

{% autoescape false %}
<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
{% endautoescape %}

6. autoescape 'html' |escape('js')|escape('html')

{% autoescape 'html' %}
<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
{% endautoescape %}

--DATA--
return array('msg' => "<>\n'\"")
--EXPECT--

1. autoescape 'html' |escape('js')

<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>

2. autoescape 'html' |escape('js')

<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>

3. autoescape 'js' |escape('js')

<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>

4. no escape

<a onclick="alert(&quot;<>
'"&quot;)"></a>

5. |escape('js')|escape('html')

<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>

6. autoescape 'html' |escape('js')|escape('html')

<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>

--TEST--
"autoescape" tag do not applies escaping on filter arguments
--TEMPLATE--
{% autoescape 'html' %}
{{ var|nl2br("<br />") }}
{{ var|nl2br("<br />"|escape) }}
{{ var|nl2br(sep) }}
{{ var|nl2br(sep|raw) }}
{{ var|nl2br(sep|escape) }}
{% endautoescape %}
--DATA--
return array('var' => "<Fabien>\nTwig", 'sep' => '<br />')
--EXPECT--
&lt;Fabien&gt;<br />
Twig
&lt;Fabien&gt;&lt;br /&gt;
Twig
&lt;Fabien&gt;<br />
Twig
&lt;Fabien&gt;<br />
Twig
&lt;Fabien&gt;&lt;br /&gt;
Twig
--TEST--
"autoescape" tag applies escaping after calling filters
--TEMPLATE--
{% autoescape 'html' %}

(escape_and_nl2br is an escaper filter)

1. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added, 
  the output is not escaped )
{{ var|escape_and_nl2br }}

2. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added, 
  the output is not escaped, |raw is redundant )
{{ var|escape_and_nl2br|raw }}

3. Explicit escape
( var is escaped by |escape_and_nl2br, line-breaks are added,
  the output is explicitly escaped by |escape )
{{ var|escape_and_nl2br|escape }}

4. Escape non-escaper filter output
( var is upper-cased by |upper,
  the output is auto-escaped )
{{ var|upper }}

5. Escape if last filter is not an escaper
( var is escaped by |escape_and_nl2br, line-breaks are added,
  the output is upper-cased by |upper,
  the output is auto-escaped as |upper is not an escaper )
{{ var|escape_and_nl2br|upper }}

6. Don't escape escaper filter output
( var is upper cased by upper,
  the output is escaped by |escape_and_nl2br, line-breaks are added,
  the output is not escaped as |escape_and_nl2br is an escaper )
{{ var|upper|escape_and_nl2br }}

7. Escape if last filter is not an escaper
( the output of |format is "<b>" ~ var ~ "</b>",
  the output is auto-escaped )
{{ "<b>%s</b>"|format(var) }}

8. Escape if last filter is not an escaper
( the output of |format is "<b>" ~ var ~ "</b>",
  |raw is redundant,
  the output is auto-escaped )
{{ "<b>%s</b>"|raw|format(var) }}

9. Don't escape escaper filter output
( the output of |format is "<b>" ~ var ~ "</b>",
  the output is not escaped due to |raw filter at the end )
{{ "<b>%s</b>"|format(var)|raw }}

10. Don't escape escaper filter output
( the output of |format is "<b>" ~ var ~ "</b>",
  the output is not escaped due to |raw filter at the end,
  the |raw filter on var is redundant )
{{ "<b>%s</b>"|format(var|raw)|raw }}

{% endautoescape %}
--DATA--
return array('var' => "<Fabien>\nTwig")
--EXPECT--

(escape_and_nl2br is an escaper filter)

1. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added, 
  the output is not escaped )
&lt;Fabien&gt;<br />
Twig

2. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added, 
  the output is not escaped, |raw is redundant )
&lt;Fabien&gt;<br />
Twig

3. Explicit escape
( var is escaped by |escape_and_nl2br, line-breaks are added,
  the output is explicitly escaped by |escape )
&amp;lt;Fabien&amp;gt;&lt;br /&gt;
Twig

4. Escape non-escaper filter output
( var is upper-cased by |upper,
  the output is auto-escaped )
&lt;FABIEN&gt;
TWIG

5. Escape if last filter is not an escaper
( var is escaped by |escape_and_nl2br, line-breaks are added,
  the output is upper-cased by |upper,
  the output is auto-escaped as |upper is not an escaper )
&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
TWIG

6. Don't escape escaper filter output
( var is upper cased by upper,
  the output is escaped by |escape_and_nl2br, line-breaks are added,
  the output is not escaped as |escape_and_nl2br is an escaper )
&lt;FABIEN&gt;<br />
TWIG

7. Escape if last filter is not an escaper
( the output of |format is "<b>" ~ var ~ "</b>",
  the output is auto-escaped )
&lt;b&gt;&lt;Fabien&gt;
Twig&lt;/b&gt;

8. Escape if last filter is not an escaper
( the output of |format is "<b>" ~ var ~ "</b>",
  |raw is redundant,
  the output is auto-escaped )
&lt;b&gt;&lt;Fabien&gt;
Twig&lt;/b&gt;

9. Don't escape escaper filter output
( the output of |format is "<b>" ~ var ~ "</b>",
  the output is not escaped due to |raw filter at the end )
<b><Fabien>
Twig</b>

10. Don't escape escaper filter output
( the output of |format is "<b>" ~ var ~ "</b>",
  the output is not escaped due to |raw filter at the end,
  the |raw filter on var is redundant )
<b><Fabien>
Twig</b>
--TEST--
"autoescape" tag applies escaping after calling filters, and before calling pre_escape filters
--TEMPLATE--
{% autoescape 'html' %}

(nl2br is pre_escaped for "html" and declared safe for "html")

1. Pre-escape and don't post-escape
( var|escape|nl2br )
{{ var|nl2br }}

2. Don't double-pre-escape
( var|escape|nl2br )
{{ var|escape|nl2br }}

3. Don't escape safe values
( var|raw|nl2br )
{{ var|raw|nl2br }}

4. Don't escape safe values
( var|escape|nl2br|nl2br )
{{ var|nl2br|nl2br }}

5. Re-escape values that are escaped for an other contexts
( var|escape_something|escape|nl2br )
{{ var|escape_something|nl2br }}

6. Still escape when using filters not declared safe
( var|escape|nl2br|upper|escape )
{{ var|nl2br|upper }}

{% endautoescape %}
--DATA--
return array('var' => "<Fabien>\nTwig")
--EXPECT--

(nl2br is pre_escaped for "html" and declared safe for "html")

1. Pre-escape and don't post-escape
( var|escape|nl2br )
&lt;Fabien&gt;<br />
Twig

2. Don't double-pre-escape
( var|escape|nl2br )
&lt;Fabien&gt;<br />
Twig

3. Don't escape safe values
( var|raw|nl2br )
<Fabien><br />
Twig

4. Don't escape safe values
( var|escape|nl2br|nl2br )
&lt;Fabien&gt;<br /><br />
Twig

5. Re-escape values that are escaped for an other contexts
( var|escape_something|escape|nl2br )
&lt;FABIEN&gt;<br />
TWIG

6. Still escape when using filters not declared safe
( var|escape|nl2br|upper|escape )
&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
TWIG

--TEST--
"autoescape" tag handles filters preserving the safety
--TEMPLATE--
{% autoescape 'html' %}

(preserves_safety is preserving safety for "html")

1. Unsafe values are still unsafe
( var|preserves_safety|escape )
{{ var|preserves_safety }}

2. Safe values are still safe
( var|escape|preserves_safety )
{{ var|escape|preserves_safety }}

3. Re-escape values that are escaped for an other contexts
( var|escape_something|preserves_safety|escape )
{{ var|escape_something|preserves_safety }}

4. Still escape when using filters not declared safe
( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
{{ var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'}) }}

{% endautoescape %}
--DATA--
return array('var' => "<Fabien>\nTwig")
--EXPECT--

(preserves_safety is preserving safety for "html")

1. Unsafe values are still unsafe
( var|preserves_safety|escape )
&lt;FABIEN&gt;
TWIG

2. Safe values are still safe
( var|escape|preserves_safety )
&LT;FABIEN&GT;
TWIG

3. Re-escape values that are escaped for an other contexts
( var|escape_something|preserves_safety|escape )
&lt;FABIEN&gt;
TWIG

4. Still escape when using filters not declared safe
( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
&amp;LT;FABPOT&amp;GT;
TWIG

--TEST--
"block" tag
--TEMPLATE--
{% block title1 %}FOO{% endblock %}
{% block title2 foo|lower %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
FOObar
--TEST--
"block" tag
--TEMPLATE--
{% block content %}
    {% block content %}
    {% endblock %}
{% endblock %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3
--TEST--
"§" special chars in a block name
--TEMPLATE--
{% block § %}
§
{% endblock § %}
--DATA--
return array()
--EXPECT--
§
--TEST--
"embed" tag
--TEMPLATE--
FOO
{% embed "foo.twig" %}
    {% block c1 %}
        {{ parent() }}
        block1extended
    {% endblock %}
{% endembed %}

BAR
--TEMPLATE(foo.twig)--
A
{% block c1 %}
    block1
{% endblock %}
B
{% block c2 %}
    block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
FOO

A
            block1

        block1extended
    B
    block2
C
BAR
--TEST--
"embed" tag
--TEMPLATE(index.twig)--
FOO
{% embed "foo.twig" %}
    {% block c1 %}
        {{ nothing }}
    {% endblock %}
{% endembed %}
BAR
--TEMPLATE(foo.twig)--
{% block c1 %}{% endblock %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5
--TEST--
"embed" tag
--TEMPLATE--
FOO
{% embed "foo.twig" %}
    {% block c1 %}
        {{ parent() }}
        block1extended
    {% endblock %}
{% endembed %}

{% embed "foo.twig" %}
    {% block c1 %}
        {{ parent() }}
        block1extended
    {% endblock %}
{% endembed %}

BAR
--TEMPLATE(foo.twig)--
A
{% block c1 %}
    block1
{% endblock %}
B
{% block c2 %}
    block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
FOO

A
            block1

        block1extended
    B
    block2
C

A
            block1

        block1extended
    B
    block2
C
BAR
--TEST--
"embed" tag
--TEMPLATE--
{% embed "foo.twig" %}
    {% block c1 %}
        {{ parent() }}
        {% embed "foo.twig" %}
            {% block c1 %}
                {{ parent() }}
                block1extended
            {% endblock %}
        {% endembed %}

    {% endblock %}
{% endembed %}
--TEMPLATE(foo.twig)--
A
{% block c1 %}
    block1
{% endblock %}
B
{% block c2 %}
    block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
A
            block1

        
A
                    block1

                block1extended
            B
    block2
C
    B
    block2
C
--TEST--
"embed" tag
--TEMPLATE--
{% extends "base.twig" %}

{% block c1 %}
    {{ parent() }}
    blockc1baseextended
{% endblock %}

{% block c2 %}
    {{ parent() }}

    {% embed "foo.twig" %}
        {% block c1 %}
            {{ parent() }}
            block1extended
        {% endblock %}
    {% endembed %}
{% endblock %}
--TEMPLATE(base.twig)--
A
{% block c1 %}
    blockc1base
{% endblock %}
{% block c2 %}
    blockc2base
{% endblock %}
B
--TEMPLATE(foo.twig)--
A
{% block c1 %}
    block1
{% endblock %}
B
{% block c2 %}
    block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
A
        blockc1base

    blockc1baseextended
        blockc2base


    
A
                block1

            block1extended
        B
    block2
CB--TEST--
"filter" tag applies a filter on its children
--TEMPLATE--
{% filter upper %}
Some text with a {{ var }}
{% endfilter %}
--DATA--
return array('var' => 'var')
--EXPECT--
SOME TEXT WITH A VAR
--TEST--
"filter" tag applies a filter on its children
--TEMPLATE--
{% filter json_encode|raw %}test{% endfilter %}
--DATA--
return array()
--EXPECT--
"test"
--TEST--
"filter" tags accept multiple chained filters
--TEMPLATE--
{% filter lower|title %}
  {{ var }}
{% endfilter %}
--DATA--
return array('var' => 'VAR')
--EXPECT--
    Var
--TEST--
"filter" tags can be nested at will
--TEMPLATE--
{% filter lower|title %}
  {{ var }}
  {% filter upper %}
    {{ var }}
  {% endfilter %}
  {{ var }}
{% endfilter %}
--DATA--
return array('var' => 'var')
--EXPECT--
  Var
      Var
    Var
--TEST--
"filter" tag applies the filter on "for" tags
--TEMPLATE--
{% filter upper %}
{% for item in items %}
{{ item }}
{% endfor %}
{% endfilter %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
A
B
--TEST--
"filter" tag applies the filter on "if" tags
--TEMPLATE--
{% filter upper %}
{% if items %}
{{ items|join(', ') }}
{% endif %}

{% if items.3 is defined %}
FOO
{% else %}
{{ items.1 }}
{% endif %}

{% if items.3 is defined %}
FOO
{% elseif items.1 %}
{{ items.0 }}
{% endif %}

{% endfilter %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
A, B

B

A
--TEST--
"for" tag takes a condition
--TEMPLATE--
{% for i in 1..5 if i is odd -%}
    {{ loop.index }}.{{ i }}{{ foo.bar }}
{% endfor %}
--DATA--
return array('foo' => array('bar' => 'X'))
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
1.1X
2.3X
3.5X
--TEST--
"for" tag keeps the context safe
--TEMPLATE--
{% for item in items %}
  {% for item in items %}
    * {{ item }}
  {% endfor %}
  * {{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
      * a
      * b
    * a
      * a
      * b
    * b
--TEST--
"for" tag can use an "else" clause
--TEMPLATE--
{% for item in items %}
  * {{ item }}
{% else %}
  no item
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
  * a
  * b
--DATA--
return array('items' => array())
--EXPECT--
  no item
--DATA--
return array()
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
  no item
--TEST--
"for" tag does not reset inner variables
--TEMPLATE--
{% for i in 1..2 %}
  {% for j in 0..2 %}
    {{k}}{% set k = k+1 %} {{ loop.parent.loop.index }}
  {% endfor %}
{% endfor %}
--DATA--
return array('k' => 0)
--EXPECT--
      0 1
      1 1
      2 1
        3 2
      4 2
      5 2
--TEST--
"for" tag can iterate over keys and values
--TEMPLATE--
{% for key, item in items %}
  * {{ key }}/{{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
  * 0/a
  * 1/b
--TEST--
"for" tag can iterate over keys
--TEMPLATE--
{% for key in items|keys %}
  * {{ key }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
  * 0
  * 1
--TEST--
"for" tag adds a loop variable to the context locally
--TEMPLATE--
{% for item in items %}
{% endfor %}
{% if loop is not defined %}WORKS{% endif %}
--DATA--
return array('items' => array())
--EXPECT--
WORKS
--TEST--
"for" tag adds a loop variable to the context
--TEMPLATE--
{% for item in items %}
  * {{ loop.index }}/{{ loop.index0 }}
  * {{ loop.revindex }}/{{ loop.revindex0 }}
  * {{ loop.first }}/{{ loop.last }}/{{ loop.length }}

{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
  * 1/0
  * 2/1
  * 1//2

  * 2/1
  * 1/0
  * /1/2
--TEST--
"for" tag
--TEMPLATE--
{% for i, item in items if loop.last > 0 %}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXCEPTION--
Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2
--TEST--
"for" tag
--TEMPLATE--
{% for i, item in items if i > 0 %}
    {{ loop.last }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXCEPTION--
Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3
--TEST--
"for" tag can use an "else" clause
--TEMPLATE--
{% for item in items %}
  {% for item in items1 %}
    * {{ item }}
  {% else %}
    no {{ item }}
  {% endfor %}
{% else %}
  no item1
{% endfor %}
--DATA--
return array('items' => array('a', 'b'), 'items1' => array())
--EXPECT--
no a
        no b
--TEST--
"for" tag iterates over iterable and countable objects
--TEMPLATE--
{% for item in items %}
  * {{ item }}
  * {{ loop.index }}/{{ loop.index0 }}
  * {{ loop.revindex }}/{{ loop.revindex0 }}
  * {{ loop.first }}/{{ loop.last }}/{{ loop.length }}

{% endfor %}

{% for key, value in items %}
  * {{ key }}/{{ value }}
{% endfor %}

{% for key in items|keys %}
  * {{ key }}
{% endfor %}
--DATA--
class ItemsIteratorCountable implements Iterator, Countable
{
  protected $values = array('foo' => 'bar', 'bar' => 'foo');
  public function current() { return current($this->values); }
  public function key() { return key($this->values); }
  public function next() { return next($this->values); }
  public function rewind() { return reset($this->values); }
  public function valid() { return false !== current($this->values); }
  public function count() { return count($this->values); }
}
return array('items' => new ItemsIteratorCountable())
--EXPECT--
  * bar
  * 1/0
  * 2/1
  * 1//2

  * foo
  * 2/1
  * 1/0
  * /1/2


  * foo/bar
  * bar/foo

  * foo
  * bar
--TEST--
"for" tag iterates over iterable objects
--TEMPLATE--
{% for item in items %}
  * {{ item }}
  * {{ loop.index }}/{{ loop.index0 }}
  * {{ loop.first }}

{% endfor %}

{% for key, value in items %}
  * {{ key }}/{{ value }}
{% endfor %}

{% for key in items|keys %}
  * {{ key }}
{% endfor %}
--DATA--
class ItemsIterator implements Iterator
{
  protected $values = array('foo' => 'bar', 'bar' => 'foo');
  public function current() { return current($this->values); }
  public function key() { return key($this->values); }
  public function next() { return next($this->values); }
  public function rewind() { return reset($this->values); }
  public function valid() { return false !== current($this->values); }
}
return array('items' => new ItemsIterator())
--EXPECT--
  * bar
  * 1/0
  * 1

  * foo
  * 2/1
  * 


  * foo/bar
  * bar/foo

  * foo
  * bar
--TEST--
"for" tags can be nested
--TEMPLATE--
{% for key, item in items %}
* {{ key }} ({{ loop.length }}):
{% for value in item %}
  * {{ value }} ({{ loop.length }})
{% endfor %}
{% endfor %}
--DATA--
return array('items' => array('a' => array('a1', 'a2', 'a3'), 'b' => array('b1')))
--EXPECT--
* a (2):
  * a1 (3)
  * a2 (3)
  * a3 (3)
* b (2):
  * b1 (1)
--TEST--
"for" tag iterates over item values
--TEMPLATE--
{% for item in items %}
  * {{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
  * a
  * b
--TEST--
global variables
--TEMPLATE--
{% include "included.twig" %}
{% from "included.twig" import foobar %}
{{ foobar() }}
--TEMPLATE(included.twig)--
{% macro foobar() %}
called foobar
{% endmacro %}
--DATA--
return array();
--EXPECT--
called foobar
--TEST--
"if" creates a condition
--TEMPLATE--
{% if a is defined %}
  {{ a }}
{% elseif b is defined %}
  {{ b }}
{% else %}
  NOTHING
{% endif %}
--DATA--
return array('a' => 'a')
--EXPECT--
  a
--DATA--
return array('b' => 'b')
--EXPECT--
  b
--DATA--
return array()
--EXPECT--
  NOTHING
--TEST--
"if" takes an expression as a test
--TEMPLATE--
{% if a < 2 %}
  A1
{% elseif a > 10 %}
  A2
{% else %}
  A3
{% endif %}
--DATA--
return array('a' => 1)
--EXPECT--
  A1
--DATA--
return array('a' => 12)
--EXPECT--
  A2
--DATA--
return array('a' => 7)
--EXPECT--
  A3
--TEST--
"include" tag
--TEMPLATE--
FOO
{% include "foo.twig" %}

BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO

FOOBAR
BAR
--TEST--
"include" tag allows expressions for the template to include
--TEMPLATE--
FOO
{% include foo %}

BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO

FOOBAR
BAR
--TEST--
"include" tag
--TEMPLATE--
{% include ["foo.twig", "bar.twig"] ignore missing %}
{% include "foo.twig" ignore missing %}
{% include "foo.twig" ignore missing with {} %}
{% include "foo.twig" ignore missing with {} only %}
--DATA--
return array()
--EXPECT--
--TEST--
"include" tag
--TEMPLATE--
{% extends "base.twig" %}

{% block content %}
    {{ parent() }}
{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}
    {% include "foo.twig" %}
{% endblock %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
--TEST--
"include" tag
--TEMPLATE--
{% include "foo.twig" %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
--TEST--
"include" tag accept variables and only
--TEMPLATE--
{% include "foo.twig" %}
{% include "foo.twig" only %}
{% include "foo.twig" with {'foo1': 'bar'} %}
{% include "foo.twig" with {'foo1': 'bar'} only %}
--TEMPLATE(foo.twig)--
{% for k, v in _context %}{{ k }},{% endfor %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
foo,global,_parent,
global,_parent,
foo,global,foo1,_parent,
foo1,global,_parent,
--TEST--
"include" tag accepts Twig_Template instance
--TEMPLATE--
{% include foo %} FOO
--TEMPLATE(foo.twig)--
BAR
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BAR FOO
--TEST--
"include" tag
--TEMPLATE--
{% include ["foo.twig", "bar.twig"] %}
{% include ["bar.twig", "foo.twig"] %}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
--TEST--
"include" tag accept variables
--TEMPLATE--
{% include "foo.twig" with {'foo': 'bar'} %}
{% include "foo.twig" with vars %}
--TEMPLATE(foo.twig)--
{{ foo }}
--DATA--
return array('vars' => array('foo' => 'bar'))
--EXPECT--
bar
bar
--TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}

{% block content %}
FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array()
--EXPECT--
FOO
--TEST--
block_expr2
--TEMPLATE--
{% extends "base2.twig" %}

{% block element -%}
    Element:
    {{- parent() -}}
{% endblock %}
--TEMPLATE(base2.twig)--
{% extends "base.twig" %}
--TEMPLATE(base.twig)--
{% spaceless %}
{% block element -%}
    <div>
        {%- if item.children is defined %}
            {%- for item in item.children %}
                {{- block('element') -}}
            {% endfor %}
        {%- endif -%}
    </div>
{%- endblock %}
{% endspaceless %}
--DATA--
return array(
    'item' => array(
        'children' => array(
            null,
            null,
        )
    )
)
--EXPECT--
Element:<div>Element:<div></div>Element:<div></div></div>
--TEST--
block_expr
--TEMPLATE--
{% extends "base.twig" %}

{% block element -%}
    Element:
    {{- parent() -}}
{% endblock %}
--TEMPLATE(base.twig)--
{% spaceless %}
{% block element -%}
    <div>
        {%- if item.children is defined %}
            {%- for item in item.children %}
                {{- block('element') -}}
            {% endfor %}
        {%- endif -%}
    </div>
{%- endblock %}
{% endspaceless %}
--DATA--
return array(
    'item' => array(
        'children' => array(
            null,
            null,
        )
    )
)
--EXPECT--
Element:<div>Element:<div></div>Element:<div></div></div>
--TEST--
"extends" tag
--TEMPLATE--
{% extends standalone ? foo : 'bar.twig' %}

{% block content %}{{ parent() }}FOO{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}FOO{% endblock %}
--TEMPLATE(bar.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array('foo' => 'foo.twig', 'standalone' => true)
--EXPECT--
FOOFOO
--TEST--
"extends" tag
--TEMPLATE--
{% extends foo %}

{% block content %}
FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO
--TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
--TEMPLATE(foo.twig)--
{% block content %}FOO{% endblock %}
--DATA--
return array()
--EXPECT--
FOO
--TEST--
"extends" tag
--TEMPLATE--
{% extends ["foo.twig", "bar.twig"] %}
--TEMPLATE(bar.twig)--
{% block content %}
foo
{% endblock %}
--DATA--
return array()
--EXPECT--
foo
--TEST--
"extends" tag
--TEMPLATE--
{% extends "layout.twig" %}{% block content %}{{ parent() }}index {% endblock %}
--TEMPLATE(layout.twig)--
{% extends "base.twig" %}{% block content %}{{ parent() }}layout {% endblock %}
--TEMPLATE(base.twig)--
{% block content %}base {% endblock %}
--DATA--
return array()
--EXPECT--
base layout index
--TEST--
"block" tag
--TEMPLATE--
{% block content %}
    CONTENT
    {%- block subcontent -%}
        SUBCONTENT
    {%- endblock -%}
    ENDCONTENT
{% endblock %}
--TEMPLATE(foo.twig)--
--DATA--
return array()
--EXPECT--
CONTENTSUBCONTENTENDCONTENT
--TEST--
"block" tag
--TEMPLATE--
{% extends "foo.twig" %}

{% block content %}
    {% block subcontent %}
        {% block subsubcontent %}
            SUBSUBCONTENT
        {% endblock %}
    {% endblock %}
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}
    {% block subcontent %}
        SUBCONTENT
    {% endblock %}
{% endblock %}
--DATA--
return array()
--EXPECT--
SUBSUBCONTENT
--TEST--
"extends" tag
--TEMPLATE--
{% extends "layout.twig" %}
{% block inside %}INSIDE{% endblock inside %}
--TEMPLATE(layout.twig)--
{% extends "base.twig" %}
{% block body %}
    {% block inside '' %}
{% endblock body %}
--TEMPLATE(base.twig)--
{% block body '' %}
--DATA--
return array()
--EXPECT--
INSIDE
--TEST--
"extends" tag
--TEMPLATE--
{% extends foo ? 'foo.twig' : 'bar.twig' %}
--TEMPLATE(foo.twig)--
FOO
--TEMPLATE(bar.twig)--
BAR
--DATA--
return array('foo' => true)
--EXPECT--
FOO
--DATA--
return array('foo' => false)
--EXPECT--
BAR
--TEST--
"extends" tag
--TEMPLATE--
{% block content %}
    {% extends "foo.twig" %}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3
--TEST--
"extends" tag
--TEMPLATE--
{% extends "base.twig" %}
{% block content %}{% include "included.twig" %}{% endblock %}

{% block footer %}Footer{% endblock %}
--TEMPLATE(included.twig)--
{% extends "base.twig" %}
{% block content %}Included Content{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}Default Content{% endblock %}

{% block footer %}Default Footer{% endblock %}
--DATA--
return array()
--EXPECT--
Included Content
Default Footer
Footer
--TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}

{% block content %}
  {% block inside %}
    INSIDE OVERRIDDEN
  {% endblock %}

  BEFORE
  {{ parent() }}
  AFTER
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}
  BAR
{% endblock %}
--DATA--
return array()
--EXPECT--

INSIDE OVERRIDDEN
  
  BEFORE
    BAR

  AFTER
--TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}

{% block content %}{{ parent() }}FOO{{ parent() }}{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array()
--EXPECT--
BARFOOBAR
--TEST--
"parent" tag
--TEMPLATE--
{% use 'foo.twig' %}

{% block content %}
    {{ parent() }}
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array()
--EXPECT--
BAR
--TEST--
"parent" tag
--TEMPLATE--
{% block content %}
    {{ parent() }}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3
--TEST--
"extends" tag accepts Twig_Template instance
--TEMPLATE--
{% extends foo %}

{% block content %}
{{ parent() }}FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BARFOO
--TEST--
"parent" function
--TEMPLATE--
{% extends "parent.twig" %}

{% use "use1.twig" %}
{% use "use2.twig" %}

{% block content_parent %}
    {{ parent() }}
{% endblock %}

{% block content_use1 %}
    {{ parent() }}
{% endblock %}

{% block content_use2 %}
    {{ parent() }}
{% endblock %}

{% block content %}
    {{ block('content_use1_only') }}
    {{ block('content_use2_only') }}
{% endblock %}
--TEMPLATE(parent.twig)--
{% block content_parent 'content_parent' %}
{% block content_use1 'content_parent' %}
{% block content_use2 'content_parent' %}
{% block content '' %}
--TEMPLATE(use1.twig)--
{% block content_use1 'content_use1' %}
{% block content_use2 'content_use1' %}
{% block content_use1_only 'content_use1_only' %}
--TEMPLATE(use2.twig)--
{% block content_use2 'content_use2' %}
{% block content_use2_only 'content_use2_only' %}
--DATA--
return array()
--EXPECT--
    content_parent
    content_use1
    content_use2
    content_use1_only
    content_use2_only
--TEST--
"macro" tag
--TEMPLATE--
{% import _self as macros %}

{{ macros.input('username') }}
{{ macros.input('password', null, 'password', 1) }}

{% macro input(name, value, type, size) %}
  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
{% endmacro %}
--DATA--
return array()
--EXPECT--
  <input type="text" name="username" value="" size="20">

  <input type="password" name="password" value="" size="1">
--TEST--
"macro" tag supports name for endmacro
--TEMPLATE--
{% import _self as macros %}

{{ macros.foo() }}
{{ macros.bar() }}

{% macro foo() %}foo{% endmacro %}
{% macro bar() %}bar{% endmacro bar %}
--DATA--
return array()
--EXPECT--
foo
bar

--TEST--
"macro" tag
--TEMPLATE--
{% import 'forms.twig' as forms %}

{{ forms.input('username') }}
{{ forms.input('password', null, 'password', 1) }}
--TEMPLATE(forms.twig)--
{% macro input(name, value, type, size) %}
  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
{% endmacro %}
--DATA--
return array()
--EXPECT--
  <input type="text" name="username" value="" size="20">

  <input type="password" name="password" value="" size="1">
--TEST--
"macro" tag
--TEMPLATE--
{% from 'forms.twig' import foo %}
{% from 'forms.twig' import foo as foobar, bar %}

{{ foo('foo') }}
{{ foobar('foo') }}
{{ bar('foo') }}
--TEMPLATE(forms.twig)--
{% macro foo(name) %}foo{{ name }}{% endmacro %}
{% macro bar(name) %}bar{{ name }}{% endmacro %}
--DATA--
return array()
--EXPECT--
foofoo
foofoo
barfoo
--TEST--
"macro" tag
--TEMPLATE--
{% from 'forms.twig' import foo %}

{{ foo('foo') }}
{{ foo() }}
--TEMPLATE(forms.twig)--
{% macro foo(name) %}{{ name|default('foo') }}{{ global }}{% endmacro %}
--DATA--
return array()
--EXPECT--
fooglobal
fooglobal
--TEST--
"macro" tag
--TEMPLATE--
{% import _self as forms %}

{{ forms.input('username') }}
{{ forms.input('password', null, 'password', 1) }}

{% macro input(name, value, type, size) %}
  <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
{% endmacro %}
--DATA--
return array()
--EXPECT--
  <input type="text" name="username" value="" size="20">

  <input type="password" name="password" value="" size="1">
--TEST--
"raw" tag
--TEMPLATE--
{% raw %}
{{ foo }}
{% endraw %}
--DATA--
return array()
--EXPECT--
{{ foo }}
--TEST--
"raw" tag
--TEMPLATE--
{% raw %}
{{ foo }}
{% endverbatim %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2
--TEST--
"raw" tag
--TEMPLATE--
1***

{%- raw %}
    {{ 'bla' }}
{% endraw %}

1***
2***

{%- raw -%}
    {{ 'bla' }}
{% endraw %}

2***
3***

{%- raw -%}
    {{ 'bla' }}
{% endraw -%}

3***
4***

{%- raw -%}
    {{ 'bla' }}
{%- endraw %}

4***
5***

{%- raw -%}
    {{ 'bla' }}
{%- endraw -%}

5***
--DATA--
return array()
--EXPECT--
1***
    {{ 'bla' }}


1***
2***{{ 'bla' }}


2***
3***{{ 'bla' }}
3***
4***{{ 'bla' }}

4***
5***{{ 'bla' }}5***
--TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
    {%- include "foo.twig" %}
    a
{%- endsandbox %}
--TEMPLATE(foo.twig)--
foo
--EXCEPTION--
Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4
--TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
    {%- include "foo.twig" %}

    {% if 1 %}
        {%- include "foo.twig" %}
    {% endif %}
{%- endsandbox %}
--TEMPLATE(foo.twig)--
foo
--EXCEPTION--
Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5
--TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
    {%- include "foo.twig" %}
{%- endsandbox %}

{%- sandbox %}
    {%- include "foo.twig" %}
    {%- include "foo.twig" %}
{%- endsandbox %}

{%- sandbox %}{% include "foo.twig" %}{% endsandbox %}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
foo
foo
--TEST--
"set" tag
--TEMPLATE--
{% set foo = 'foo' %}
{% set bar = 'foo<br />' %}

{{ foo }}
{{ bar }}

{% set foo, bar = 'foo', 'bar' %}

{{ foo }}{{ bar }}
--DATA--
return array()
--EXPECT--
foo
foo&lt;br /&gt;


foobar
--TEST--
"set" tag block empty capture
--TEMPLATE--
{% set foo %}{% endset %}

{% if foo %}FAIL{% endif %}
--DATA--
return array()
--EXPECT--
--TEST--
"set" tag block capture
--TEMPLATE--
{% set foo %}f<br />o<br />o{% endset %}

{{ foo }}
--DATA--
return array()
--EXPECT--
f<br />o<br />o
--TEST--
"set" tag
--TEMPLATE--
{% set foo, bar = 'foo' ~ 'bar', 'bar' ~ 'foo' %}

{{ foo }}
{{ bar }}
--DATA--
return array()
--EXPECT--
foobar
barfoo
--TEST--
"spaceless" tag removes whites between HTML tags
--TEMPLATE--
{% spaceless %}

    <div>   <div>   foo   </div>   </div>

{% endspaceless %}
--DATA--
return array()
--EXPECT--
<div><div>   foo   </div></div>
--TEST--
"§" custom tag
--TEMPLATE--
{% § %}
--DATA--
return array()
--EXPECT--
§
--TEST--
Whitespace trimming on tags.
--TEMPLATE--
{{ 5 * '{#-'|length }}
{{ '{{-'|length * 5 + '{%-'|length }}

Trim on control tag:
{% for i in range(1, 9) -%}
	{{ i }}
{%- endfor %}


Trim on output tag:
{% for i in range(1, 9) %}
	{{- i -}}
{% endfor %}


Trim comments:
      
{#- Invisible -#}
       
After the comment.

Trim leading space:
{% if leading %}

		{{- leading }}
{% endif %}

{%- if leading %}
	{{- leading }}

{%- endif %}


Trim trailing space:
{% if trailing -%}          
	{{ trailing -}}

{% endif -%}

Combined:

{%- if both -%}
<ul>
	<li>    {{- both -}}   </li>
</ul>

{%- endif -%}

end
--DATA--
return array('leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both')
--EXPECT--
15
18

Trim on control tag:
123456789

Trim on output tag:
123456789

Trim comments:After the comment.

Trim leading space:
leading space
leading space

Trim trailing space:
trailing spaceCombined:<ul>
	<li>both</li>
</ul>end
--TEST--
"use" tag
--TEMPLATE--
{% use "blocks.twig" with content as foo %}

{{ block('foo') }}
--TEMPLATE(blocks.twig)--
{% block content 'foo' %}
--DATA--
return array()
--EXPECT--
foo
--TEST--
"use" tag
--TEMPLATE--
{% use "blocks.twig" %}

{{ block('content') }}
--TEMPLATE(blocks.twig)--
{% block content 'foo' %}
--DATA--
return array()
--EXPECT--
foo
--TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}
--TEMPLATE(foo.twig)--
{% use "bar.twig" %}
--TEMPLATE(bar.twig)--
--DATA--
return array()
--EXPECT--
--TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}

{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
--TEMPLATE(foo.twig)--
{% use "bar.twig" %}

{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
foo
foo
bar
--TEST--
"use" tag
--TEMPLATE--
{% use "ancestor.twig" %}
{% use "parent.twig" %}

{{ block('container') }}
--TEMPLATE(parent.twig)--
{% block sub_container %}
    <div class="overriden_sub_container">overriden sub_container</div>
{% endblock %}
--TEMPLATE(ancestor.twig)--
{% block container %}
    <div class="container">{{ block('sub_container') }}</div>
{% endblock %}

{% block sub_container %}
    <div class="sub_container">sub_container</div>
{% endblock %}
--DATA--
return array()
--EXPECT--
<div class="container">    <div class="overriden_sub_container">overriden sub_container</div>
</div>
--TEST--
"use" tag
--TEMPLATE--
{% use "parent.twig" %}

{{ block('container') }}
--TEMPLATE(parent.twig)--
{% use "ancestor.twig" %}

{% block sub_container %}
    <div class="overriden_sub_container">overriden sub_container</div>
{% endblock %}
--TEMPLATE(ancestor.twig)--
{% block container %}
    <div class="container">{{ block('sub_container') }}</div>
{% endblock %}

{% block sub_container %}
    <div class="sub_container">sub_container</div>
{% endblock %}
--DATA--
return array()
--EXPECT--
<div class="container">    <div class="overriden_sub_container">overriden sub_container</div>
</div>
--TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" with content as foo_content %}
{% use "bar.twig" %}

{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
{{ block('foo_content') }}
--TEMPLATE(foo.twig)--
{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
bar
foo
bar
foo
--TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}
{% use "bar.twig" %}

{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
--TEMPLATE(foo.twig)--
{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
bar
foo
bar
--TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig'%}
{% block foobar %}
    {{- parent() -}}
    Content of block (second override)
{% endblock foobar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' %}
{% block foobar %}
    {{- parent() -}}
    Content of block (first override)
{% endblock foobar %}
--TEMPLATE(file1.html.twig)--
{% block foobar -%}
    Content of block
{% endblock foobar %}
--DATA--
return array()
--EXPECT--
Content of block
Content of block (first override)
Content of block (second override)
--TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig' %}
{% use 'file1.html.twig' with foo %}
{% block foo %}
    {{- parent() -}}
    Content of foo (second override)
{% endblock foo %}
{% block bar %}
    {{- parent() -}}
    Content of bar (second override)
{% endblock bar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' %}
{% block foo %}
    {{- parent() -}}
    Content of foo (first override)
{% endblock foo %}
{% block bar %}
    {{- parent() -}}
    Content of bar (first override)
{% endblock bar %}
--TEMPLATE(file1.html.twig)--
{% block foo -%}
    Content of foo
{% endblock foo %}
{% block bar -%}
    Content of bar
{% endblock bar %}
--DATA--
return array()
--EXPECT--
Content of foo
Content of foo (first override)
Content of foo (second override)
Content of bar
Content of bar (second override)
--TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig' with foobar as base_base_foobar %}
{% block foobar %}
    {{- block('base_base_foobar') -}}
    Content of block (second override)
{% endblock foobar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' with foobar as base_foobar %}
{% block foobar %}
    {{- block('base_foobar') -}}
    Content of block (first override)
{% endblock foobar %}
--TEMPLATE(file1.html.twig)--
{% block foobar -%}
    Content of block
{% endblock foobar %}
--DATA--
return array()
--EXPECT--
Content of block
Content of block (first override)
Content of block (second override)
--TEST--
"verbatim" tag
--TEMPLATE--
{% verbatim %}
{{ foo }}
{% endverbatim %}
--DATA--
return array()
--EXPECT--
{{ foo }}
--TEST--
"verbatim" tag
--TEMPLATE--
{% verbatim %}
{{ foo }}
{% endraw %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2
--TEST--
"verbatim" tag
--TEMPLATE--
1***

{%- verbatim %}
    {{ 'bla' }}
{% endverbatim %}

1***
2***

{%- verbatim -%}
    {{ 'bla' }}
{% endverbatim %}

2***
3***

{%- verbatim -%}
    {{ 'bla' }}
{% endverbatim -%}

3***
4***

{%- verbatim -%}
    {{ 'bla' }}
{%- endverbatim %}

4***
5***

{%- verbatim -%}
    {{ 'bla' }}
{%- endverbatim -%}

5***
--DATA--
return array()
--EXPECT--
1***
    {{ 'bla' }}


1***
2***{{ 'bla' }}


2***
3***{{ 'bla' }}
3***
4***{{ 'bla' }}

4***
5***{{ 'bla' }}5***
--TEST--
array index test
--TEMPLATE--
{% for key, value in days %}
{{ key }}
{% endfor %}
--DATA--
return array('days' => array(
    1  => array('money' => 9),
    2  => array('money' => 21),
    3  => array('money' => 38),
    4  => array('money' => 6),
    18 => array('money' => 6),
    19 => array('money' => 3),
    31 => array('money' => 11),
));
--EXPECT--
1
2
3
4
18
19
31
--TEST--
"const" test
--TEMPLATE--
{{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }}
{{ 'bar' is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
{{ value is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
{{ 2 is constant('ARRAY_AS_PROPS', object) ? 'ok' : 'no' }}
--DATA--
return array('value' => 'bar', 'object' => new ArrayObject(array('hi')));
--EXPECT--
ok
ok
ok
ok--TEST--
"defined" test
--TEMPLATE--
{{ definedVar                     is     defined ? 'ok' : 'ko' }}
{{ definedVar                     is not defined ? 'ko' : 'ok' }}
{{ undefinedVar                   is     defined ? 'ko' : 'ok' }}
{{ undefinedVar                   is not defined ? 'ok' : 'ko' }}
{{ zeroVar                        is     defined ? 'ok' : 'ko' }}
{{ nullVar                        is     defined ? 'ok' : 'ko' }}
{{ nested.definedVar              is     defined ? 'ok' : 'ko' }}
{{ nested['definedVar']           is     defined ? 'ok' : 'ko' }}
{{ nested.definedVar              is not defined ? 'ko' : 'ok' }}
{{ nested.undefinedVar            is     defined ? 'ko' : 'ok' }}
{{ nested['undefinedVar']         is     defined ? 'ko' : 'ok' }}
{{ nested.undefinedVar            is not defined ? 'ok' : 'ko' }}
{{ nested.zeroVar                 is     defined ? 'ok' : 'ko' }}
{{ nested.nullVar                 is     defined ? 'ok' : 'ko' }}
{{ nested.definedArray.0          is     defined ? 'ok' : 'ko' }}
{{ nested['definedArray'][0]      is     defined ? 'ok' : 'ko' }}
{{ object.foo                     is     defined ? 'ok' : 'ko' }}
{{ object.undefinedMethod         is     defined ? 'ko' : 'ok' }}
{{ object.getFoo()                is     defined ? 'ok' : 'ko' }}
{{ object.getFoo('a')             is     defined ? 'ok' : 'ko' }}
{{ object.undefinedMethod()       is     defined ? 'ko' : 'ok' }}
{{ object.undefinedMethod('a')    is     defined ? 'ko' : 'ok' }}
{{ object.self.foo                is     defined ? 'ok' : 'ko' }}
{{ object.self.undefinedMethod    is     defined ? 'ko' : 'ok' }}
{{ object.undefinedMethod.self    is     defined ? 'ko' : 'ok' }}
--DATA--
return array(
    'definedVar' => 'defined',
    'zeroVar'    => 0,
    'nullVar'    => null,
    'nested'      => array(
        'definedVar'   => 'defined',
        'zeroVar'      => 0,
        'nullVar'      => null,
        'definedArray' => array(0),
    ),
    'object' => new TwigTestFoo(),
);
--EXPECT--
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
--DATA--
return array(
    'definedVar' => 'defined',
    'zeroVar'    => 0,
    'nullVar'    => null,
    'nested'      => array(
        'definedVar'   => 'defined',
        'zeroVar'      => 0,
        'nullVar'      => null,
        'definedArray' => array(0),
    ),
    'object' => new TwigTestFoo(),
);
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
--TEST--
"empty" test
--TEMPLATE--
{{ foo is empty ? 'ok' : 'ko' }}
{{ bar is empty ? 'ok' : 'ko' }}
{{ foobar is empty ? 'ok' : 'ko' }}
{{ array is empty ? 'ok' : 'ko' }}
{{ zero is empty ? 'ok' : 'ko' }}
{{ string is empty ? 'ok' : 'ko' }}
{{ countable_empty is empty ? 'ok' : 'ko' }}
{{ countable_not_empty is empty ? 'ok' : 'ko' }}
{{ markup_empty is empty ? 'ok' : 'ko' }}
{{ markup_not_empty is empty ? 'ok' : 'ko' }}
--DATA--

class CountableStub implements Countable
{
    private $items;

    public function __construct(array $items)
    {
        $this->items = $items;
    }

    public function count()
    {
        return count($this->items);
    }
}
return array(
    'foo' => '', 'bar' => null, 'foobar' => false, 'array' => array(), 'zero' => 0, 'string' => '0',
    'countable_empty' => new CountableStub(array()), 'countable_not_empty' => new CountableStub(array(1, 2)),
    'markup_empty' => new Twig_Markup('', 'UTF-8'), 'markup_not_empty' => new Twig_Markup('test', 'UTF-8'),
);
--EXPECT--
ok
ok
ok
ok
ko
ko
ok
ko
ok
ko
--TEST--
"even" test
--TEMPLATE--
{{ 1 is even ? 'ko' : 'ok' }}
{{ 2 is even ? 'ok' : 'ko' }}
{{ 1 is not even ? 'ok' : 'ko' }}
{{ 2 is not even ? 'ko' : 'ok' }}
--DATA--
return array()
--EXPECT--
ok
ok
ok
ok
--TEST--
Twig supports the in operator
--TEMPLATE--
{% if bar in foo %}
TRUE
{% endif %}
{% if not (bar in foo) %}
{% else %}
TRUE
{% endif %}
{% if bar not in foo %}
{% else %}
TRUE
{% endif %}
{% if 'a' in bar %}
TRUE
{% endif %}
{% if 'c' not in bar %}
TRUE
{% endif %}
{% if '' not in bar %}
TRUE
{% endif %}
{% if '' in '' %}
TRUE
{% endif %}
{% if '0' not in '' %}
TRUE
{% endif %}
{% if 'a' not in '0' %}
TRUE
{% endif %}
{% if '0' in '0' %}
TRUE
{% endif %}
{{ false in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ true in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ '0' in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ '' in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ 0 in ['', 1] ? 'TRUE' : 'FALSE' }}
{{ '' in 'foo' ? 'TRUE' : 'FALSE' }}
{{ 0 in 'foo' ? 'TRUE' : 'FALSE' }}
{{ false in 'foo' ? 'TRUE' : 'FALSE' }}
{{ true in '100' ? 'TRUE' : 'FALSE' }}
{{ [] in 'Array' ? 'TRUE' : 'FALSE' }}
{{ [] in [true, false] ? 'TRUE' : 'FALSE' }}
{{ [] in [true, ''] ? 'TRUE' : 'FALSE' }}
{{ [] in [true, []] ? 'TRUE' : 'FALSE' }}
{{ dir_object in 'foo'~dir_name ? 'TRUE' : 'FALSE' }}
{{ 5 in 125 ? 'TRUE' : 'FALSE' }}
--DATA--
return array('bar' => 'bar', 'foo' => array('bar' => 'bar'), 'dir_name' => dirname(__FILE__), 'dir_object' => new SplFileInfo(dirname(__FILE__)))
--EXPECT--
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
FALSE
FALSE
FALSE
FALSE
FALSE
TRUE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
TRUE
FALSE
FALSE
--TEST--
Twig supports the in operator when using objects
--TEMPLATE--
{% if object in object_list %}
TRUE
{% endif %}
--DATA--
$foo = new TwigTestFoo();
$foo1 = new TwigTestFoo();

$foo->position = $foo1;
$foo1->position = $foo;

return array(
    'object'      => $foo,
    'object_list' => array($foo1, $foo),
);
--EXPECT--
TRUE
--TEST--
"iterable" test
--TEMPLATE--
{{ foo is iterable ? 'ok' : 'ko' }}
{{ traversable is iterable ? 'ok' : 'ko' }}
{{ obj is iterable ? 'ok' : 'ko' }}
{{ val is iterable ? 'ok' : 'ko' }}
--DATA--
return array(
    'foo' => array(),
    'traversable' => new ArrayIterator(array()),
    'obj' => new stdClass(),
    'val' => 'test',
);
--EXPECT--
ok
ok
ko
ko--TEST--
"odd" test
--TEMPLATE--
{{ 1 is odd ? 'ok' : 'ko' }}
{{ 2 is odd ? 'ko' : 'ok' }}
--DATA--
return array()
--EXPECT--
ok
ok
