4613 lines
88 KiB
Plaintext
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--
|
|
<br />
|
|
--TEST--
|
|
"escape" filter
|
|
--TEMPLATE--
|
|
{{ "愛していますか? <br />"|e }}
|
|
--DATA--
|
|
return array()
|
|
--EXPECT--
|
|
愛していますか? <br />
|
|
--TEST--
|
|
"escape" filter
|
|
--TEMPLATE--
|
|
{{ "foo <br />"|e }}
|
|
--DATA--
|
|
return array()
|
|
--EXPECT--
|
|
foo <br />
|
|
--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<br />
|
|
\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 <strong>HTML</strong><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 <strong>HTML</strong> 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&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&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&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&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<br />
|
|
--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--
|
|
<br /><br />
|
|
<br /><br />
|
|
<br /><br />
|
|
<br /><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--
|
|
<br />
|
|
--TEST--
|
|
"autoescape" tag does not double-escape
|
|
--TEMPLATE--
|
|
{% autoescape 'html' %}
|
|
{{ var|escape }}
|
|
{% endautoescape %}
|
|
--DATA--
|
|
return array('var' => '<br />')
|
|
--EXPECT--
|
|
<br />
|
|
--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
|
|
<br />
|
|
|
|
unsafe_br()|raw
|
|
<br />
|
|
|
|
safe_br()|escape
|
|
<br />
|
|
|
|
safe_br()|raw
|
|
<br />
|
|
|
|
unsafe_br()|escape
|
|
<br />
|
|
|
|
|
|
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
|
|
<br />
|
|
|
|
4. Nested conditionals with only literals
|
|
<br />
|
|
|
|
5. Nested conditionals with a variable
|
|
<br />
|
|
|
|
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--
|
|
<br />
|
|
<br />
|
|
<br />
|
|
<br />
|
|
<br />
|
|
<br />
|
|
<br />
|
|
--TEST--
|
|
"autoescape" tag applies escaping to object method calls
|
|
--TEMPLATE--
|
|
{% autoescape 'html' %}
|
|
{{ user.name }}
|
|
{{ user.name|lower }}
|
|
{{ user }}
|
|
{% endautoescape %}
|
|
--EXPECT--
|
|
Fabien<br />
|
|
fabien<br />
|
|
Fabien<br />
|
|
--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
|
|
<br />"
|
|
\x3Cbr\x20\x2F\x3E\x22
|
|
<br />"
|
|
--TEST--
|
|
escape types
|
|
--TEMPLATE--
|
|
|
|
1. autoescape 'html' |escape('js')
|
|
|
|
{% autoescape 'html' %}
|
|
<a onclick="alert("{{ msg|escape('js') }}")"></a>
|
|
{% endautoescape %}
|
|
|
|
2. autoescape 'html' |escape('js')
|
|
|
|
{% autoescape 'html' %}
|
|
<a onclick="alert("{{ msg|escape('js') }}")"></a>
|
|
{% endautoescape %}
|
|
|
|
3. autoescape 'js' |escape('js')
|
|
|
|
{% autoescape 'js' %}
|
|
<a onclick="alert("{{ msg|escape('js') }}")"></a>
|
|
{% endautoescape %}
|
|
|
|
4. no escape
|
|
|
|
{% autoescape false %}
|
|
<a onclick="alert("{{ msg }}")"></a>
|
|
{% endautoescape %}
|
|
|
|
5. |escape('js')|escape('html')
|
|
|
|
{% autoescape false %}
|
|
<a onclick="alert("{{ msg|escape('js')|escape('html') }}")"></a>
|
|
{% endautoescape %}
|
|
|
|
6. autoescape 'html' |escape('js')|escape('html')
|
|
|
|
{% autoescape 'html' %}
|
|
<a onclick="alert("{{ msg|escape('js')|escape('html') }}")"></a>
|
|
{% endautoescape %}
|
|
|
|
--DATA--
|
|
return array('msg' => "<>\n'\"")
|
|
--EXPECT--
|
|
|
|
1. autoescape 'html' |escape('js')
|
|
|
|
<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a>
|
|
|
|
2. autoescape 'html' |escape('js')
|
|
|
|
<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a>
|
|
|
|
3. autoescape 'js' |escape('js')
|
|
|
|
<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a>
|
|
|
|
4. no escape
|
|
|
|
<a onclick="alert("<>
|
|
'"")"></a>
|
|
|
|
5. |escape('js')|escape('html')
|
|
|
|
<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a>
|
|
|
|
6. autoescape 'html' |escape('js')|escape('html')
|
|
|
|
<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></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--
|
|
<Fabien><br />
|
|
Twig
|
|
<Fabien><br />
|
|
Twig
|
|
<Fabien><br />
|
|
Twig
|
|
<Fabien><br />
|
|
Twig
|
|
<Fabien><br />
|
|
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 )
|
|
<Fabien><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 )
|
|
<Fabien><br />
|
|
Twig
|
|
|
|
3. Explicit escape
|
|
( var is escaped by |escape_and_nl2br, line-breaks are added,
|
|
the output is explicitly escaped by |escape )
|
|
&lt;Fabien&gt;<br />
|
|
Twig
|
|
|
|
4. Escape non-escaper filter output
|
|
( var is upper-cased by |upper,
|
|
the output is auto-escaped )
|
|
<FABIEN>
|
|
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 )
|
|
&LT;FABIEN&GT;<BR />
|
|
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 )
|
|
<FABIEN><br />
|
|
TWIG
|
|
|
|
7. Escape if last filter is not an escaper
|
|
( the output of |format is "<b>" ~ var ~ "</b>",
|
|
the output is auto-escaped )
|
|
<b><Fabien>
|
|
Twig</b>
|
|
|
|
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><Fabien>
|
|
Twig</b>
|
|
|
|
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 )
|
|
<Fabien><br />
|
|
Twig
|
|
|
|
2. Don't double-pre-escape
|
|
( var|escape|nl2br )
|
|
<Fabien><br />
|
|
Twig
|
|
|
|
3. Don't escape safe values
|
|
( var|raw|nl2br )
|
|
<Fabien><br />
|
|
Twig
|
|
|
|
4. Don't escape safe values
|
|
( var|escape|nl2br|nl2br )
|
|
<Fabien><br /><br />
|
|
Twig
|
|
|
|
5. Re-escape values that are escaped for an other contexts
|
|
( var|escape_something|escape|nl2br )
|
|
<FABIEN><br />
|
|
TWIG
|
|
|
|
6. Still escape when using filters not declared safe
|
|
( var|escape|nl2br|upper|escape )
|
|
&LT;FABIEN&GT;<BR />
|
|
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 )
|
|
<FABIEN>
|
|
TWIG
|
|
|
|
2. Safe values are still safe
|
|
( var|escape|preserves_safety )
|
|
<FABIEN>
|
|
TWIG
|
|
|
|
3. Re-escape values that are escaped for an other contexts
|
|
( var|escape_something|preserves_safety|escape )
|
|
<FABIEN>
|
|
TWIG
|
|
|
|
4. Still escape when using filters not declared safe
|
|
( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
|
|
&LT;FABPOT&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<br />
|
|
|
|
|
|
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
|