2016-12-27 08:10:47 -08:00

4613 lines
88 KiB
Plaintext

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