
After go1.16, go will use module mode by default, even when the repository is checked out under GOPATH or in a one-off directory. Add go.mod, go.sum to keep this repo buildable without opting out of the module mode. > go mod init github.com/mmcgrana/gobyexample > go mod tidy > go mod vendor In module mode, the 'vendor' directory is special and its contents will be actively maintained by the go command. pygments aren't the dependency the go will know about, so it will delete the contents from vendor directory. Move it to `third_party` directory now. And, vendor the blackfriday package. Note: the tutorial contents are not affected by the change in go1.16 because all the examples in this tutorial ask users to run the go command with the explicit list of files to be compiled (e.g. `go run hello-world.go` or `go build command-line-arguments.go`). When the source list is provided, the go command does not have to compute the build list and whether it's running in GOPATH mode or module mode becomes irrelevant.
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
|