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
':'' }} {{ foo ~ (bar ? ('-' ~ bar) : '') }} --DATA-- return array('foo' => 'foo', 'bar' => 'bar') --EXPECT-- YES NO YES1 NO1 foo
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) %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %} --DATA-- return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')) --EXPECT--
a
b
c
d
e
f
g
h
i
j
--TEST-- "batch" filter --TEMPLATE-- {% for row in items|batch(3) %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %} --DATA-- return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')) --EXPECT--
a
b
c
d
e
f
g
h
i
j
--TEST-- "batch" filter --TEMPLATE-- {% for row in items|batch(3, '') %} {% for column in row %} {% endfor %} {% endfor %}
{{ column }}
--DATA-- return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')) --EXPECT--
a b c
d e f
g h i
j
--TEST-- "batch" filter --TEMPLATE-- {% for row in items|batch(3, 'fill') %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %} --DATA-- return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l')) --EXPECT--
a
b
c
d
e
f
g
h
i
j
k
l
--TEST-- "batch" filter --TEMPLATE-- {% for row in items|batch(3, 'fill') %} {% for column in row %} {% endfor %} {% endfor %}
{{ column }}
--DATA-- return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')) --EXPECT--
a b c
d e f
g h i
j fill fill
--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-- {{ '
'|escape('html_attr') }} --DATA-- return array() --EXPECT-- <br /> --TEST-- "escape" filter --TEMPLATE-- {{ "愛していますか?
"|e }} --DATA-- return array() --EXPECT-- 愛していますか? <br /> --TEST-- "escape" filter --TEMPLATE-- {{ "foo
"|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
{% 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
--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 HTML\nit will be escaped.") --EXPECT-- I like Twig.
You will like it too.

Everybody like it! If you have some <strong>HTML</strong>
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 HTML 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)--

Test

--DATA-- return array() --EXPECT--

Test

--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') %}
    {{ block('children') }}
{% endblock %} --TEMPLATE(menu.twig)-- {% extends "base.twig" %} {% block list %}{% endblock %} {% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %} {% block item %}
  • {% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %}
  • {% 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--
    1. "1"
    2. "2"
      1. "3.1"
        1. "3.2.1"
        2. "3.2.2"
      2. "3.4"
    --TEST-- "source" function --TEMPLATE-- FOO {{ source("foo.twig") }} BAR --TEMPLATE(foo.twig)-- {{ foo }}
    --DATA-- return array() --EXPECT-- FOO {{ foo }}
    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() %}
    {% endmacro %} {{ macros.foo(macros.bar()) }} --DATA-- return array(); --EXPECT--
    --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') }}
    {{- b|default('b') }}
    {%- endmacro %} {{ test.test() }} {{ test() }} {{ test.test(1, "c") }} {{ test(1, "c") }} --DATA-- return array(); --EXPECT-- a
    b
    a
    b
    1
    c
    1
    c
    --TEST-- macro with a filter --TEMPLATE-- {% import _self as test %} {% macro test() %} {% filter escape %}foo
    {% 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('foobar')) --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 }}
    {% endautoescape %} {% autoescape 'html' %} {{ var }}
    {% endautoescape %} {% autoescape false %} {{ var }}
    {% endautoescape %} {% autoescape true %} {{ var }}
    {% endautoescape %} {% autoescape false %} {{ var }}
    {% endautoescape %} --DATA-- return array('var' => '
    ') --EXPECT-- <br />
    <br />


    <br />


    --TEST-- "autoescape" tag applies escaping on embedded blocks --TEMPLATE-- {% autoescape 'html' %} {% block foo %} {{ var }} {% endblock %} {% endautoescape %} --DATA-- return array('var' => '
    ') --EXPECT-- <br /> --TEST-- "autoescape" tag does not double-escape --TEMPLATE-- {% autoescape 'html' %} {{ var|escape }} {% endautoescape %} --DATA-- return array('var' => '
    ') --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
    unsafe_br
    autoescape 'html' safe_br
    unsafe_br <br /> unsafe_br()|raw
    safe_br()|escape <br /> safe_br()|raw
    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 {{ "
    " }} 2. Conditional expression with only literals {{ true ? "
    " : "
    " }} 3. Conditional expression with a variable {{ true ? "
    " : someVar }} 4. Nested conditionals with only literals {{ true ? (true ? "
    " : "
    ") : "\n" }} 5. Nested conditionals with a variable {{ true ? (true ? "
    " : someVar) : "\n" }} 6. Nested conditionals with a variable marked safe {{ true ? (true ? "
    " : someVar|raw) : "\n" }} {% endautoescape %} --DATA-- return array() --EXPECT-- 1. Simple literal
    2. Conditional expression with only literals
    3. Conditional expression with a variable <br /> 4. Nested conditionals with only literals
    5. Nested conditionals with a variable <br /> 6. Nested conditionals with a variable marked safe
    --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' => '
    ') --EXPECT-- <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' => '
    ') --EXPECT--
    --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' => '
    "') --EXPECT-- \x3Cbr\x20\x2F\x3E\x22 <br />" \x3Cbr\x20\x2F\x3E\x22 <br />" --TEST-- escape types --TEMPLATE-- 1. autoescape 'html' |escape('js') {% autoescape 'html' %} {% endautoescape %} 2. autoescape 'html' |escape('js') {% autoescape 'html' %} {% endautoescape %} 3. autoescape 'js' |escape('js') {% autoescape 'js' %} {% endautoescape %} 4. no escape {% autoescape false %} {% endautoescape %} 5. |escape('js')|escape('html') {% autoescape false %} {% endautoescape %} 6. autoescape 'html' |escape('js')|escape('html') {% autoescape 'html' %} {% endautoescape %} --DATA-- return array('msg' => "<>\n'\"") --EXPECT-- 1. autoescape 'html' |escape('js') 2. autoescape 'html' |escape('js') 3. autoescape 'js' |escape('js') 4. no escape 5. |escape('js')|escape('html') 6. autoescape 'html' |escape('js')|escape('html') --TEST-- "autoescape" tag do not applies escaping on filter arguments --TEMPLATE-- {% autoescape 'html' %} {{ var|nl2br("
    ") }} {{ var|nl2br("
    "|escape) }} {{ var|nl2br(sep) }} {{ var|nl2br(sep|raw) }} {{ var|nl2br(sep|escape) }} {% endautoescape %} --DATA-- return array('var' => "\nTwig", 'sep' => '
    ') --EXPECT-- <Fabien>
    Twig <Fabien><br /> Twig <Fabien>
    Twig <Fabien>
    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 "" ~ var ~ "", the output is auto-escaped ) {{ "%s"|format(var) }} 8. Escape if last filter is not an escaper ( the output of |format is "" ~ var ~ "", |raw is redundant, the output is auto-escaped ) {{ "%s"|raw|format(var) }} 9. Don't escape escaper filter output ( the output of |format is "" ~ var ~ "", the output is not escaped due to |raw filter at the end ) {{ "%s"|format(var)|raw }} 10. Don't escape escaper filter output ( the output of |format is "" ~ var ~ "", the output is not escaped due to |raw filter at the end, the |raw filter on var is redundant ) {{ "%s"|format(var|raw)|raw }} {% endautoescape %} --DATA-- return array('var' => "\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>
    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>
    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>
    TWIG 7. Escape if last filter is not an escaper ( the output of |format is "" ~ var ~ "", the output is auto-escaped ) <b><Fabien> Twig</b> 8. Escape if last filter is not an escaper ( the output of |format is "" ~ var ~ "", |raw is redundant, the output is auto-escaped ) <b><Fabien> Twig</b> 9. Don't escape escaper filter output ( the output of |format is "" ~ var ~ "", the output is not escaped due to |raw filter at the end ) Twig 10. Don't escape escaper filter output ( the output of |format is "" ~ var ~ "", the output is not escaped due to |raw filter at the end, the |raw filter on var is redundant ) Twig --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' => "\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>
    Twig 2. Don't double-pre-escape ( var|escape|nl2br ) <Fabien>
    Twig 3. Don't escape safe values ( var|raw|nl2br )
    Twig 4. Don't escape safe values ( var|escape|nl2br|nl2br ) <Fabien>

    Twig 5. Re-escape values that are escaped for an other contexts ( var|escape_something|escape|nl2br ) <FABIEN>
    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' => "\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 -%}
    {%- if item.children is defined %} {%- for item in item.children %} {{- block('element') -}} {% endfor %} {%- endif -%}
    {%- endblock %} {% endspaceless %} --DATA-- return array( 'item' => array( 'children' => array( null, null, ) ) ) --EXPECT-- Element:
    Element:
    Element:
    --TEST-- block_expr --TEMPLATE-- {% extends "base.twig" %} {% block element -%} Element: {{- parent() -}} {% endblock %} --TEMPLATE(base.twig)-- {% spaceless %} {% block element -%}
    {%- if item.children is defined %} {%- for item in item.children %} {{- block('element') -}} {% endfor %} {%- endif -%}
    {%- endblock %} {% endspaceless %} --DATA-- return array( 'item' => array( 'children' => array( null, null, ) ) ) --EXPECT-- Element:
    Element:
    Element:
    --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) %} {% endmacro %} --DATA-- return array() --EXPECT-- --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) %} {% endmacro %} --DATA-- return array() --EXPECT-- --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) %} {% endmacro %} --DATA-- return array() --EXPECT-- --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
    ' %} {{ 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
    o
    o{% endset %} {{ foo }} --DATA-- return array() --EXPECT-- f
    o
    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 %}
    foo
    {% endspaceless %} --DATA-- return array() --EXPECT--
    foo
    --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 -%}
    • {{- both -}}
    {%- 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:
    • both
    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 %}
    overriden sub_container
    {% endblock %} --TEMPLATE(ancestor.twig)-- {% block container %}
    {{ block('sub_container') }}
    {% endblock %} {% block sub_container %}
    sub_container
    {% endblock %} --DATA-- return array() --EXPECT--
    overriden sub_container
    --TEST-- "use" tag --TEMPLATE-- {% use "parent.twig" %} {{ block('container') }} --TEMPLATE(parent.twig)-- {% use "ancestor.twig" %} {% block sub_container %}
    overriden sub_container
    {% endblock %} --TEMPLATE(ancestor.twig)-- {% block container %}
    {{ block('sub_container') }}
    {% endblock %} {% block sub_container %}
    sub_container
    {% endblock %} --DATA-- return array() --EXPECT--
    overriden sub_container
    --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