Hana 9e216da9ef go.mod: add go.mod and move pygments to third_party
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.
2021-02-15 16:45:26 -05:00

4613 lines
88 KiB
Plaintext

From the Twig test suite, https://github.com/fabpot/Twig, available under BSD license.
--TEST--
Exception for an unclosed tag
--TEMPLATE--
{% block foo %}
{% if foo %}
{% for i in fo %}
{% endfor %}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Unexpected tag name "endblock" (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16
--TEST--
Exception for an undefined trait
--TEMPLATE--
{% use 'foo' with foobar as bar %}
--TEMPLATE(foo)--
{% block bar %}
{% endblock %}
--EXCEPTION--
Twig_Error_Runtime: Block "foobar" is not defined in trait "foo" in "index.twig".
--TEST--
Twig supports method calls
--TEMPLATE--
{{ items.foo }}
{{ items['foo'] }}
{{ items[foo] }}
{{ items[items[foo]] }}
--DATA--
return array('foo' => 'bar', 'items' => array('foo' => 'bar', 'bar' => 'foo'))
--EXPECT--
bar
bar
foo
bar
--TEST--
Twig supports array notation
--TEMPLATE--
{# empty array #}
{{ []|join(',') }}
{{ [1, 2]|join(',') }}
{{ ['foo', "bar"]|join(',') }}
{{ {0: 1, 'foo': 'bar'}|join(',') }}
{{ {0: 1, 'foo': 'bar'}|keys|join(',') }}
{{ {0: 1, foo: 'bar'}|join(',') }}
{{ {0: 1, foo: 'bar'}|keys|join(',') }}
{# nested arrays #}
{% set a = [1, 2, [1, 2], {'foo': {'foo': 'bar'}}] %}
{{ a[2]|join(',') }}
{{ a[3]["foo"]|join(',') }}
{# works even if [] is used inside the array #}
{{ [foo[bar]]|join(',') }}
{# elements can be any expression #}
{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }}
{# arrays can have a trailing , like in PHP #}
{{
[
1,
2,
]|join(',')
}}
{# keys can be any expression #}
{% set a = 1 %}
{% set b = "foo" %}
{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %}
{{ ary|keys|join(',') }}
{{ ary|join(',') }}
--DATA--
return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
--EXPECT--
1,2
foo,bar
1,bar
0,foo
1,bar
0,foo
1,2
bar
bar
FOO,BAR,
1,2
1,foo,c,1foo
a,b,c,d
--TEST--
Twig supports binary operations (+, -, *, /, ~, %, and, or)
--TEMPLATE--
{{ 1 + 1 }}
{{ 2 - 1 }}
{{ 2 * 2 }}
{{ 2 / 2 }}
{{ 3 % 2 }}
{{ 1 and 1 }}
{{ 1 and 0 }}
{{ 0 and 1 }}
{{ 0 and 0 }}
{{ 1 or 1 }}
{{ 1 or 0 }}
{{ 0 or 1 }}
{{ 0 or 0 }}
{{ 0 or 1 and 0 }}
{{ 1 or 0 and 1 }}
{{ "foo" ~ "bar" }}
{{ foo ~ "bar" }}
{{ "foo" ~ bar }}
{{ foo ~ bar }}
{{ 20 // 7 }}
--DATA--
return array('foo' => 'bar', 'bar' => 'foo')
--EXPECT--
2
1
4
1
1
1
1
1
1
1
foobar
barbar
foofoo
barfoo
2
--TEST--
Twig supports bitwise operations
--TEMPLATE--
{{ 1 b-and 5 }}
{{ 1 b-or 5 }}
{{ 1 b-xor 5 }}
{{ (1 and 0 b-or 0) is same as(1 and (0 b-or 0)) ? 'ok' : 'ko' }}
--DATA--
return array()
--EXPECT--
1
5
4
ok
--TEST--
Twig supports comparison operators (==, !=, <, >, >=, <=)
--TEMPLATE--
{{ 1 > 2 }}/{{ 1 > 1 }}/{{ 1 >= 2 }}/{{ 1 >= 1 }}
{{ 1 < 2 }}/{{ 1 < 1 }}/{{ 1 <= 2 }}/{{ 1 <= 1 }}
{{ 1 == 1 }}/{{ 1 == 2 }}
{{ 1 != 1 }}/{{ 1 != 2 }}
--DATA--
return array()
--EXPECT--
///1
1//1/1
1/
/1
--TEST--
Twig supports the "divisible by" operator
--TEMPLATE--
{{ 8 is divisible by(2) ? 'OK' }}
{{ 8 is not divisible by(3) ? 'OK' }}
{{ 8 is divisible by (2) ? 'OK' }}
{{ 8 is not
divisible
by
(3) ? 'OK' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
--TEST--
Twig supports the .. operator
--TEMPLATE--
{% for i in 0..10 %}{{ i }} {% endfor %}
{% for letter in 'a'..'z' %}{{ letter }} {% endfor %}
{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %}
{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %}
{% for i in 0 + 1 .. 10 - 1 %}{{ i }} {% endfor %}
--DATA--
return array('foo' => array(1, 10))
--EXPECT--
0 1 2 3 4 5 6 7 8 9 10
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9
--TEST--
Twig supports the "ends with" operator
--TEMPLATE--
{{ 'foo' ends with 'o' ? 'OK' : 'KO' }}
{{ not ('foo' ends with 'f') ? 'OK' : 'KO' }}
{{ not ('foo' ends with 'foowaytoolong') ? 'OK' : 'KO' }}
{{ 'foo' ends with '' ? 'OK' : 'KO' }}
{{ '1' ends with true ? 'OK' : 'KO' }}
{{ 1 ends with true ? 'OK' : 'KO' }}
{{ 0 ends with false ? 'OK' : 'KO' }}
{{ '' ends with false ? 'OK' : 'KO' }}
{{ false ends with false ? 'OK' : 'KO' }}
{{ false ends with '' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
KO
KO
KO
KO
KO
KO
--TEST--
Twig supports grouping of expressions
--TEMPLATE--
{{ (2 + 2) / 2 }}
--DATA--
return array()
--EXPECT--
2
--TEST--
Twig supports literals
--TEMPLATE--
1 {{ true }}
2 {{ TRUE }}
3 {{ false }}
4 {{ FALSE }}
5 {{ none }}
6 {{ NONE }}
7 {{ null }}
8 {{ NULL }}
--DATA--
return array()
--EXPECT--
1 1
2 1
3
4
5
6
7
8
--TEST--
Twig supports __call() for attributes
--TEMPLATE--
{{ foo.foo }}
{{ foo.bar }}
--EXPECT--
foo_from_call
bar_from_getbar
--TEST--
Twig supports the "matches" operator
--TEMPLATE--
{{ 'foo' matches '/o/' ? 'OK' : 'KO' }}
{{ 'foo' matches '/^fo/' ? 'OK' : 'KO' }}
{{ 'foo' matches '/O/i' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
--TEST--
Twig supports method calls
--TEMPLATE--
{{ items.foo.foo }}
{{ items.foo.getFoo() }}
{{ items.foo.bar }}
{{ items.foo['bar'] }}
{{ items.foo.bar('a', 43) }}
{{ items.foo.bar(foo) }}
{{ items.foo.self.foo() }}
{{ items.foo.is }}
{{ items.foo.in }}
{{ items.foo.not }}
--DATA--
return array('foo' => 'bar', 'items' => array('foo' => new TwigTestFoo(), 'bar' => 'foo'))
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
foo
foo
bar
bar_a-43
bar_bar
foo
is
in
not
--TEST--
Twig allows to use named operators as variable names
--TEMPLATE--
{% for match in matches %}
{{- match }}
{% endfor %}
{{ in }}
{{ is }}
--DATA--
return array('matches' => array(1, 2, 3), 'in' => 'in', 'is' => 'is')
--EXPECT--
1
2
3
in
is
--TEST--
Twig parses postfix expressions
--TEMPLATE--
{% import _self as macros %}
{% macro foo() %}foo{% endmacro %}
{{ 'a' }}
{{ 'a'|upper }}
{{ ('a')|upper }}
{{ -1|upper }}
{{ macros.foo() }}
{{ (macros).foo() }}
--DATA--
return array();
--EXPECT--
a
A
A
-1
foo
foo
--TEST--
Twig supports the "same as" operator
--TEMPLATE--
{{ 1 is same as(1) ? 'OK' }}
{{ 1 is not same as(true) ? 'OK' }}
{{ 1 is same as(1) ? 'OK' }}
{{ 1 is not same as(true) ? 'OK' }}
{{ 1 is same as (1) ? 'OK' }}
{{ 1 is not
same
as
(true) ? 'OK' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
OK
OK
--TEST--
Twig supports the "starts with" operator
--TEMPLATE--
{{ 'foo' starts with 'f' ? 'OK' : 'KO' }}
{{ not ('foo' starts with 'oo') ? 'OK' : 'KO' }}
{{ not ('foo' starts with 'foowaytoolong') ? 'OK' : 'KO' }}
{{ 'foo' starts with 'f' ? 'OK' : 'KO' }}
{{ 'foo' starts
with 'f' ? 'OK' : 'KO' }}
{{ 'foo' starts with '' ? 'OK' : 'KO' }}
{{ '1' starts with true ? 'OK' : 'KO' }}
{{ '' starts with false ? 'OK' : 'KO' }}
{{ 'a' starts with false ? 'OK' : 'KO' }}
{{ false starts with '' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
OK
OK
KO
KO
KO
KO
--TEST--
Twig supports string interpolation
--TEMPLATE--
{# "foo #{"foo #{bar} baz"} baz" #}
{# "foo #{bar}#{bar} baz" #}
--DATA--
return array('bar' => 'BAR');
--EXPECT--
foo foo BAR baz baz
foo BARBAR baz
--TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 1 ? 'YES' }}
{{ 0 ? 'YES' }}
--DATA--
return array()
--EXPECT--
YES
--TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 'YES' ?: 'NO' }}
{{ 0 ?: 'NO' }}
--DATA--
return array()
--EXPECT--
YES
NO
--TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 1 ? 'YES' : 'NO' }}
{{ 0 ? 'YES' : 'NO' }}
{{ 0 ? 'YES' : (1 ? 'YES1' : 'NO1') }}
{{ 0 ? 'YES' : (0 ? 'YES1' : 'NO1') }}
{{ 1 == 1 ? 'foo<br />':'' }}
{{ foo ~ (bar ? ('-' ~ bar) : '') }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--EXPECT--
YES
NO
YES1
NO1
foo<br />
foo-bar
--TEST--
Twig does not allow to use two-word named operators as variable names
--TEMPLATE--
{{ starts with }}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected token "operator" of value "starts with" in "index.twig" at line 2
--TEST--
Twig unary operators precedence
--TEMPLATE--
{{ -1 - 1 }}
{{ -1 - -1 }}
{{ -1 * -1 }}
{{ 4 / -1 * 5 }}
--DATA--
return array()
--EXPECT--
-2
0
1
-20
--TEST--
Twig supports unary operators (not, -, +)
--TEMPLATE--
{{ not 1 }}/{{ not 0 }}
{{ +1 + 1 }}/{{ -1 - 1 }}
{{ not (false or true) }}
--DATA--
return array()
--EXPECT--
/1
2/-2
--TEST--
"abs" filter
--TEMPLATE--
{{ (-5.5)|abs }}
{{ (-5)|abs }}
{{ (-0)|abs }}
{{ 0|abs }}
{{ 5|abs }}
{{ 5.5|abs }}
{{ number1|abs }}
{{ number2|abs }}
{{ number3|abs }}
{{ number4|abs }}
{{ number5|abs }}
{{ number6|abs }}
--DATA--
return array('number1' => -5.5, 'number2' => -5, 'number3' => -0, 'number4' => 0, 'number5' => 5, 'number6' => 5.5)
--EXPECT--
5.5
5
0
0
5
5.5
5.5
5
0
0
5
5.5
--TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3.1) %}
<div class=row>
{% for column in row %}
<div class=item>{{ column }}</div>
{% endfor %}
</div>
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
<div class=row>
<div class=item>a</div>
<div class=item>b</div>
<div class=item>c</div>
<div class=item>d</div>
</div>
<div class=row>
<div class=item>e</div>
<div class=item>f</div>
<div class=item>g</div>
<div class=item>h</div>
</div>
<div class=row>
<div class=item>i</div>
<div class=item>j</div>
</div>
--TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3) %}
<div class=row>
{% for column in row %}
<div class=item>{{ column }}</div>
{% endfor %}
</div>
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
<div class=row>
<div class=item>a</div>
<div class=item>b</div>
<div class=item>c</div>
</div>
<div class=row>
<div class=item>d</div>
<div class=item>e</div>
<div class=item>f</div>
</div>
<div class=row>
<div class=item>g</div>
<div class=item>h</div>
<div class=item>i</div>
</div>
<div class=row>
<div class=item>j</div>
</div>
--TEST--
"batch" filter
--TEMPLATE--
<table>
{% for row in items|batch(3, '') %}
<tr>
{% for column in row %}
<td>{{ column }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
<table>
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
<tr>
<td>d</td>
<td>e</td>
<td>f</td>
</tr>
<tr>
<td>g</td>
<td>h</td>
<td>i</td>
</tr>
<tr>
<td>j</td>
<td></td>
<td></td>
</tr>
</table>
--TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3, 'fill') %}
<div class=row>
{% for column in row %}
<div class=item>{{ column }}</div>
{% endfor %}
</div>
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'))
--EXPECT--
<div class=row>
<div class=item>a</div>
<div class=item>b</div>
<div class=item>c</div>
</div>
<div class=row>
<div class=item>d</div>
<div class=item>e</div>
<div class=item>f</div>
</div>
<div class=row>
<div class=item>g</div>
<div class=item>h</div>
<div class=item>i</div>
</div>
<div class=row>
<div class=item>j</div>
<div class=item>k</div>
<div class=item>l</div>
</div>
--TEST--
"batch" filter
--TEMPLATE--
<table>
{% for row in items|batch(3, 'fill') %}
<tr>
{% for column in row %}
<td>{{ column }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
<table>
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
<tr>
<td>d</td>
<td>e</td>
<td>f</td>
</tr>
<tr>
<td>g</td>
<td>h</td>
<td>i</td>
</tr>
<tr>
<td>j</td>
<td>fill</td>
<td>fill</td>
</tr>
</table>
--TEST--
"convert_encoding" filter
--CONDITION--
function_exists('iconv') || function_exists('mb_convert_encoding')
--TEMPLATE--
{{ "愛していますか?"|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }}
--DATA--
return array()
--EXPECT--
愛していますか?
--TEST--
"date" filter (interval support as of PHP 5.3)
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
{{ date2|date }}
{{ date2|date('%d days') }}
--DATA--
date_default_timezone_set('UTC');
$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
return array(
'date2' => new DateInterval('P2D'),
)
--EXPECT--
2 days 0 hours
2 days
--TEST--
"date" filter
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
--DATA--
date_default_timezone_set('UTC');
$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
return array(
'date1' => mktime(13, 45, 0, 10, 4, 2010),
)
--EXPECT--
2010-10-04
04/10/2010
--TEST--
"date" filter
--CONDITION--
version_compare(phpversion(), '5.5.0', '>=')
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s', timezone1) }}
{{ date1|date('d/m/Y H:i:s') }}
{{ date2|date('d/m/Y H:i:s P', 'Europe/Paris') }}
{{ date2|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date2|date('d/m/Y H:i:s P', false) }}
{{ date2|date('e', 'Europe/Paris') }}
{{ date2|date('e', false) }}
--DATA--
date_default_timezone_set('Europe/Paris');
return array(
'date1' => new DateTimeImmutable('2010-10-04 13:45'),
'date2' => new DateTimeImmutable('2010-10-04 13:45', new DateTimeZone('America/New_York')),
'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 07:45:00
04/10/2010 13:45:00
04/10/2010 19:45:00 +02:00
05/10/2010 01:45:00 +08:00
04/10/2010 13:45:00 -04:00
Europe/Paris
America/New_York
--TEST--
"date" filter (interval support as of PHP 5.3)
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
{{ date1|date }}
{{ date1|date('%d days %h hours') }}
{{ date1|date('%d days %h hours', timezone1) }}
--DATA--
date_default_timezone_set('UTC');
return array(
'date1' => new DateInterval('P2D'),
// This should have no effect on DateInterval formatting
'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
2 days
2 days 0 hours
2 days 0 hours
--TEST--
"date_modify" filter
--TEMPLATE--
{{ date1|date_modify('-1day')|date('Y-m-d H:i:s') }}
{{ date2|date_modify('-1day')|date('Y-m-d H:i:s') }}
--DATA--
date_default_timezone_set('UTC');
return array(
'date1' => '2010-10-04 13:45',
'date2' => new DateTime('2010-10-04 13:45'),
)
--EXPECT--
2010-10-03 13:45:00
2010-10-03 13:45:00
--TEST--
"date" filter
--TEMPLATE--
{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }}
{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }}
{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }}
--DATA--
date_default_timezone_set('UTC');
return array('date' => mktime(13, 45, 0, 10, 4, 2010))
--EXPECT--
04/10/2010 08:45:00 -05:00
04/10/2010 08:45:00 -05:00
04/10/2010 08:45:00 -05:00
--TEST--
"date" filter
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s P', 'America/Chicago') }}
{{ date1|date('e') }}
{{ date1|date('d/m/Y H:i:s') }}
{{ date2|date }}
{{ date2|date('d/m/Y') }}
{{ date2|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date2|date('d/m/Y H:i:s', timezone1) }}
{{ date2|date('d/m/Y H:i:s') }}
{{ date3|date }}
{{ date3|date('d/m/Y') }}
{{ date4|date }}
{{ date4|date('d/m/Y') }}
{{ date5|date }}
{{ date5|date('d/m/Y') }}
{{ date6|date('d/m/Y H:i:s P', 'Europe/Paris') }}
{{ date6|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date6|date('d/m/Y H:i:s P', false) }}
{{ date6|date('e', 'Europe/Paris') }}
{{ date6|date('e', false) }}
{{ date7|date }}
--DATA--
date_default_timezone_set('Europe/Paris');
return array(
'date1' => mktime(13, 45, 0, 10, 4, 2010),
'date2' => new DateTime('2010-10-04 13:45'),
'date3' => '2010-10-04 13:45',
'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
'date6' => new DateTime('2010-10-04 13:45', new DateTimeZone('America/New_York')),
'date7' => '2010-01-28T15:00:00+05:00',
'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 19:45:00 +08:00
04/10/2010 06:45:00 -05:00
Europe/Paris
04/10/2010 13:45:00
October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 07:45:00
04/10/2010 13:45:00
October 4, 2010 13:45
04/10/2010
October 4, 2010 15:45
04/10/2010
January 2, 1964 04:04
02/01/1964
04/10/2010 19:45:00 +02:00
05/10/2010 01:45:00 +08:00
04/10/2010 13:45:00 -04:00
Europe/Paris
America/New_York
January 28, 2010 11:00
--TEST--
"default" filter
--TEMPLATE--
Variable:
{{ definedVar |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ zeroVar |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ emptyVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nullVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ undefinedVar |default('default') is same as('default') ? 'ok' : 'ko' }}
Array access:
{{ nested.definedVar |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested['definedVar'] |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested.zeroVar |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested.emptyVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.nullVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.undefinedVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested['undefinedVar'] |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ undefinedVar.foo |default('default') is same as('default') ? 'ok' : 'ko' }}
Plain values:
{{ 'defined' |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ 0 |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ '' |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ null |default('default') is same as('default') ? 'ok' : 'ko' }}
Precedence:
{{ 'o' ~ nullVar |default('k') }}
{{ 'o' ~ nested.nullVar |default('k') }}
Object methods:
{{ object.foo |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.getFoo() |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.getFoo('a') |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.undefinedMethod() |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.undefinedMethod('a') |default('default') is same as('default') ? 'ok' : 'ko' }}
Deep nested:
{{ nested.undefinedVar.foo.bar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.definedArray.0 |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested['definedArray'][0] |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.self.foo |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.self.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.undefinedMethod.self |default('default') is same as('default') ? 'ok' : 'ko' }}
--DATA--
return array(
'definedVar' => 'defined',
'zeroVar' => 0,
'emptyVar' => '',
'nullVar' => null,
'nested' => array(
'definedVar' => 'defined',
'zeroVar' => 0,
'emptyVar' => '',
'nullVar' => null,
'definedArray' => array(0),
),
'object' => new TwigTestFoo(),
)
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
Variable:
ok
ok
ok
ok
ok
Array access:
ok
ok
ok
ok
ok
ok
ok
ok
Plain values:
ok
ok
ok
ok
Precedence:
ok
ok
Object methods:
ok
ok
ok
ok
ok
ok
Deep nested:
ok
ok
ok
ok
ok
ok
--DATA--
return array(
'definedVar' => 'defined',
'zeroVar' => 0,
'emptyVar' => '',
'nullVar' => null,
'nested' => array(
'definedVar' => 'defined',
'zeroVar' => 0,
'emptyVar' => '',
'nullVar' => null,
'definedArray' => array(0),
),
'object' => new TwigTestFoo(),
)
--CONFIG--
return array('strict_variables' => true)
--EXPECT--
Variable:
ok
ok
ok
ok
ok
Array access:
ok
ok
ok
ok
ok
ok
ok
ok
Plain values:
ok
ok
ok
ok
Precedence:
ok
ok
Object methods:
ok
ok
ok
ok
ok
ok
Deep nested:
ok
ok
ok
ok
ok
ok
--TEST--
dynamic filter
--TEMPLATE--
{{ 'bar'|foo_path }}
{{ 'bar'|a_foo_b_bar }}
--DATA--
return array()
--EXPECT--
foo/bar
a/b/bar
--TEST--
"escape" filter does not escape with the html strategy when using the html_attr strategy
--TEMPLATE--
{{ '<br />'|escape('html_attr') }}
--DATA--
return array()
--EXPECT--
&lt;br&#x20;&#x2F;&gt;
--TEST--
"escape" filter
--TEMPLATE--
{{ "愛していますか? <br />"|e }}
--DATA--
return array()
--EXPECT--
愛していますか? &lt;br /&gt;
--TEST--
"escape" filter
--TEMPLATE--
{{ "foo <br />"|e }}
--DATA--
return array()
--EXPECT--
foo &lt;br /&gt;
--TEST--
"first" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|first }}
{{ {a: 1, b: 2, c: 3, d: 4}|first }}
{{ '1234'|first }}
{{ arr|first }}
{{ 'Ä€é'|first }}
{{ ''|first }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
1
1
1
1
Ä
--TEST--
"escape" filter
--TEMPLATE--
{% set foo %}
foo<br />
{% endset %}
{{ foo|e('html') -}}
{{ foo|e('js') }}
{% autoescape true %}
{{ foo }}
{% endautoescape %}
--DATA--
return array()
--EXPECT--
foo&lt;br /&gt;
\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A
foo<br />
--TEST--
"format" filter
--TEMPLATE--
{{ string|format(foo, 3) }}
--DATA--
return array('string' => '%s/%d', 'foo' => 'bar')
--EXPECT--
bar/3
--TEST--
"join" filter
--TEMPLATE--
{{ ["foo", "bar"]|join(', ') }}
{{ foo|join(', ') }}
{{ bar|join(', ') }}
--DATA--
return array('foo' => new TwigTestFoo(), 'bar' => new ArrayObject(array(3, 4)))
--EXPECT--
foo, bar
1, 2
3, 4
--TEST--
"json_encode" filter
--TEMPLATE--
{{ "foo"|json_encode|raw }}
{{ foo|json_encode|raw }}
{{ [foo, "foo"]|json_encode|raw }}
--DATA--
return array('foo' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
"foo"
"foo"
["foo","foo"]
--TEST--
"last" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|last }}
{{ {a: 1, b: 2, c: 3, d: 4}|last }}
{{ '1234'|last }}
{{ arr|last }}
{{ 'Ä€é'|last }}
{{ ''|last }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
4
4
4
4
é
--TEST--
"length" filter
--TEMPLATE--
{{ array|length }}
{{ string|length }}
{{ number|length }}
{{ markup|length }}
--DATA--
return array('array' => array(1, 4), 'string' => 'foo', 'number' => 1000, 'markup' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
2
3
4
3
--TEST--
"length" filter
--CONDITION--
function_exists('mb_get_info')
--TEMPLATE--
{{ string|length }}
{{ markup|length }}
--DATA--
return array('string' => 'été', 'markup' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
3
3
--TEST--
"merge" filter
--TEMPLATE--
{{ items|merge({'bar': 'foo'})|join }}
{{ items|merge({'bar': 'foo'})|keys|join }}
{{ {'bar': 'foo'}|merge(items)|join }}
{{ {'bar': 'foo'}|merge(items)|keys|join }}
{{ numerics|merge([4, 5, 6])|join }}
--DATA--
return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3))
--EXPECT--
barfoo
foobar
foobar
barfoo
123456
--TEST--
"nl2br" filter
--TEMPLATE--
{{ "I like Twig.\nYou will like it too.\n\nEverybody like it!"|nl2br }}
{{ text|nl2br }}
--DATA--
return array('text' => "If you have some <strong>HTML</strong>\nit will be escaped.")
--EXPECT--
I like Twig.<br />
You will like it too.<br />
<br />
Everybody like it!
If you have some &lt;strong&gt;HTML&lt;/strong&gt;<br />
it will be escaped.
--TEST--
"number_format" filter with defaults.
--TEMPLATE--
{{ 20|number_format }}
{{ 20.25|number_format }}
{{ 20.25|number_format(1) }}
{{ 20.25|number_format(2, ',') }}
{{ 1020.25|number_format }}
{{ 1020.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',', '.') }}
--DATA--
$twig->getExtension('core')->setNumberFormat(2, '!', '=');
return array();
--EXPECT--
20!00
20!25
20!3
20,25
1=020!25
1=020,25
1.020,25
--TEST--
"number_format" filter
--TEMPLATE--
{{ 20|number_format }}
{{ 20.25|number_format }}
{{ 20.25|number_format(2) }}
{{ 20.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',', '.') }}
--DATA--
return array();
--EXPECT--
20
20
20.25
20,25
1,020,25
1.020,25
--TEST--
"replace" filter
--TEMPLATE--
{{ "I like %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }}
--DATA--
return array()
--EXPECT--
I like foo and bar.
--TEST--
"reverse" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|reverse|join('') }}
{{ '1234évènement'|reverse }}
{{ arr|reverse|join('') }}
{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }}
{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }}
{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
4321
tnemenèvé4321
4321
a,c
a,c
a,c
--TEST--
"round" filter
--TEMPLATE--
{{ 2.7|round }}
{{ 2.1|round }}
{{ 2.1234|round(3, 'floor') }}
{{ 2.1|round(0, 'ceil') }}
{{ 21.3|round(-1)}}
{{ 21.3|round(-1, 'ceil')}}
{{ 21.3|round(-1, 'floor')}}
--DATA--
return array()
--EXPECT--
3
2
2.123
3
20
30
20
--TEST--
"slice" filter
--TEMPLATE--
{{ [1, 2, 3, 4][1:2]|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }}
{{ [1, 2, 3, 4][start:length]|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2)|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2)|keys|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2, true)|keys|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|keys|join('') }}
{{ '1234'|slice(1, 2) }}
{{ '1234'[1:2] }}
{{ arr|slice(1, 2)|join('') }}
{{ arr[1:2]|join('') }}
{{ [1, 2, 3, 4]|slice(1)|join('') }}
{{ [1, 2, 3, 4][1:]|join('') }}
{{ '1234'|slice(1) }}
{{ '1234'[1:] }}
{{ '1234'[:1] }}
--DATA--
return array('start' => 1, 'length' => 2, 'arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
23
23
23
23
01
12
23
bc
23
23
23
23
234
234
234
234
1
--TEST--
"sort" filter
--TEMPLATE--
{{ array1|sort|join }}
{{ array2|sort|join }}
--DATA--
return array('array1' => array(4, 1), 'array2' => array('foo', 'bar'))
--EXPECT--
14
barfoo
--TEST--
"split" filter
--TEMPLATE--
{{ "one,two,three,four,five"|split(',')|join('-') }}
{{ foo|split(',')|join('-') }}
{{ foo|split(',', 3)|join('-') }}
{{ baz|split('')|join('-') }}
{{ baz|split('', 2)|join('-') }}
{{ foo|split(',', -2)|join('-') }}
--DATA--
return array('foo' => "one,two,three,four,five", 'baz' => '12345',)
--EXPECT--
one-two-three-four-five
one-two-three-four-five
one-two-three,four,five
1-2-3-4-5
12-34-5
one-two-three--TEST--
"trim" filter
--TEMPLATE--
{{ " I like Twig. "|trim }}
{{ text|trim }}
{{ " foo/"|trim("/") }}
--DATA--
return array('text' => " If you have some <strong>HTML</strong> it will be escaped. ")
--EXPECT--
I like Twig.
If you have some &lt;strong&gt;HTML&lt;/strong&gt; it will be escaped.
foo
--TEST--
"url_encode" filter for PHP < 5.4 and HHVM
--CONDITION--
defined('PHP_QUERY_RFC3986')
--TEMPLATE--
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
{{ {}|url_encode|default("default") }}
{{ 'spéßi%le%c0d@dspa ce'|url_encode }}
--DATA--
return array()
--EXPECT--
foo=bar&amp;number=3&amp;sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&amp;spa%20ce=
foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
default
sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce
--TEST--
"url_encode" filter
--CONDITION--
defined('PHP_QUERY_RFC3986')
--TEMPLATE--
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
{{ {}|url_encode|default("default") }}
{{ 'spéßi%le%c0d@dspa ce'|url_encode }}
--DATA--
return array()
--EXPECT--
foo=bar&amp;number=3&amp;sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&amp;spa%20ce=
foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
default
sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce
--TEST--
"attribute" function
--TEMPLATE--
{{ attribute(obj, method) }}
{{ attribute(array, item) }}
{{ attribute(obj, "bar", ["a", "b"]) }}
{{ attribute(obj, "bar", arguments) }}
{{ attribute(obj, method) is defined ? 'ok' : 'ko' }}
{{ attribute(obj, nonmethod) is defined ? 'ok' : 'ko' }}
--DATA--
return array('obj' => new TwigTestFoo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo', 'nonmethod' => 'xxx', 'arguments' => array('a', 'b'))
--EXPECT--
foo
bar
bar_a-b
bar_a-b
ok
ko
--TEST--
"block" function
--TEMPLATE--
{% extends 'base.twig' %}
{% block bar %}BAR{% endblock %}
--TEMPLATE(base.twig)--
{% block foo %}{{ block('bar') }}{% endblock %}
{% block bar %}BAR_BASE{% endblock %}
--DATA--
return array()
--EXPECT--
BARBAR
--TEST--
"constant" function
--TEMPLATE--
{{ constant('DATE_W3C') == expect ? 'true' : 'false' }}
{{ constant('ARRAY_AS_PROPS', object) }}
--DATA--
return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi')));
--EXPECT--
true
2
--TEST--
"cycle" function
--TEMPLATE--
{% for i in 0..6 %}
{{ cycle(array1, i) }}-{{ cycle(array2, i) }}
{% endfor %}
--DATA--
return array('array1' => array('odd', 'even'), 'array2' => array('apple', 'orange', 'citrus'))
--EXPECT--
odd-apple
even-orange
odd-citrus
even-apple
odd-orange
even-citrus
odd-apple
--TEST--
"date" function
--TEMPLATE--
{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }}
{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }}
--DATA--
date_default_timezone_set('UTC');
return array('date' => mktime(13, 45, 0, 10, 4, 2010))
--EXPECT--
04/10/2010 09:45:00 -04:00
04/10/2010 09:45:00 -04:00
--TEST--
"date" function
--TEMPLATE--
{{ date() == date('now') ? 'OK' : 'KO' }}
{{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }}
--DATA--
date_default_timezone_set('UTC');
return array(
'date1' => mktime(13, 45, 0, 10, 4, 2010),
'date2' => new DateTime('2010-10-04 13:45'),
'date3' => '2010-10-04 13:45',
'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
)
--EXPECT--
OK
OK
OK
OK
OK
OK
--TEST--
"dump" function, xdebug is not loaded or xdebug <2.2-dev is loaded
--CONDITION--
!extension_loaded('xdebug') || (($r = new ReflectionExtension('xdebug')) && version_compare($r->getVersion(), '2.2-dev', '<'))
--TEMPLATE--
{{ dump() }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--CONFIG--
return array('debug' => true, 'autoescape' => false);
--TEST--
"dump" function
--CONDITION--
!extension_loaded('xdebug')
--TEMPLATE--
{{ dump('foo') }}
{{ dump('foo', 'bar') }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--CONFIG--
return array('debug' => true, 'autoescape' => false);
--EXPECT--
string(3) "foo"
string(3) "foo"
string(3) "bar"
--TEST--
dynamic function
--TEMPLATE--
{{ foo_path('bar') }}
{{ a_foo_b_bar('bar') }}
--DATA--
return array()
--EXPECT--
foo/bar
a/b/bar
--TEST--
"include" function
--TEMPLATE--
{% set tmp = include("foo.twig") %}
FOO{{ tmp }}BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO
FOOBARBAR
--TEST--
"include" function is safe for auto-escaping
--TEMPLATE--
{{ include("foo.twig") }}
--TEMPLATE(foo.twig)--
<p>Test</p>
--DATA--
return array()
--EXPECT--
<p>Test</p>
--TEST--
"include" function
--TEMPLATE--
FOO
{{ include("foo.twig") }}
BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO
FOOBAR
BAR
--TEST--
"include" function allows expressions for the template to include
--TEMPLATE--
FOO
{{ include(foo) }}
BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO
FOOBAR
BAR
--TEST--
"include" function
--TEMPLATE--
{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }}
{{ include("foo.twig", ignore_missing = true) }}
{{ include("foo.twig", ignore_missing = true, variables = {}) }}
{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }}
--DATA--
return array()
--EXPECT--
--TEST--
"include" function
--TEMPLATE--
{% extends "base.twig" %}
{% block content %}
{{ parent() }}
{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}
{{ include("foo.twig") }}
{% endblock %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
--TEST--
"include" function
--TEMPLATE--
{{ include("foo.twig") }}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
--TEST--
"include" tag sandboxed
--TEMPLATE--
{{ include("foo.twig", sandboxed = true) }}
--TEMPLATE(foo.twig)--
{{ foo|e }}
--DATA--
return array()
--EXCEPTION--
Twig_Sandbox_SecurityError: Filter "e" is not allowed in "index.twig" at line 2.
--TEST--
"include" function accepts Twig_Template instance
--TEMPLATE--
{{ include(foo) }} FOO
--TEMPLATE(foo.twig)--
BAR
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BAR FOO
--TEST--
"include" function
--TEMPLATE--
{{ include(["foo.twig", "bar.twig"]) }}
{{- include(["bar.twig", "foo.twig"]) }}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
--TEST--
"include" function accept variables and with_context
--TEMPLATE--
{{ include("foo.twig") }}
{{- include("foo.twig", with_context = false) }}
{{- include("foo.twig", {'foo1': 'bar'}) }}
{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }}
--TEMPLATE(foo.twig)--
{% for k, v in _context %}{{ k }},{% endfor %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
foo,global,_parent,
global,_parent,
foo,global,foo1,_parent,
foo1,global,_parent,
--TEST--
"include" function accept variables
--TEMPLATE--
{{ include("foo.twig", {'foo': 'bar'}) }}
{{- include("foo.twig", vars) }}
--TEMPLATE(foo.twig)--
{{ foo }}
--DATA--
return array('vars' => array('foo' => 'bar'))
--EXPECT--
bar
bar
--TEST--
"max" function
--TEMPLATE--
{{ max([2, 1, 3, 5, 4]) }}
{{ max(2, 1, 3, 5, 4) }}
{{ max({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }}
--DATA--
return array()
--EXPECT--
5
5
two
--TEST--
"min" function
--TEMPLATE--
{{ min(2, 1, 3, 5, 4) }}
{{ min([2, 1, 3, 5, 4]) }}
{{ min({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }}
--DATA--
return array()
--EXPECT--
1
1
five
--TEST--
"range" function
--TEMPLATE--
{{ range(low=0+1, high=10+0, step=2)|join(',') }}
--DATA--
return array()
--EXPECT--
1,3,5,7,9
--TEST--
"block" function recursively called in a parent template
--TEMPLATE--
{% extends "ordered_menu.twig" %}
{% block label %}"{{ parent() }}"{% endblock %}
{% block list %}{% set class = 'b' %}{{ parent() }}{% endblock %}
--TEMPLATE(ordered_menu.twig)--
{% extends "menu.twig" %}
{% block list %}{% set class = class|default('a') %}<ol class="{{ class }}">{{ block('children') }}</ol>{% endblock %}
--TEMPLATE(menu.twig)--
{% extends "base.twig" %}
{% block list %}<ul>{{ block('children') }}</ul>{% endblock %}
{% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %}
{% block item %}<li>{% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %}</li>{% endblock %}
{% block label %}{{ item }}{{ block('unknown') }}{% endblock %}
--TEMPLATE(base.twig)--
{{ block('list') }}
--DATA--
return array('item' => array('1', '2', array('3.1', array('3.2.1', '3.2.2'), '3.4')))
--EXPECT--
<ol class="b"><li>"1"</li><li>"2"</li><li><ol class="b"><li>"3.1"</li><li><ol class="b"><li>"3.2.1"</li><li>"3.2.2"</li></ol></li><li>"3.4"</li></ol></li></ol>
--TEST--
"source" function
--TEMPLATE--
FOO
{{ source("foo.twig") }}
BAR
--TEMPLATE(foo.twig)--
{{ foo }}<br />
--DATA--
return array()
--EXPECT--
FOO
{{ foo }}<br />
BAR
--TEST--
"template_from_string" function
--TEMPLATE--
{% include template_from_string(template) %}
{% include template_from_string("Hello {{ name }}") %}
{% include template_from_string('{% extends "parent.twig" %}{% block content %}Hello {{ name }}{% endblock %}') %}
--TEMPLATE(parent.twig)--
{% block content %}{% endblock %}
--DATA--
return array('name' => 'Fabien', 'template' => "Hello {{ name }}")
--EXPECT--
Hello Fabien
Hello Fabien
Hello Fabien
--TEST--
macro
--TEMPLATE--
{% from _self import test %}
{% macro test(a, b = 'bar') -%}
{{ a }}{{ b }}
{%- endmacro %}
{{ test('foo') }}
{{ test('bar', 'foo') }}
--DATA--
return array();
--EXPECT--
foobar
barfoo
--TEST--
macro
--TEMPLATE--
{% import _self as macros %}
{% macro foo(data) %}
{{ data }}
{% endmacro %}
{% macro bar() %}
<br />
{% endmacro %}
{{ macros.foo(macros.bar()) }}
--DATA--
return array();
--EXPECT--
<br />
--TEST--
macro
--TEMPLATE--
{% from _self import test %}
{% macro test(this) -%}
{{ this }}
{%- endmacro %}
{{ test(this) }}
--DATA--
return array('this' => 'foo');
--EXPECT--
foo
--TEST--
macro
--TEMPLATE--
{% import _self as test %}
{% from _self import test %}
{% macro test(a, b) -%}
{{ a|default('a') }}<br />
{{- b|default('b') }}<br />
{%- endmacro %}
{{ test.test() }}
{{ test() }}
{{ test.test(1, "c") }}
{{ test(1, "c") }}
--DATA--
return array();
--EXPECT--
a<br />b<br />
a<br />b<br />
1<br />c<br />
1<br />c<br />
--TEST--
macro with a filter
--TEMPLATE--
{% import _self as test %}
{% macro test() %}
{% filter escape %}foo<br />{% endfilter %}
{% endmacro %}
{{ test.test() }}
--DATA--
return array();
--EXPECT--
foo&lt;br /&gt;
--TEST--
Twig outputs 0 nodes correctly
--TEMPLATE--
{{ foo }}0{{ foo }}
--DATA--
return array('foo' => 'foo')
--EXPECT--
foo0foo
--TEST--
error in twig extension
--TEMPLATE--
{{ object.region is not null ? object.regionChoices[object.region] }}
--EXPECT--
house.region.s
--TEST--
Twig is able to deal with SimpleXMLElement instances as variables
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
Hello '{{ images.image.0.group }}'!
{{ images.image.0.group.attributes.myattr }}
{{ images.children().image.count() }}
{% for image in images %}
- {{ image.group }}
{% endfor %}
--DATA--
return array('images' => new SimpleXMLElement('<images><image><group myattr="example">foo</group></image><image><group>bar</group></image></images>'))
--EXPECT--
Hello 'foo'!
example
2
- foo
- bar
--TEST--
Twig does not confuse strings with integers in getAttribute()
--TEMPLATE--
{{ hash['2e2'] }}
--DATA--
return array('hash' => array('2e2' => 'works'))
--EXPECT--
works
--TEST--
"autoescape" tag applies escaping on its children
--TEMPLATE--
{% autoescape %}
{{ var }}<br />
{% endautoescape %}
{% autoescape 'html' %}
{{ var }}<br />
{% endautoescape %}
{% autoescape false %}
{{ var }}<br />
{% endautoescape %}
{% autoescape true %}
{{ var }}<br />
{% endautoescape %}
{% autoescape false %}
{{ var }}<br />
{% endautoescape %}
--DATA--
return array('var' => '<br />')
--EXPECT--
&lt;br /&gt;<br />
&lt;br /&gt;<br />
<br /><br />
&lt;br /&gt;<br />
<br /><br />
--TEST--
"autoescape" tag applies escaping on embedded blocks
--TEMPLATE--
{% autoescape 'html' %}
{% block foo %}
{{ var }}
{% endblock %}
{% endautoescape %}
--DATA--
return array('var' => '<br />')
--EXPECT--
&lt;br /&gt;
--TEST--
"autoescape" tag does not double-escape
--TEMPLATE--
{% autoescape 'html' %}
{{ var|escape }}
{% endautoescape %}
--DATA--
return array('var' => '<br />')
--EXPECT--
&lt;br /&gt;
--TEST--
"autoescape" tag applies escaping after calling functions
--TEMPLATE--
autoescape false
{% autoescape false %}
safe_br
{{ safe_br() }}
unsafe_br
{{ unsafe_br() }}
{% endautoescape %}
autoescape 'html'
{% autoescape 'html' %}
safe_br
{{ safe_br() }}
unsafe_br
{{ unsafe_br() }}
unsafe_br()|raw
{{ (unsafe_br())|raw }}
safe_br()|escape
{{ (safe_br())|escape }}
safe_br()|raw
{{ (safe_br())|raw }}
unsafe_br()|escape
{{ (unsafe_br())|escape }}
{% endautoescape %}
autoescape js
{% autoescape 'js' %}
safe_br
{{ safe_br() }}
{% endautoescape %}
--DATA--
return array()
--EXPECT--
autoescape false
safe_br
<br />
unsafe_br
<br />
autoescape 'html'
safe_br
<br />
unsafe_br
&lt;br /&gt;
unsafe_br()|raw
<br />
safe_br()|escape
&lt;br /&gt;
safe_br()|raw
<br />
unsafe_br()|escape
&lt;br /&gt;
autoescape js
safe_br
\x3Cbr\x20\x2F\x3E
--TEST--
"autoescape" tag does not apply escaping on literals
--TEMPLATE--
{% autoescape 'html' %}
1. Simple literal
{{ "<br />" }}
2. Conditional expression with only literals
{{ true ? "<br />" : "<br>" }}
3. Conditional expression with a variable
{{ true ? "<br />" : someVar }}
4. Nested conditionals with only literals
{{ true ? (true ? "<br />" : "<br>") : "\n" }}
5. Nested conditionals with a variable
{{ true ? (true ? "<br />" : someVar) : "\n" }}
6. Nested conditionals with a variable marked safe
{{ true ? (true ? "<br />" : someVar|raw) : "\n" }}
{% endautoescape %}
--DATA--
return array()
--EXPECT--
1. Simple literal
<br />
2. Conditional expression with only literals
<br />
3. Conditional expression with a variable
&lt;br /&gt;
4. Nested conditionals with only literals
<br />
5. Nested conditionals with a variable
&lt;br /&gt;
6. Nested conditionals with a variable marked safe
<br />
--TEST--
"autoescape" tags can be nested at will
--TEMPLATE--
{{ var }}
{% autoescape 'html' %}
{{ var }}
{% autoescape false %}
{{ var }}
{% autoescape 'html' %}
{{ var }}
{% endautoescape %}
{{ var }}
{% endautoescape %}
{{ var }}
{% endautoescape %}
{{ var }}
--DATA--
return array('var' => '<br />')
--EXPECT--
&lt;br /&gt;
&lt;br /&gt;
<br />
&lt;br /&gt;
<br />
&lt;br /&gt;
&lt;br /&gt;
--TEST--
"autoescape" tag applies escaping to object method calls
--TEMPLATE--
{% autoescape 'html' %}
{{ user.name }}
{{ user.name|lower }}
{{ user }}
{% endautoescape %}
--EXPECT--
Fabien&lt;br /&gt;
fabien&lt;br /&gt;
Fabien&lt;br /&gt;
--TEST--
"autoescape" tag does not escape when raw is used as a filter
--TEMPLATE--
{% autoescape 'html' %}
{{ var|raw }}
{% endautoescape %}
--DATA--
return array('var' => '<br />')
--EXPECT--
<br />
--TEST--
"autoescape" tag accepts an escaping strategy
--TEMPLATE--
{% autoescape true js %}{{ var }}{% endautoescape %}
{% autoescape true html %}{{ var }}{% endautoescape %}
{% autoescape 'js' %}{{ var }}{% endautoescape %}
{% autoescape 'html' %}{{ var }}{% endautoescape %}
--DATA--
return array('var' => '<br />"')
--EXPECT--
\x3Cbr\x20\x2F\x3E\x22
&lt;br /&gt;&quot;
\x3Cbr\x20\x2F\x3E\x22
&lt;br /&gt;&quot;
--TEST--
escape types
--TEMPLATE--
1. autoescape 'html' |escape('js')
{% autoescape 'html' %}
<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
{% endautoescape %}
2. autoescape 'html' |escape('js')
{% autoescape 'html' %}
<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
{% endautoescape %}
3. autoescape 'js' |escape('js')
{% autoescape 'js' %}
<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
{% endautoescape %}
4. no escape
{% autoescape false %}
<a onclick="alert(&quot;{{ msg }}&quot;)"></a>
{% endautoescape %}
5. |escape('js')|escape('html')
{% autoescape false %}
<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
{% endautoescape %}
6. autoescape 'html' |escape('js')|escape('html')
{% autoescape 'html' %}
<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
{% endautoescape %}
--DATA--
return array('msg' => "<>\n'\"")
--EXPECT--
1. autoescape 'html' |escape('js')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
2. autoescape 'html' |escape('js')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
3. autoescape 'js' |escape('js')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
4. no escape
<a onclick="alert(&quot;<>
'"&quot;)"></a>
5. |escape('js')|escape('html')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
6. autoescape 'html' |escape('js')|escape('html')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
--TEST--
"autoescape" tag do not applies escaping on filter arguments
--TEMPLATE--
{% autoescape 'html' %}
{{ var|nl2br("<br />") }}
{{ var|nl2br("<br />"|escape) }}
{{ var|nl2br(sep) }}
{{ var|nl2br(sep|raw) }}
{{ var|nl2br(sep|escape) }}
{% endautoescape %}
--DATA--
return array('var' => "<Fabien>\nTwig", 'sep' => '<br />')
--EXPECT--
&lt;Fabien&gt;<br />
Twig
&lt;Fabien&gt;&lt;br /&gt;
Twig
&lt;Fabien&gt;<br />
Twig
&lt;Fabien&gt;<br />
Twig
&lt;Fabien&gt;&lt;br /&gt;
Twig
--TEST--
"autoescape" tag applies escaping after calling filters
--TEMPLATE--
{% autoescape 'html' %}
(escape_and_nl2br is an escaper filter)
1. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped )
{{ var|escape_and_nl2br }}
2. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped, |raw is redundant )
{{ var|escape_and_nl2br|raw }}
3. Explicit escape
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is explicitly escaped by |escape )
{{ var|escape_and_nl2br|escape }}
4. Escape non-escaper filter output
( var is upper-cased by |upper,
the output is auto-escaped )
{{ var|upper }}
5. Escape if last filter is not an escaper
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is upper-cased by |upper,
the output is auto-escaped as |upper is not an escaper )
{{ var|escape_and_nl2br|upper }}
6. Don't escape escaper filter output
( var is upper cased by upper,
the output is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped as |escape_and_nl2br is an escaper )
{{ var|upper|escape_and_nl2br }}
7. Escape if last filter is not an escaper
( the output of |format is "<b>" ~ var ~ "</b>",
the output is auto-escaped )
{{ "<b>%s</b>"|format(var) }}
8. Escape if last filter is not an escaper
( the output of |format is "<b>" ~ var ~ "</b>",
|raw is redundant,
the output is auto-escaped )
{{ "<b>%s</b>"|raw|format(var) }}
9. Don't escape escaper filter output
( the output of |format is "<b>" ~ var ~ "</b>",
the output is not escaped due to |raw filter at the end )
{{ "<b>%s</b>"|format(var)|raw }}
10. Don't escape escaper filter output
( the output of |format is "<b>" ~ var ~ "</b>",
the output is not escaped due to |raw filter at the end,
the |raw filter on var is redundant )
{{ "<b>%s</b>"|format(var|raw)|raw }}
{% endautoescape %}
--DATA--
return array('var' => "<Fabien>\nTwig")
--EXPECT--
(escape_and_nl2br is an escaper filter)
1. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped )
&lt;Fabien&gt;<br />
Twig
2. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped, |raw is redundant )
&lt;Fabien&gt;<br />
Twig
3. Explicit escape
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is explicitly escaped by |escape )
&amp;lt;Fabien&amp;gt;&lt;br /&gt;
Twig
4. Escape non-escaper filter output
( var is upper-cased by |upper,
the output is auto-escaped )
&lt;FABIEN&gt;
TWIG
5. Escape if last filter is not an escaper
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is upper-cased by |upper,
the output is auto-escaped as |upper is not an escaper )
&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
TWIG
6. Don't escape escaper filter output
( var is upper cased by upper,
the output is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped as |escape_and_nl2br is an escaper )
&lt;FABIEN&gt;<br />
TWIG
7. Escape if last filter is not an escaper
( the output of |format is "<b>" ~ var ~ "</b>",
the output is auto-escaped )
&lt;b&gt;&lt;Fabien&gt;
Twig&lt;/b&gt;
8. Escape if last filter is not an escaper
( the output of |format is "<b>" ~ var ~ "</b>",
|raw is redundant,
the output is auto-escaped )
&lt;b&gt;&lt;Fabien&gt;
Twig&lt;/b&gt;
9. Don't escape escaper filter output
( the output of |format is "<b>" ~ var ~ "</b>",
the output is not escaped due to |raw filter at the end )
<b><Fabien>
Twig</b>
10. Don't escape escaper filter output
( the output of |format is "<b>" ~ var ~ "</b>",
the output is not escaped due to |raw filter at the end,
the |raw filter on var is redundant )
<b><Fabien>
Twig</b>
--TEST--
"autoescape" tag applies escaping after calling filters, and before calling pre_escape filters
--TEMPLATE--
{% autoescape 'html' %}
(nl2br is pre_escaped for "html" and declared safe for "html")
1. Pre-escape and don't post-escape
( var|escape|nl2br )
{{ var|nl2br }}
2. Don't double-pre-escape
( var|escape|nl2br )
{{ var|escape|nl2br }}
3. Don't escape safe values
( var|raw|nl2br )
{{ var|raw|nl2br }}
4. Don't escape safe values
( var|escape|nl2br|nl2br )
{{ var|nl2br|nl2br }}
5. Re-escape values that are escaped for an other contexts
( var|escape_something|escape|nl2br )
{{ var|escape_something|nl2br }}
6. Still escape when using filters not declared safe
( var|escape|nl2br|upper|escape )
{{ var|nl2br|upper }}
{% endautoescape %}
--DATA--
return array('var' => "<Fabien>\nTwig")
--EXPECT--
(nl2br is pre_escaped for "html" and declared safe for "html")
1. Pre-escape and don't post-escape
( var|escape|nl2br )
&lt;Fabien&gt;<br />
Twig
2. Don't double-pre-escape
( var|escape|nl2br )
&lt;Fabien&gt;<br />
Twig
3. Don't escape safe values
( var|raw|nl2br )
<Fabien><br />
Twig
4. Don't escape safe values
( var|escape|nl2br|nl2br )
&lt;Fabien&gt;<br /><br />
Twig
5. Re-escape values that are escaped for an other contexts
( var|escape_something|escape|nl2br )
&lt;FABIEN&gt;<br />
TWIG
6. Still escape when using filters not declared safe
( var|escape|nl2br|upper|escape )
&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
TWIG
--TEST--
"autoescape" tag handles filters preserving the safety
--TEMPLATE--
{% autoescape 'html' %}
(preserves_safety is preserving safety for "html")
1. Unsafe values are still unsafe
( var|preserves_safety|escape )
{{ var|preserves_safety }}
2. Safe values are still safe
( var|escape|preserves_safety )
{{ var|escape|preserves_safety }}
3. Re-escape values that are escaped for an other contexts
( var|escape_something|preserves_safety|escape )
{{ var|escape_something|preserves_safety }}
4. Still escape when using filters not declared safe
( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
{{ var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'}) }}
{% endautoescape %}
--DATA--
return array('var' => "<Fabien>\nTwig")
--EXPECT--
(preserves_safety is preserving safety for "html")
1. Unsafe values are still unsafe
( var|preserves_safety|escape )
&lt;FABIEN&gt;
TWIG
2. Safe values are still safe
( var|escape|preserves_safety )
&LT;FABIEN&GT;
TWIG
3. Re-escape values that are escaped for an other contexts
( var|escape_something|preserves_safety|escape )
&lt;FABIEN&gt;
TWIG
4. Still escape when using filters not declared safe
( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
&amp;LT;FABPOT&amp;GT;
TWIG
--TEST--
"block" tag
--TEMPLATE--
{% block title1 %}FOO{% endblock %}
{% block title2 foo|lower %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
FOObar
--TEST--
"block" tag
--TEMPLATE--
{% block content %}
{% block content %}
{% endblock %}
{% endblock %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3
--TEST--
"§" special chars in a block name
--TEMPLATE--
{% block § %}
§
{% endblock § %}
--DATA--
return array()
--EXPECT--
§
--TEST--
"embed" tag
--TEMPLATE--
FOO
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
BAR
--TEMPLATE(foo.twig)--
A
{% block c1 %}
block1
{% endblock %}
B
{% block c2 %}
block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
FOO
A
block1
block1extended
B
block2
C
BAR
--TEST--
"embed" tag
--TEMPLATE(index.twig)--
FOO
{% embed "foo.twig" %}
{% block c1 %}
{{ nothing }}
{% endblock %}
{% endembed %}
BAR
--TEMPLATE(foo.twig)--
{% block c1 %}{% endblock %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5
--TEST--
"embed" tag
--TEMPLATE--
FOO
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
BAR
--TEMPLATE(foo.twig)--
A
{% block c1 %}
block1
{% endblock %}
B
{% block c2 %}
block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
FOO
A
block1
block1extended
B
block2
C
A
block1
block1extended
B
block2
C
BAR
--TEST--
"embed" tag
--TEMPLATE--
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
{% endblock %}
{% endembed %}
--TEMPLATE(foo.twig)--
A
{% block c1 %}
block1
{% endblock %}
B
{% block c2 %}
block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
A
block1
A
block1
block1extended
B
block2
C
B
block2
C
--TEST--
"embed" tag
--TEMPLATE--
{% extends "base.twig" %}
{% block c1 %}
{{ parent() }}
blockc1baseextended
{% endblock %}
{% block c2 %}
{{ parent() }}
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
{% endblock %}
--TEMPLATE(base.twig)--
A
{% block c1 %}
blockc1base
{% endblock %}
{% block c2 %}
blockc2base
{% endblock %}
B
--TEMPLATE(foo.twig)--
A
{% block c1 %}
block1
{% endblock %}
B
{% block c2 %}
block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
A
blockc1base
blockc1baseextended
blockc2base
A
block1
block1extended
B
block2
CB--TEST--
"filter" tag applies a filter on its children
--TEMPLATE--
{% filter upper %}
Some text with a {{ var }}
{% endfilter %}
--DATA--
return array('var' => 'var')
--EXPECT--
SOME TEXT WITH A VAR
--TEST--
"filter" tag applies a filter on its children
--TEMPLATE--
{% filter json_encode|raw %}test{% endfilter %}
--DATA--
return array()
--EXPECT--
"test"
--TEST--
"filter" tags accept multiple chained filters
--TEMPLATE--
{% filter lower|title %}
{{ var }}
{% endfilter %}
--DATA--
return array('var' => 'VAR')
--EXPECT--
Var
--TEST--
"filter" tags can be nested at will
--TEMPLATE--
{% filter lower|title %}
{{ var }}
{% filter upper %}
{{ var }}
{% endfilter %}
{{ var }}
{% endfilter %}
--DATA--
return array('var' => 'var')
--EXPECT--
Var
Var
Var
--TEST--
"filter" tag applies the filter on "for" tags
--TEMPLATE--
{% filter upper %}
{% for item in items %}
{{ item }}
{% endfor %}
{% endfilter %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
A
B
--TEST--
"filter" tag applies the filter on "if" tags
--TEMPLATE--
{% filter upper %}
{% if items %}
{{ items|join(', ') }}
{% endif %}
{% if items.3 is defined %}
FOO
{% else %}
{{ items.1 }}
{% endif %}
{% if items.3 is defined %}
FOO
{% elseif items.1 %}
{{ items.0 }}
{% endif %}
{% endfilter %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
A, B
B
A
--TEST--
"for" tag takes a condition
--TEMPLATE--
{% for i in 1..5 if i is odd -%}
{{ loop.index }}.{{ i }}{{ foo.bar }}
{% endfor %}
--DATA--
return array('foo' => array('bar' => 'X'))
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
1.1X
2.3X
3.5X
--TEST--
"for" tag keeps the context safe
--TEMPLATE--
{% for item in items %}
{% for item in items %}
* {{ item }}
{% endfor %}
* {{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* a
* b
* a
* a
* b
* b
--TEST--
"for" tag can use an "else" clause
--TEMPLATE--
{% for item in items %}
* {{ item }}
{% else %}
no item
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* a
* b
--DATA--
return array('items' => array())
--EXPECT--
no item
--DATA--
return array()
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
no item
--TEST--
"for" tag does not reset inner variables
--TEMPLATE--
{% for i in 1..2 %}
{% for j in 0..2 %}
{{k}}{% set k = k+1 %} {{ loop.parent.loop.index }}
{% endfor %}
{% endfor %}
--DATA--
return array('k' => 0)
--EXPECT--
0 1
1 1
2 1
3 2
4 2
5 2
--TEST--
"for" tag can iterate over keys and values
--TEMPLATE--
{% for key, item in items %}
* {{ key }}/{{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* 0/a
* 1/b
--TEST--
"for" tag can iterate over keys
--TEMPLATE--
{% for key in items|keys %}
* {{ key }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* 0
* 1
--TEST--
"for" tag adds a loop variable to the context locally
--TEMPLATE--
{% for item in items %}
{% endfor %}
{% if loop is not defined %}WORKS{% endif %}
--DATA--
return array('items' => array())
--EXPECT--
WORKS
--TEST--
"for" tag adds a loop variable to the context
--TEMPLATE--
{% for item in items %}
* {{ loop.index }}/{{ loop.index0 }}
* {{ loop.revindex }}/{{ loop.revindex0 }}
* {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* 1/0
* 2/1
* 1//2
* 2/1
* 1/0
* /1/2
--TEST--
"for" tag
--TEMPLATE--
{% for i, item in items if loop.last > 0 %}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXCEPTION--
Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2
--TEST--
"for" tag
--TEMPLATE--
{% for i, item in items if i > 0 %}
{{ loop.last }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXCEPTION--
Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3
--TEST--
"for" tag can use an "else" clause
--TEMPLATE--
{% for item in items %}
{% for item in items1 %}
* {{ item }}
{% else %}
no {{ item }}
{% endfor %}
{% else %}
no item1
{% endfor %}
--DATA--
return array('items' => array('a', 'b'), 'items1' => array())
--EXPECT--
no a
no b
--TEST--
"for" tag iterates over iterable and countable objects
--TEMPLATE--
{% for item in items %}
* {{ item }}
* {{ loop.index }}/{{ loop.index0 }}
* {{ loop.revindex }}/{{ loop.revindex0 }}
* {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
{% endfor %}
{% for key, value in items %}
* {{ key }}/{{ value }}
{% endfor %}
{% for key in items|keys %}
* {{ key }}
{% endfor %}
--DATA--
class ItemsIteratorCountable implements Iterator, Countable
{
protected $values = array('foo' => 'bar', 'bar' => 'foo');
public function current() { return current($this->values); }
public function key() { return key($this->values); }
public function next() { return next($this->values); }
public function rewind() { return reset($this->values); }
public function valid() { return false !== current($this->values); }
public function count() { return count($this->values); }
}
return array('items' => new ItemsIteratorCountable())
--EXPECT--
* bar
* 1/0
* 2/1
* 1//2
* foo
* 2/1
* 1/0
* /1/2
* foo/bar
* bar/foo
* foo
* bar
--TEST--
"for" tag iterates over iterable objects
--TEMPLATE--
{% for item in items %}
* {{ item }}
* {{ loop.index }}/{{ loop.index0 }}
* {{ loop.first }}
{% endfor %}
{% for key, value in items %}
* {{ key }}/{{ value }}
{% endfor %}
{% for key in items|keys %}
* {{ key }}
{% endfor %}
--DATA--
class ItemsIterator implements Iterator
{
protected $values = array('foo' => 'bar', 'bar' => 'foo');
public function current() { return current($this->values); }
public function key() { return key($this->values); }
public function next() { return next($this->values); }
public function rewind() { return reset($this->values); }
public function valid() { return false !== current($this->values); }
}
return array('items' => new ItemsIterator())
--EXPECT--
* bar
* 1/0
* 1
* foo
* 2/1
*
* foo/bar
* bar/foo
* foo
* bar
--TEST--
"for" tags can be nested
--TEMPLATE--
{% for key, item in items %}
* {{ key }} ({{ loop.length }}):
{% for value in item %}
* {{ value }} ({{ loop.length }})
{% endfor %}
{% endfor %}
--DATA--
return array('items' => array('a' => array('a1', 'a2', 'a3'), 'b' => array('b1')))
--EXPECT--
* a (2):
* a1 (3)
* a2 (3)
* a3 (3)
* b (2):
* b1 (1)
--TEST--
"for" tag iterates over item values
--TEMPLATE--
{% for item in items %}
* {{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* a
* b
--TEST--
global variables
--TEMPLATE--
{% include "included.twig" %}
{% from "included.twig" import foobar %}
{{ foobar() }}
--TEMPLATE(included.twig)--
{% macro foobar() %}
called foobar
{% endmacro %}
--DATA--
return array();
--EXPECT--
called foobar
--TEST--
"if" creates a condition
--TEMPLATE--
{% if a is defined %}
{{ a }}
{% elseif b is defined %}
{{ b }}
{% else %}
NOTHING
{% endif %}
--DATA--
return array('a' => 'a')
--EXPECT--
a
--DATA--
return array('b' => 'b')
--EXPECT--
b
--DATA--
return array()
--EXPECT--
NOTHING
--TEST--
"if" takes an expression as a test
--TEMPLATE--
{% if a < 2 %}
A1
{% elseif a > 10 %}
A2
{% else %}
A3
{% endif %}
--DATA--
return array('a' => 1)
--EXPECT--
A1
--DATA--
return array('a' => 12)
--EXPECT--
A2
--DATA--
return array('a' => 7)
--EXPECT--
A3
--TEST--
"include" tag
--TEMPLATE--
FOO
{% include "foo.twig" %}
BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO
FOOBAR
BAR
--TEST--
"include" tag allows expressions for the template to include
--TEMPLATE--
FOO
{% include foo %}
BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO
FOOBAR
BAR
--TEST--
"include" tag
--TEMPLATE--
{% include ["foo.twig", "bar.twig"] ignore missing %}
{% include "foo.twig" ignore missing %}
{% include "foo.twig" ignore missing with {} %}
{% include "foo.twig" ignore missing with {} only %}
--DATA--
return array()
--EXPECT--
--TEST--
"include" tag
--TEMPLATE--
{% extends "base.twig" %}
{% block content %}
{{ parent() }}
{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}
{% include "foo.twig" %}
{% endblock %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
--TEST--
"include" tag
--TEMPLATE--
{% include "foo.twig" %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
--TEST--
"include" tag accept variables and only
--TEMPLATE--
{% include "foo.twig" %}
{% include "foo.twig" only %}
{% include "foo.twig" with {'foo1': 'bar'} %}
{% include "foo.twig" with {'foo1': 'bar'} only %}
--TEMPLATE(foo.twig)--
{% for k, v in _context %}{{ k }},{% endfor %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
foo,global,_parent,
global,_parent,
foo,global,foo1,_parent,
foo1,global,_parent,
--TEST--
"include" tag accepts Twig_Template instance
--TEMPLATE--
{% include foo %} FOO
--TEMPLATE(foo.twig)--
BAR
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BAR FOO
--TEST--
"include" tag
--TEMPLATE--
{% include ["foo.twig", "bar.twig"] %}
{% include ["bar.twig", "foo.twig"] %}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
--TEST--
"include" tag accept variables
--TEMPLATE--
{% include "foo.twig" with {'foo': 'bar'} %}
{% include "foo.twig" with vars %}
--TEMPLATE(foo.twig)--
{{ foo }}
--DATA--
return array('vars' => array('foo' => 'bar'))
--EXPECT--
bar
bar
--TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
{% block content %}
FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array()
--EXPECT--
FOO
--TEST--
block_expr2
--TEMPLATE--
{% extends "base2.twig" %}
{% block element -%}
Element:
{{- parent() -}}
{% endblock %}
--TEMPLATE(base2.twig)--
{% extends "base.twig" %}
--TEMPLATE(base.twig)--
{% spaceless %}
{% block element -%}
<div>
{%- if item.children is defined %}
{%- for item in item.children %}
{{- block('element') -}}
{% endfor %}
{%- endif -%}
</div>
{%- endblock %}
{% endspaceless %}
--DATA--
return array(
'item' => array(
'children' => array(
null,
null,
)
)
)
--EXPECT--
Element:<div>Element:<div></div>Element:<div></div></div>
--TEST--
block_expr
--TEMPLATE--
{% extends "base.twig" %}
{% block element -%}
Element:
{{- parent() -}}
{% endblock %}
--TEMPLATE(base.twig)--
{% spaceless %}
{% block element -%}
<div>
{%- if item.children is defined %}
{%- for item in item.children %}
{{- block('element') -}}
{% endfor %}
{%- endif -%}
</div>
{%- endblock %}
{% endspaceless %}
--DATA--
return array(
'item' => array(
'children' => array(
null,
null,
)
)
)
--EXPECT--
Element:<div>Element:<div></div>Element:<div></div></div>
--TEST--
"extends" tag
--TEMPLATE--
{% extends standalone ? foo : 'bar.twig' %}
{% block content %}{{ parent() }}FOO{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}FOO{% endblock %}
--TEMPLATE(bar.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array('foo' => 'foo.twig', 'standalone' => true)
--EXPECT--
FOOFOO
--TEST--
"extends" tag
--TEMPLATE--
{% extends foo %}
{% block content %}
FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO
--TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
--TEMPLATE(foo.twig)--
{% block content %}FOO{% endblock %}
--DATA--
return array()
--EXPECT--
FOO
--TEST--
"extends" tag
--TEMPLATE--
{% extends ["foo.twig", "bar.twig"] %}
--TEMPLATE(bar.twig)--
{% block content %}
foo
{% endblock %}
--DATA--
return array()
--EXPECT--
foo
--TEST--
"extends" tag
--TEMPLATE--
{% extends "layout.twig" %}{% block content %}{{ parent() }}index {% endblock %}
--TEMPLATE(layout.twig)--
{% extends "base.twig" %}{% block content %}{{ parent() }}layout {% endblock %}
--TEMPLATE(base.twig)--
{% block content %}base {% endblock %}
--DATA--
return array()
--EXPECT--
base layout index
--TEST--
"block" tag
--TEMPLATE--
{% block content %}
CONTENT
{%- block subcontent -%}
SUBCONTENT
{%- endblock -%}
ENDCONTENT
{% endblock %}
--TEMPLATE(foo.twig)--
--DATA--
return array()
--EXPECT--
CONTENTSUBCONTENTENDCONTENT
--TEST--
"block" tag
--TEMPLATE--
{% extends "foo.twig" %}
{% block content %}
{% block subcontent %}
{% block subsubcontent %}
SUBSUBCONTENT
{% endblock %}
{% endblock %}
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}
{% block subcontent %}
SUBCONTENT
{% endblock %}
{% endblock %}
--DATA--
return array()
--EXPECT--
SUBSUBCONTENT
--TEST--
"extends" tag
--TEMPLATE--
{% extends "layout.twig" %}
{% block inside %}INSIDE{% endblock inside %}
--TEMPLATE(layout.twig)--
{% extends "base.twig" %}
{% block body %}
{% block inside '' %}
{% endblock body %}
--TEMPLATE(base.twig)--
{% block body '' %}
--DATA--
return array()
--EXPECT--
INSIDE
--TEST--
"extends" tag
--TEMPLATE--
{% extends foo ? 'foo.twig' : 'bar.twig' %}
--TEMPLATE(foo.twig)--
FOO
--TEMPLATE(bar.twig)--
BAR
--DATA--
return array('foo' => true)
--EXPECT--
FOO
--DATA--
return array('foo' => false)
--EXPECT--
BAR
--TEST--
"extends" tag
--TEMPLATE--
{% block content %}
{% extends "foo.twig" %}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3
--TEST--
"extends" tag
--TEMPLATE--
{% extends "base.twig" %}
{% block content %}{% include "included.twig" %}{% endblock %}
{% block footer %}Footer{% endblock %}
--TEMPLATE(included.twig)--
{% extends "base.twig" %}
{% block content %}Included Content{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}Default Content{% endblock %}
{% block footer %}Default Footer{% endblock %}
--DATA--
return array()
--EXPECT--
Included Content
Default Footer
Footer
--TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
{% block content %}
{% block inside %}
INSIDE OVERRIDDEN
{% endblock %}
BEFORE
{{ parent() }}
AFTER
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}
BAR
{% endblock %}
--DATA--
return array()
--EXPECT--
INSIDE OVERRIDDEN
BEFORE
BAR
AFTER
--TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
{% block content %}{{ parent() }}FOO{{ parent() }}{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array()
--EXPECT--
BARFOOBAR
--TEST--
"parent" tag
--TEMPLATE--
{% use 'foo.twig' %}
{% block content %}
{{ parent() }}
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array()
--EXPECT--
BAR
--TEST--
"parent" tag
--TEMPLATE--
{% block content %}
{{ parent() }}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3
--TEST--
"extends" tag accepts Twig_Template instance
--TEMPLATE--
{% extends foo %}
{% block content %}
{{ parent() }}FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BARFOO
--TEST--
"parent" function
--TEMPLATE--
{% extends "parent.twig" %}
{% use "use1.twig" %}
{% use "use2.twig" %}
{% block content_parent %}
{{ parent() }}
{% endblock %}
{% block content_use1 %}
{{ parent() }}
{% endblock %}
{% block content_use2 %}
{{ parent() }}
{% endblock %}
{% block content %}
{{ block('content_use1_only') }}
{{ block('content_use2_only') }}
{% endblock %}
--TEMPLATE(parent.twig)--
{% block content_parent 'content_parent' %}
{% block content_use1 'content_parent' %}
{% block content_use2 'content_parent' %}
{% block content '' %}
--TEMPLATE(use1.twig)--
{% block content_use1 'content_use1' %}
{% block content_use2 'content_use1' %}
{% block content_use1_only 'content_use1_only' %}
--TEMPLATE(use2.twig)--
{% block content_use2 'content_use2' %}
{% block content_use2_only 'content_use2_only' %}
--DATA--
return array()
--EXPECT--
content_parent
content_use1
content_use2
content_use1_only
content_use2_only
--TEST--
"macro" tag
--TEMPLATE--
{% import _self as macros %}
{{ macros.input('username') }}
{{ macros.input('password', null, 'password', 1) }}
{% macro input(name, value, type, size) %}
<input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
{% endmacro %}
--DATA--
return array()
--EXPECT--
<input type="text" name="username" value="" size="20">
<input type="password" name="password" value="" size="1">
--TEST--
"macro" tag supports name for endmacro
--TEMPLATE--
{% import _self as macros %}
{{ macros.foo() }}
{{ macros.bar() }}
{% macro foo() %}foo{% endmacro %}
{% macro bar() %}bar{% endmacro bar %}
--DATA--
return array()
--EXPECT--
foo
bar
--TEST--
"macro" tag
--TEMPLATE--
{% import 'forms.twig' as forms %}
{{ forms.input('username') }}
{{ forms.input('password', null, 'password', 1) }}
--TEMPLATE(forms.twig)--
{% macro input(name, value, type, size) %}
<input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
{% endmacro %}
--DATA--
return array()
--EXPECT--
<input type="text" name="username" value="" size="20">
<input type="password" name="password" value="" size="1">
--TEST--
"macro" tag
--TEMPLATE--
{% from 'forms.twig' import foo %}
{% from 'forms.twig' import foo as foobar, bar %}
{{ foo('foo') }}
{{ foobar('foo') }}
{{ bar('foo') }}
--TEMPLATE(forms.twig)--
{% macro foo(name) %}foo{{ name }}{% endmacro %}
{% macro bar(name) %}bar{{ name }}{% endmacro %}
--DATA--
return array()
--EXPECT--
foofoo
foofoo
barfoo
--TEST--
"macro" tag
--TEMPLATE--
{% from 'forms.twig' import foo %}
{{ foo('foo') }}
{{ foo() }}
--TEMPLATE(forms.twig)--
{% macro foo(name) %}{{ name|default('foo') }}{{ global }}{% endmacro %}
--DATA--
return array()
--EXPECT--
fooglobal
fooglobal
--TEST--
"macro" tag
--TEMPLATE--
{% import _self as forms %}
{{ forms.input('username') }}
{{ forms.input('password', null, 'password', 1) }}
{% macro input(name, value, type, size) %}
<input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
{% endmacro %}
--DATA--
return array()
--EXPECT--
<input type="text" name="username" value="" size="20">
<input type="password" name="password" value="" size="1">
--TEST--
"raw" tag
--TEMPLATE--
{% raw %}
{{ foo }}
{% endraw %}
--DATA--
return array()
--EXPECT--
{{ foo }}
--TEST--
"raw" tag
--TEMPLATE--
{% raw %}
{{ foo }}
{% endverbatim %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2
--TEST--
"raw" tag
--TEMPLATE--
1***
{%- raw %}
{{ 'bla' }}
{% endraw %}
1***
2***
{%- raw -%}
{{ 'bla' }}
{% endraw %}
2***
3***
{%- raw -%}
{{ 'bla' }}
{% endraw -%}
3***
4***
{%- raw -%}
{{ 'bla' }}
{%- endraw %}
4***
5***
{%- raw -%}
{{ 'bla' }}
{%- endraw -%}
5***
--DATA--
return array()
--EXPECT--
1***
{{ 'bla' }}
1***
2***{{ 'bla' }}
2***
3***{{ 'bla' }}
3***
4***{{ 'bla' }}
4***
5***{{ 'bla' }}5***
--TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
{%- include "foo.twig" %}
a
{%- endsandbox %}
--TEMPLATE(foo.twig)--
foo
--EXCEPTION--
Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4
--TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
{%- include "foo.twig" %}
{% if 1 %}
{%- include "foo.twig" %}
{% endif %}
{%- endsandbox %}
--TEMPLATE(foo.twig)--
foo
--EXCEPTION--
Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5
--TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
{%- include "foo.twig" %}
{%- endsandbox %}
{%- sandbox %}
{%- include "foo.twig" %}
{%- include "foo.twig" %}
{%- endsandbox %}
{%- sandbox %}{% include "foo.twig" %}{% endsandbox %}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
foo
foo
--TEST--
"set" tag
--TEMPLATE--
{% set foo = 'foo' %}
{% set bar = 'foo<br />' %}
{{ foo }}
{{ bar }}
{% set foo, bar = 'foo', 'bar' %}
{{ foo }}{{ bar }}
--DATA--
return array()
--EXPECT--
foo
foo&lt;br /&gt;
foobar
--TEST--
"set" tag block empty capture
--TEMPLATE--
{% set foo %}{% endset %}
{% if foo %}FAIL{% endif %}
--DATA--
return array()
--EXPECT--
--TEST--
"set" tag block capture
--TEMPLATE--
{% set foo %}f<br />o<br />o{% endset %}
{{ foo }}
--DATA--
return array()
--EXPECT--
f<br />o<br />o
--TEST--
"set" tag
--TEMPLATE--
{% set foo, bar = 'foo' ~ 'bar', 'bar' ~ 'foo' %}
{{ foo }}
{{ bar }}
--DATA--
return array()
--EXPECT--
foobar
barfoo
--TEST--
"spaceless" tag removes whites between HTML tags
--TEMPLATE--
{% spaceless %}
<div> <div> foo </div> </div>
{% endspaceless %}
--DATA--
return array()
--EXPECT--
<div><div> foo </div></div>
--TEST--
"§" custom tag
--TEMPLATE--
{% § %}
--DATA--
return array()
--EXPECT--
§
--TEST--
Whitespace trimming on tags.
--TEMPLATE--
{{ 5 * '{#-'|length }}
{{ '{{-'|length * 5 + '{%-'|length }}
Trim on control tag:
{% for i in range(1, 9) -%}
{{ i }}
{%- endfor %}
Trim on output tag:
{% for i in range(1, 9) %}
{{- i -}}
{% endfor %}
Trim comments:
{#- Invisible -#}
After the comment.
Trim leading space:
{% if leading %}
{{- leading }}
{% endif %}
{%- if leading %}
{{- leading }}
{%- endif %}
Trim trailing space:
{% if trailing -%}
{{ trailing -}}
{% endif -%}
Combined:
{%- if both -%}
<ul>
<li> {{- both -}} </li>
</ul>
{%- endif -%}
end
--DATA--
return array('leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both')
--EXPECT--
15
18
Trim on control tag:
123456789
Trim on output tag:
123456789
Trim comments:After the comment.
Trim leading space:
leading space
leading space
Trim trailing space:
trailing spaceCombined:<ul>
<li>both</li>
</ul>end
--TEST--
"use" tag
--TEMPLATE--
{% use "blocks.twig" with content as foo %}
{{ block('foo') }}
--TEMPLATE(blocks.twig)--
{% block content 'foo' %}
--DATA--
return array()
--EXPECT--
foo
--TEST--
"use" tag
--TEMPLATE--
{% use "blocks.twig" %}
{{ block('content') }}
--TEMPLATE(blocks.twig)--
{% block content 'foo' %}
--DATA--
return array()
--EXPECT--
foo
--TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}
--TEMPLATE(foo.twig)--
{% use "bar.twig" %}
--TEMPLATE(bar.twig)--
--DATA--
return array()
--EXPECT--
--TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}
{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
--TEMPLATE(foo.twig)--
{% use "bar.twig" %}
{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
foo
foo
bar
--TEST--
"use" tag
--TEMPLATE--
{% use "ancestor.twig" %}
{% use "parent.twig" %}
{{ block('container') }}
--TEMPLATE(parent.twig)--
{% block sub_container %}
<div class="overriden_sub_container">overriden sub_container</div>
{% endblock %}
--TEMPLATE(ancestor.twig)--
{% block container %}
<div class="container">{{ block('sub_container') }}</div>
{% endblock %}
{% block sub_container %}
<div class="sub_container">sub_container</div>
{% endblock %}
--DATA--
return array()
--EXPECT--
<div class="container"> <div class="overriden_sub_container">overriden sub_container</div>
</div>
--TEST--
"use" tag
--TEMPLATE--
{% use "parent.twig" %}
{{ block('container') }}
--TEMPLATE(parent.twig)--
{% use "ancestor.twig" %}
{% block sub_container %}
<div class="overriden_sub_container">overriden sub_container</div>
{% endblock %}
--TEMPLATE(ancestor.twig)--
{% block container %}
<div class="container">{{ block('sub_container') }}</div>
{% endblock %}
{% block sub_container %}
<div class="sub_container">sub_container</div>
{% endblock %}
--DATA--
return array()
--EXPECT--
<div class="container"> <div class="overriden_sub_container">overriden sub_container</div>
</div>
--TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" with content as foo_content %}
{% use "bar.twig" %}
{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
{{ block('foo_content') }}
--TEMPLATE(foo.twig)--
{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
bar
foo
bar
foo
--TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}
{% use "bar.twig" %}
{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
--TEMPLATE(foo.twig)--
{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
bar
foo
bar
--TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig'%}
{% block foobar %}
{{- parent() -}}
Content of block (second override)
{% endblock foobar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' %}
{% block foobar %}
{{- parent() -}}
Content of block (first override)
{% endblock foobar %}
--TEMPLATE(file1.html.twig)--
{% block foobar -%}
Content of block
{% endblock foobar %}
--DATA--
return array()
--EXPECT--
Content of block
Content of block (first override)
Content of block (second override)
--TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig' %}
{% use 'file1.html.twig' with foo %}
{% block foo %}
{{- parent() -}}
Content of foo (second override)
{% endblock foo %}
{% block bar %}
{{- parent() -}}
Content of bar (second override)
{% endblock bar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' %}
{% block foo %}
{{- parent() -}}
Content of foo (first override)
{% endblock foo %}
{% block bar %}
{{- parent() -}}
Content of bar (first override)
{% endblock bar %}
--TEMPLATE(file1.html.twig)--
{% block foo -%}
Content of foo
{% endblock foo %}
{% block bar -%}
Content of bar
{% endblock bar %}
--DATA--
return array()
--EXPECT--
Content of foo
Content of foo (first override)
Content of foo (second override)
Content of bar
Content of bar (second override)
--TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig' with foobar as base_base_foobar %}
{% block foobar %}
{{- block('base_base_foobar') -}}
Content of block (second override)
{% endblock foobar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' with foobar as base_foobar %}
{% block foobar %}
{{- block('base_foobar') -}}
Content of block (first override)
{% endblock foobar %}
--TEMPLATE(file1.html.twig)--
{% block foobar -%}
Content of block
{% endblock foobar %}
--DATA--
return array()
--EXPECT--
Content of block
Content of block (first override)
Content of block (second override)
--TEST--
"verbatim" tag
--TEMPLATE--
{% verbatim %}
{{ foo }}
{% endverbatim %}
--DATA--
return array()
--EXPECT--
{{ foo }}
--TEST--
"verbatim" tag
--TEMPLATE--
{% verbatim %}
{{ foo }}
{% endraw %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2
--TEST--
"verbatim" tag
--TEMPLATE--
1***
{%- verbatim %}
{{ 'bla' }}
{% endverbatim %}
1***
2***
{%- verbatim -%}
{{ 'bla' }}
{% endverbatim %}
2***
3***
{%- verbatim -%}
{{ 'bla' }}
{% endverbatim -%}
3***
4***
{%- verbatim -%}
{{ 'bla' }}
{%- endverbatim %}
4***
5***
{%- verbatim -%}
{{ 'bla' }}
{%- endverbatim -%}
5***
--DATA--
return array()
--EXPECT--
1***
{{ 'bla' }}
1***
2***{{ 'bla' }}
2***
3***{{ 'bla' }}
3***
4***{{ 'bla' }}
4***
5***{{ 'bla' }}5***
--TEST--
array index test
--TEMPLATE--
{% for key, value in days %}
{{ key }}
{% endfor %}
--DATA--
return array('days' => array(
1 => array('money' => 9),
2 => array('money' => 21),
3 => array('money' => 38),
4 => array('money' => 6),
18 => array('money' => 6),
19 => array('money' => 3),
31 => array('money' => 11),
));
--EXPECT--
1
2
3
4
18
19
31
--TEST--
"const" test
--TEMPLATE--
{{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }}
{{ 'bar' is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
{{ value is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
{{ 2 is constant('ARRAY_AS_PROPS', object) ? 'ok' : 'no' }}
--DATA--
return array('value' => 'bar', 'object' => new ArrayObject(array('hi')));
--EXPECT--
ok
ok
ok
ok--TEST--
"defined" test
--TEMPLATE--
{{ definedVar is defined ? 'ok' : 'ko' }}
{{ definedVar is not defined ? 'ko' : 'ok' }}
{{ undefinedVar is defined ? 'ko' : 'ok' }}
{{ undefinedVar is not defined ? 'ok' : 'ko' }}
{{ zeroVar is defined ? 'ok' : 'ko' }}
{{ nullVar is defined ? 'ok' : 'ko' }}
{{ nested.definedVar is defined ? 'ok' : 'ko' }}
{{ nested['definedVar'] is defined ? 'ok' : 'ko' }}
{{ nested.definedVar is not defined ? 'ko' : 'ok' }}
{{ nested.undefinedVar is defined ? 'ko' : 'ok' }}
{{ nested['undefinedVar'] is defined ? 'ko' : 'ok' }}
{{ nested.undefinedVar is not defined ? 'ok' : 'ko' }}
{{ nested.zeroVar is defined ? 'ok' : 'ko' }}
{{ nested.nullVar is defined ? 'ok' : 'ko' }}
{{ nested.definedArray.0 is defined ? 'ok' : 'ko' }}
{{ nested['definedArray'][0] is defined ? 'ok' : 'ko' }}
{{ object.foo is defined ? 'ok' : 'ko' }}
{{ object.undefinedMethod is defined ? 'ko' : 'ok' }}
{{ object.getFoo() is defined ? 'ok' : 'ko' }}
{{ object.getFoo('a') is defined ? 'ok' : 'ko' }}
{{ object.undefinedMethod() is defined ? 'ko' : 'ok' }}
{{ object.undefinedMethod('a') is defined ? 'ko' : 'ok' }}
{{ object.self.foo is defined ? 'ok' : 'ko' }}
{{ object.self.undefinedMethod is defined ? 'ko' : 'ok' }}
{{ object.undefinedMethod.self is defined ? 'ko' : 'ok' }}
--DATA--
return array(
'definedVar' => 'defined',
'zeroVar' => 0,
'nullVar' => null,
'nested' => array(
'definedVar' => 'defined',
'zeroVar' => 0,
'nullVar' => null,
'definedArray' => array(0),
),
'object' => new TwigTestFoo(),
);
--EXPECT--
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
--DATA--
return array(
'definedVar' => 'defined',
'zeroVar' => 0,
'nullVar' => null,
'nested' => array(
'definedVar' => 'defined',
'zeroVar' => 0,
'nullVar' => null,
'definedArray' => array(0),
),
'object' => new TwigTestFoo(),
);
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
--TEST--
"empty" test
--TEMPLATE--
{{ foo is empty ? 'ok' : 'ko' }}
{{ bar is empty ? 'ok' : 'ko' }}
{{ foobar is empty ? 'ok' : 'ko' }}
{{ array is empty ? 'ok' : 'ko' }}
{{ zero is empty ? 'ok' : 'ko' }}
{{ string is empty ? 'ok' : 'ko' }}
{{ countable_empty is empty ? 'ok' : 'ko' }}
{{ countable_not_empty is empty ? 'ok' : 'ko' }}
{{ markup_empty is empty ? 'ok' : 'ko' }}
{{ markup_not_empty is empty ? 'ok' : 'ko' }}
--DATA--
class CountableStub implements Countable
{
private $items;
public function __construct(array $items)
{
$this->items = $items;
}
public function count()
{
return count($this->items);
}
}
return array(
'foo' => '', 'bar' => null, 'foobar' => false, 'array' => array(), 'zero' => 0, 'string' => '0',
'countable_empty' => new CountableStub(array()), 'countable_not_empty' => new CountableStub(array(1, 2)),
'markup_empty' => new Twig_Markup('', 'UTF-8'), 'markup_not_empty' => new Twig_Markup('test', 'UTF-8'),
);
--EXPECT--
ok
ok
ok
ok
ko
ko
ok
ko
ok
ko
--TEST--
"even" test
--TEMPLATE--
{{ 1 is even ? 'ko' : 'ok' }}
{{ 2 is even ? 'ok' : 'ko' }}
{{ 1 is not even ? 'ok' : 'ko' }}
{{ 2 is not even ? 'ko' : 'ok' }}
--DATA--
return array()
--EXPECT--
ok
ok
ok
ok
--TEST--
Twig supports the in operator
--TEMPLATE--
{% if bar in foo %}
TRUE
{% endif %}
{% if not (bar in foo) %}
{% else %}
TRUE
{% endif %}
{% if bar not in foo %}
{% else %}
TRUE
{% endif %}
{% if 'a' in bar %}
TRUE
{% endif %}
{% if 'c' not in bar %}
TRUE
{% endif %}
{% if '' not in bar %}
TRUE
{% endif %}
{% if '' in '' %}
TRUE
{% endif %}
{% if '0' not in '' %}
TRUE
{% endif %}
{% if 'a' not in '0' %}
TRUE
{% endif %}
{% if '0' in '0' %}
TRUE
{% endif %}
{{ false in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ true in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ '0' in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ '' in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ 0 in ['', 1] ? 'TRUE' : 'FALSE' }}
{{ '' in 'foo' ? 'TRUE' : 'FALSE' }}
{{ 0 in 'foo' ? 'TRUE' : 'FALSE' }}
{{ false in 'foo' ? 'TRUE' : 'FALSE' }}
{{ true in '100' ? 'TRUE' : 'FALSE' }}
{{ [] in 'Array' ? 'TRUE' : 'FALSE' }}
{{ [] in [true, false] ? 'TRUE' : 'FALSE' }}
{{ [] in [true, ''] ? 'TRUE' : 'FALSE' }}
{{ [] in [true, []] ? 'TRUE' : 'FALSE' }}
{{ dir_object in 'foo'~dir_name ? 'TRUE' : 'FALSE' }}
{{ 5 in 125 ? 'TRUE' : 'FALSE' }}
--DATA--
return array('bar' => 'bar', 'foo' => array('bar' => 'bar'), 'dir_name' => dirname(__FILE__), 'dir_object' => new SplFileInfo(dirname(__FILE__)))
--EXPECT--
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
FALSE
FALSE
FALSE
FALSE
FALSE
TRUE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
TRUE
FALSE
FALSE
--TEST--
Twig supports the in operator when using objects
--TEMPLATE--
{% if object in object_list %}
TRUE
{% endif %}
--DATA--
$foo = new TwigTestFoo();
$foo1 = new TwigTestFoo();
$foo->position = $foo1;
$foo1->position = $foo;
return array(
'object' => $foo,
'object_list' => array($foo1, $foo),
);
--EXPECT--
TRUE
--TEST--
"iterable" test
--TEMPLATE--
{{ foo is iterable ? 'ok' : 'ko' }}
{{ traversable is iterable ? 'ok' : 'ko' }}
{{ obj is iterable ? 'ok' : 'ko' }}
{{ val is iterable ? 'ok' : 'ko' }}
--DATA--
return array(
'foo' => array(),
'traversable' => new ArrayIterator(array()),
'obj' => new stdClass(),
'val' => 'test',
);
--EXPECT--
ok
ok
ko
ko--TEST--
"odd" test
--TEMPLATE--
{{ 1 is odd ? 'ok' : 'ko' }}
{{ 2 is odd ? 'ko' : 'ok' }}
--DATA--
return array()
--EXPECT--
ok
ok