1From the Twig test suite, https://github.com/fabpot/Twig, available under BSD license. 2 3--TEST-- 4Exception for an unclosed tag 5--TEMPLATE-- 6{% block foo %} 7 {% if foo %} 8 9 10 11 12 {% for i in fo %} 13 14 15 16 {% endfor %} 17 18 19 20{% endblock %} 21--EXCEPTION-- 22Twig_Error_Syntax: Unexpected tag name "endblock" (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16 23--TEST-- 24Exception for an undefined trait 25--TEMPLATE-- 26{% use 'foo' with foobar as bar %} 27--TEMPLATE(foo)-- 28{% block bar %} 29{% endblock %} 30--EXCEPTION-- 31Twig_Error_Runtime: Block "foobar" is not defined in trait "foo" in "index.twig". 32--TEST-- 33Twig supports method calls 34--TEMPLATE-- 35{{ items.foo }} 36{{ items['foo'] }} 37{{ items[foo] }} 38{{ items[items[foo]] }} 39--DATA-- 40return array('foo' => 'bar', 'items' => array('foo' => 'bar', 'bar' => 'foo')) 41--EXPECT-- 42bar 43bar 44foo 45bar 46--TEST-- 47Twig supports array notation 48--TEMPLATE-- 49{# empty array #} 50{{ []|join(',') }} 51 52{{ [1, 2]|join(',') }} 53{{ ['foo', "bar"]|join(',') }} 54{{ {0: 1, 'foo': 'bar'}|join(',') }} 55{{ {0: 1, 'foo': 'bar'}|keys|join(',') }} 56 57{{ {0: 1, foo: 'bar'}|join(',') }} 58{{ {0: 1, foo: 'bar'}|keys|join(',') }} 59 60{# nested arrays #} 61{% set a = [1, 2, [1, 2], {'foo': {'foo': 'bar'}}] %} 62{{ a[2]|join(',') }} 63{{ a[3]["foo"]|join(',') }} 64 65{# works even if [] is used inside the array #} 66{{ [foo[bar]]|join(',') }} 67 68{# elements can be any expression #} 69{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }} 70 71{# arrays can have a trailing , like in PHP #} 72{{ 73 [ 74 1, 75 2, 76 ]|join(',') 77}} 78 79{# keys can be any expression #} 80{% set a = 1 %} 81{% set b = "foo" %} 82{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %} 83{{ ary|keys|join(',') }} 84{{ ary|join(',') }} 85--DATA-- 86return array('bar' => 'bar', 'foo' => array('bar' => 'bar')) 87--EXPECT-- 881,2 89foo,bar 901,bar 910,foo 92 931,bar 940,foo 95 961,2 97bar 98 99bar 100 101FOO,BAR, 102 1031,2 104 1051,foo,c,1foo 106a,b,c,d 107--TEST-- 108Twig supports binary operations (+, -, *, /, ~, %, and, or) 109--TEMPLATE-- 110{{ 1 + 1 }} 111{{ 2 - 1 }} 112{{ 2 * 2 }} 113{{ 2 / 2 }} 114{{ 3 % 2 }} 115{{ 1 and 1 }} 116{{ 1 and 0 }} 117{{ 0 and 1 }} 118{{ 0 and 0 }} 119{{ 1 or 1 }} 120{{ 1 or 0 }} 121{{ 0 or 1 }} 122{{ 0 or 0 }} 123{{ 0 or 1 and 0 }} 124{{ 1 or 0 and 1 }} 125{{ "foo" ~ "bar" }} 126{{ foo ~ "bar" }} 127{{ "foo" ~ bar }} 128{{ foo ~ bar }} 129{{ 20 // 7 }} 130--DATA-- 131return array('foo' => 'bar', 'bar' => 'foo') 132--EXPECT-- 1332 1341 1354 1361 1371 1381 139 140 141 1421 1431 1441 145 146 1471 148foobar 149barbar 150foofoo 151barfoo 1522 153--TEST-- 154Twig supports bitwise operations 155--TEMPLATE-- 156{{ 1 b-and 5 }} 157{{ 1 b-or 5 }} 158{{ 1 b-xor 5 }} 159{{ (1 and 0 b-or 0) is same as(1 and (0 b-or 0)) ? 'ok' : 'ko' }} 160--DATA-- 161return array() 162--EXPECT-- 1631 1645 1654 166ok 167--TEST-- 168Twig supports comparison operators (==, !=, <, >, >=, <=) 169--TEMPLATE-- 170{{ 1 > 2 }}/{{ 1 > 1 }}/{{ 1 >= 2 }}/{{ 1 >= 1 }} 171{{ 1 < 2 }}/{{ 1 < 1 }}/{{ 1 <= 2 }}/{{ 1 <= 1 }} 172{{ 1 == 1 }}/{{ 1 == 2 }} 173{{ 1 != 1 }}/{{ 1 != 2 }} 174--DATA-- 175return array() 176--EXPECT-- 177///1 1781//1/1 1791/ 180/1 181--TEST-- 182Twig supports the "divisible by" operator 183--TEMPLATE-- 184{{ 8 is divisible by(2) ? 'OK' }} 185{{ 8 is not divisible by(3) ? 'OK' }} 186{{ 8 is divisible by (2) ? 'OK' }} 187{{ 8 is not 188 divisible 189 by 190 (3) ? 'OK' }} 191--DATA-- 192return array() 193--EXPECT-- 194OK 195OK 196OK 197OK 198--TEST-- 199Twig supports the .. operator 200--TEMPLATE-- 201{% for i in 0..10 %}{{ i }} {% endfor %} 202 203{% for letter in 'a'..'z' %}{{ letter }} {% endfor %} 204 205{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %} 206 207{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %} 208 209{% for i in 0 + 1 .. 10 - 1 %}{{ i }} {% endfor %} 210--DATA-- 211return array('foo' => array(1, 10)) 212--EXPECT-- 2130 1 2 3 4 5 6 7 8 9 10 214a 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 215A 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 2161 2 3 4 5 6 7 8 9 10 2171 2 3 4 5 6 7 8 9 218--TEST-- 219Twig supports the "ends with" operator 220--TEMPLATE-- 221{{ 'foo' ends with 'o' ? 'OK' : 'KO' }} 222{{ not ('foo' ends with 'f') ? 'OK' : 'KO' }} 223{{ not ('foo' ends with 'foowaytoolong') ? 'OK' : 'KO' }} 224{{ 'foo' ends with '' ? 'OK' : 'KO' }} 225{{ '1' ends with true ? 'OK' : 'KO' }} 226{{ 1 ends with true ? 'OK' : 'KO' }} 227{{ 0 ends with false ? 'OK' : 'KO' }} 228{{ '' ends with false ? 'OK' : 'KO' }} 229{{ false ends with false ? 'OK' : 'KO' }} 230{{ false ends with '' ? 'OK' : 'KO' }} 231--DATA-- 232return array() 233--EXPECT-- 234OK 235OK 236OK 237OK 238KO 239KO 240KO 241KO 242KO 243KO 244--TEST-- 245Twig supports grouping of expressions 246--TEMPLATE-- 247{{ (2 + 2) / 2 }} 248--DATA-- 249return array() 250--EXPECT-- 2512 252--TEST-- 253Twig supports literals 254--TEMPLATE-- 2551 {{ true }} 2562 {{ TRUE }} 2573 {{ false }} 2584 {{ FALSE }} 2595 {{ none }} 2606 {{ NONE }} 2617 {{ null }} 2628 {{ NULL }} 263--DATA-- 264return array() 265--EXPECT-- 2661 1 2672 1 2683 2694 2705 2716 2727 2738 274--TEST-- 275Twig supports __call() for attributes 276--TEMPLATE-- 277{{ foo.foo }} 278{{ foo.bar }} 279--EXPECT-- 280foo_from_call 281bar_from_getbar 282--TEST-- 283Twig supports the "matches" operator 284--TEMPLATE-- 285{{ 'foo' matches '/o/' ? 'OK' : 'KO' }} 286{{ 'foo' matches '/^fo/' ? 'OK' : 'KO' }} 287{{ 'foo' matches '/O/i' ? 'OK' : 'KO' }} 288--DATA-- 289return array() 290--EXPECT-- 291OK 292OK 293OK 294--TEST-- 295Twig supports method calls 296--TEMPLATE-- 297{{ items.foo.foo }} 298{{ items.foo.getFoo() }} 299{{ items.foo.bar }} 300{{ items.foo['bar'] }} 301{{ items.foo.bar('a', 43) }} 302{{ items.foo.bar(foo) }} 303{{ items.foo.self.foo() }} 304{{ items.foo.is }} 305{{ items.foo.in }} 306{{ items.foo.not }} 307--DATA-- 308return array('foo' => 'bar', 'items' => array('foo' => new TwigTestFoo(), 'bar' => 'foo')) 309--CONFIG-- 310return array('strict_variables' => false) 311--EXPECT-- 312foo 313foo 314bar 315 316bar_a-43 317bar_bar 318foo 319is 320in 321not 322--TEST-- 323Twig allows to use named operators as variable names 324--TEMPLATE-- 325{% for match in matches %} 326 {{- match }} 327{% endfor %} 328{{ in }} 329{{ is }} 330--DATA-- 331return array('matches' => array(1, 2, 3), 'in' => 'in', 'is' => 'is') 332--EXPECT-- 3331 3342 3353 336in 337is 338--TEST-- 339Twig parses postfix expressions 340--TEMPLATE-- 341{% import _self as macros %} 342 343{% macro foo() %}foo{% endmacro %} 344 345{{ 'a' }} 346{{ 'a'|upper }} 347{{ ('a')|upper }} 348{{ -1|upper }} 349{{ macros.foo() }} 350{{ (macros).foo() }} 351--DATA-- 352return array(); 353--EXPECT-- 354a 355A 356A 357-1 358foo 359foo 360--TEST-- 361Twig supports the "same as" operator 362--TEMPLATE-- 363{{ 1 is same as(1) ? 'OK' }} 364{{ 1 is not same as(true) ? 'OK' }} 365{{ 1 is same as(1) ? 'OK' }} 366{{ 1 is not same as(true) ? 'OK' }} 367{{ 1 is same as (1) ? 'OK' }} 368{{ 1 is not 369 same 370 as 371 (true) ? 'OK' }} 372--DATA-- 373return array() 374--EXPECT-- 375OK 376OK 377OK 378OK 379OK 380OK 381--TEST-- 382Twig supports the "starts with" operator 383--TEMPLATE-- 384{{ 'foo' starts with 'f' ? 'OK' : 'KO' }} 385{{ not ('foo' starts with 'oo') ? 'OK' : 'KO' }} 386{{ not ('foo' starts with 'foowaytoolong') ? 'OK' : 'KO' }} 387{{ 'foo' starts with 'f' ? 'OK' : 'KO' }} 388{{ 'foo' starts 389with 'f' ? 'OK' : 'KO' }} 390{{ 'foo' starts with '' ? 'OK' : 'KO' }} 391{{ '1' starts with true ? 'OK' : 'KO' }} 392{{ '' starts with false ? 'OK' : 'KO' }} 393{{ 'a' starts with false ? 'OK' : 'KO' }} 394{{ false starts with '' ? 'OK' : 'KO' }} 395--DATA-- 396return array() 397--EXPECT-- 398OK 399OK 400OK 401OK 402OK 403OK 404KO 405KO 406KO 407KO 408--TEST-- 409Twig supports string interpolation 410--TEMPLATE-- 411{# "foo #{"foo #{bar} baz"} baz" #} 412{# "foo #{bar}#{bar} baz" #} 413--DATA-- 414return array('bar' => 'BAR'); 415--EXPECT-- 416foo foo BAR baz baz 417foo BARBAR baz 418--TEST-- 419Twig supports the ternary operator 420--TEMPLATE-- 421{{ 1 ? 'YES' }} 422{{ 0 ? 'YES' }} 423--DATA-- 424return array() 425--EXPECT-- 426YES 427 428--TEST-- 429Twig supports the ternary operator 430--TEMPLATE-- 431{{ 'YES' ?: 'NO' }} 432{{ 0 ?: 'NO' }} 433--DATA-- 434return array() 435--EXPECT-- 436YES 437NO 438--TEST-- 439Twig supports the ternary operator 440--TEMPLATE-- 441{{ 1 ? 'YES' : 'NO' }} 442{{ 0 ? 'YES' : 'NO' }} 443{{ 0 ? 'YES' : (1 ? 'YES1' : 'NO1') }} 444{{ 0 ? 'YES' : (0 ? 'YES1' : 'NO1') }} 445{{ 1 == 1 ? 'foo<br />':'' }} 446{{ foo ~ (bar ? ('-' ~ bar) : '') }} 447--DATA-- 448return array('foo' => 'foo', 'bar' => 'bar') 449--EXPECT-- 450YES 451NO 452YES1 453NO1 454foo<br /> 455foo-bar 456--TEST-- 457Twig does not allow to use two-word named operators as variable names 458--TEMPLATE-- 459{{ starts with }} 460--DATA-- 461return array() 462--EXCEPTION-- 463Twig_Error_Syntax: Unexpected token "operator" of value "starts with" in "index.twig" at line 2 464--TEST-- 465Twig unary operators precedence 466--TEMPLATE-- 467{{ -1 - 1 }} 468{{ -1 - -1 }} 469{{ -1 * -1 }} 470{{ 4 / -1 * 5 }} 471--DATA-- 472return array() 473--EXPECT-- 474-2 4750 4761 477-20 478--TEST-- 479Twig supports unary operators (not, -, +) 480--TEMPLATE-- 481{{ not 1 }}/{{ not 0 }} 482{{ +1 + 1 }}/{{ -1 - 1 }} 483{{ not (false or true) }} 484--DATA-- 485return array() 486--EXPECT-- 487/1 4882/-2 489 490--TEST-- 491"abs" filter 492--TEMPLATE-- 493{{ (-5.5)|abs }} 494{{ (-5)|abs }} 495{{ (-0)|abs }} 496{{ 0|abs }} 497{{ 5|abs }} 498{{ 5.5|abs }} 499{{ number1|abs }} 500{{ number2|abs }} 501{{ number3|abs }} 502{{ number4|abs }} 503{{ number5|abs }} 504{{ number6|abs }} 505--DATA-- 506return array('number1' => -5.5, 'number2' => -5, 'number3' => -0, 'number4' => 0, 'number5' => 5, 'number6' => 5.5) 507--EXPECT-- 5085.5 5095 5100 5110 5125 5135.5 5145.5 5155 5160 5170 5185 5195.5 520--TEST-- 521"batch" filter 522--TEMPLATE-- 523{% for row in items|batch(3.1) %} 524 <div class=row> 525 {% for column in row %} 526 <div class=item>{{ column }}</div> 527 {% endfor %} 528 </div> 529{% endfor %} 530--DATA-- 531return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')) 532--EXPECT-- 533<div class=row> 534 <div class=item>a</div> 535 <div class=item>b</div> 536 <div class=item>c</div> 537 <div class=item>d</div> 538 </div> 539 <div class=row> 540 <div class=item>e</div> 541 <div class=item>f</div> 542 <div class=item>g</div> 543 <div class=item>h</div> 544 </div> 545 <div class=row> 546 <div class=item>i</div> 547 <div class=item>j</div> 548 </div> 549--TEST-- 550"batch" filter 551--TEMPLATE-- 552{% for row in items|batch(3) %} 553 <div class=row> 554 {% for column in row %} 555 <div class=item>{{ column }}</div> 556 {% endfor %} 557 </div> 558{% endfor %} 559--DATA-- 560return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')) 561--EXPECT-- 562<div class=row> 563 <div class=item>a</div> 564 <div class=item>b</div> 565 <div class=item>c</div> 566 </div> 567 <div class=row> 568 <div class=item>d</div> 569 <div class=item>e</div> 570 <div class=item>f</div> 571 </div> 572 <div class=row> 573 <div class=item>g</div> 574 <div class=item>h</div> 575 <div class=item>i</div> 576 </div> 577 <div class=row> 578 <div class=item>j</div> 579 </div> 580--TEST-- 581"batch" filter 582--TEMPLATE-- 583<table> 584{% for row in items|batch(3, '') %} 585 <tr> 586 {% for column in row %} 587 <td>{{ column }}</td> 588 {% endfor %} 589 </tr> 590{% endfor %} 591</table> 592--DATA-- 593return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')) 594--EXPECT-- 595<table> 596 <tr> 597 <td>a</td> 598 <td>b</td> 599 <td>c</td> 600 </tr> 601 <tr> 602 <td>d</td> 603 <td>e</td> 604 <td>f</td> 605 </tr> 606 <tr> 607 <td>g</td> 608 <td>h</td> 609 <td>i</td> 610 </tr> 611 <tr> 612 <td>j</td> 613 <td></td> 614 <td></td> 615 </tr> 616</table> 617--TEST-- 618"batch" filter 619--TEMPLATE-- 620{% for row in items|batch(3, 'fill') %} 621 <div class=row> 622 {% for column in row %} 623 <div class=item>{{ column }}</div> 624 {% endfor %} 625 </div> 626{% endfor %} 627--DATA-- 628return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l')) 629--EXPECT-- 630<div class=row> 631 <div class=item>a</div> 632 <div class=item>b</div> 633 <div class=item>c</div> 634 </div> 635 <div class=row> 636 <div class=item>d</div> 637 <div class=item>e</div> 638 <div class=item>f</div> 639 </div> 640 <div class=row> 641 <div class=item>g</div> 642 <div class=item>h</div> 643 <div class=item>i</div> 644 </div> 645 <div class=row> 646 <div class=item>j</div> 647 <div class=item>k</div> 648 <div class=item>l</div> 649 </div> 650--TEST-- 651"batch" filter 652--TEMPLATE-- 653<table> 654{% for row in items|batch(3, 'fill') %} 655 <tr> 656 {% for column in row %} 657 <td>{{ column }}</td> 658 {% endfor %} 659 </tr> 660{% endfor %} 661</table> 662--DATA-- 663return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')) 664--EXPECT-- 665<table> 666 <tr> 667 <td>a</td> 668 <td>b</td> 669 <td>c</td> 670 </tr> 671 <tr> 672 <td>d</td> 673 <td>e</td> 674 <td>f</td> 675 </tr> 676 <tr> 677 <td>g</td> 678 <td>h</td> 679 <td>i</td> 680 </tr> 681 <tr> 682 <td>j</td> 683 <td>fill</td> 684 <td>fill</td> 685 </tr> 686</table> 687--TEST-- 688"convert_encoding" filter 689--CONDITION-- 690function_exists('iconv') || function_exists('mb_convert_encoding') 691--TEMPLATE-- 692{{ "愛していますか?"|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }} 693--DATA-- 694return array() 695--EXPECT-- 696愛していますか? 697--TEST-- 698"date" filter (interval support as of PHP 5.3) 699--CONDITION-- 700version_compare(phpversion(), '5.3.0', '>=') 701--TEMPLATE-- 702{{ date2|date }} 703{{ date2|date('%d days') }} 704--DATA-- 705date_default_timezone_set('UTC'); 706$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours'); 707return array( 708 'date2' => new DateInterval('P2D'), 709) 710--EXPECT-- 7112 days 0 hours 7122 days 713--TEST-- 714"date" filter 715--TEMPLATE-- 716{{ date1|date }} 717{{ date1|date('d/m/Y') }} 718--DATA-- 719date_default_timezone_set('UTC'); 720$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours'); 721return array( 722 'date1' => mktime(13, 45, 0, 10, 4, 2010), 723) 724--EXPECT-- 7252010-10-04 72604/10/2010 727--TEST-- 728"date" filter 729--CONDITION-- 730version_compare(phpversion(), '5.5.0', '>=') 731--TEMPLATE-- 732{{ date1|date }} 733{{ date1|date('d/m/Y') }} 734{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }} 735{{ date1|date('d/m/Y H:i:s', timezone1) }} 736{{ date1|date('d/m/Y H:i:s') }} 737 738{{ date2|date('d/m/Y H:i:s P', 'Europe/Paris') }} 739{{ date2|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }} 740{{ date2|date('d/m/Y H:i:s P', false) }} 741{{ date2|date('e', 'Europe/Paris') }} 742{{ date2|date('e', false) }} 743--DATA-- 744date_default_timezone_set('Europe/Paris'); 745return array( 746 'date1' => new DateTimeImmutable('2010-10-04 13:45'), 747 'date2' => new DateTimeImmutable('2010-10-04 13:45', new DateTimeZone('America/New_York')), 748 'timezone1' => new DateTimeZone('America/New_York'), 749) 750--EXPECT-- 751October 4, 2010 13:45 75204/10/2010 75304/10/2010 19:45:00 75404/10/2010 07:45:00 75504/10/2010 13:45:00 756 75704/10/2010 19:45:00 +02:00 75805/10/2010 01:45:00 +08:00 75904/10/2010 13:45:00 -04:00 760Europe/Paris 761America/New_York 762--TEST-- 763"date" filter (interval support as of PHP 5.3) 764--CONDITION-- 765version_compare(phpversion(), '5.3.0', '>=') 766--TEMPLATE-- 767{{ date1|date }} 768{{ date1|date('%d days %h hours') }} 769{{ date1|date('%d days %h hours', timezone1) }} 770--DATA-- 771date_default_timezone_set('UTC'); 772return array( 773 'date1' => new DateInterval('P2D'), 774 // This should have no effect on DateInterval formatting 775 'timezone1' => new DateTimeZone('America/New_York'), 776) 777--EXPECT-- 7782 days 7792 days 0 hours 7802 days 0 hours 781--TEST-- 782"date_modify" filter 783--TEMPLATE-- 784{{ date1|date_modify('-1day')|date('Y-m-d H:i:s') }} 785{{ date2|date_modify('-1day')|date('Y-m-d H:i:s') }} 786--DATA-- 787date_default_timezone_set('UTC'); 788return array( 789 'date1' => '2010-10-04 13:45', 790 'date2' => new DateTime('2010-10-04 13:45'), 791) 792--EXPECT-- 7932010-10-03 13:45:00 7942010-10-03 13:45:00 795--TEST-- 796"date" filter 797--TEMPLATE-- 798{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }} 799{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }} 800{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }} 801--DATA-- 802date_default_timezone_set('UTC'); 803return array('date' => mktime(13, 45, 0, 10, 4, 2010)) 804--EXPECT-- 80504/10/2010 08:45:00 -05:00 80604/10/2010 08:45:00 -05:00 80704/10/2010 08:45:00 -05:00 808--TEST-- 809"date" filter 810--TEMPLATE-- 811{{ date1|date }} 812{{ date1|date('d/m/Y') }} 813{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }} 814{{ date1|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }} 815{{ date1|date('d/m/Y H:i:s P', 'America/Chicago') }} 816{{ date1|date('e') }} 817{{ date1|date('d/m/Y H:i:s') }} 818 819{{ date2|date }} 820{{ date2|date('d/m/Y') }} 821{{ date2|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }} 822{{ date2|date('d/m/Y H:i:s', timezone1) }} 823{{ date2|date('d/m/Y H:i:s') }} 824 825{{ date3|date }} 826{{ date3|date('d/m/Y') }} 827 828{{ date4|date }} 829{{ date4|date('d/m/Y') }} 830 831{{ date5|date }} 832{{ date5|date('d/m/Y') }} 833 834{{ date6|date('d/m/Y H:i:s P', 'Europe/Paris') }} 835{{ date6|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }} 836{{ date6|date('d/m/Y H:i:s P', false) }} 837{{ date6|date('e', 'Europe/Paris') }} 838{{ date6|date('e', false) }} 839 840{{ date7|date }} 841--DATA-- 842date_default_timezone_set('Europe/Paris'); 843return array( 844 'date1' => mktime(13, 45, 0, 10, 4, 2010), 845 'date2' => new DateTime('2010-10-04 13:45'), 846 'date3' => '2010-10-04 13:45', 847 'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT 848 'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(), 849 'date6' => new DateTime('2010-10-04 13:45', new DateTimeZone('America/New_York')), 850 'date7' => '2010-01-28T15:00:00+05:00', 851 'timezone1' => new DateTimeZone('America/New_York'), 852) 853--EXPECT-- 854October 4, 2010 13:45 85504/10/2010 85604/10/2010 19:45:00 85704/10/2010 19:45:00 +08:00 85804/10/2010 06:45:00 -05:00 859Europe/Paris 86004/10/2010 13:45:00 861 862October 4, 2010 13:45 86304/10/2010 86404/10/2010 19:45:00 86504/10/2010 07:45:00 86604/10/2010 13:45:00 867 868October 4, 2010 13:45 86904/10/2010 870 871October 4, 2010 15:45 87204/10/2010 873 874January 2, 1964 04:04 87502/01/1964 876 87704/10/2010 19:45:00 +02:00 87805/10/2010 01:45:00 +08:00 87904/10/2010 13:45:00 -04:00 880Europe/Paris 881America/New_York 882 883January 28, 2010 11:00 884--TEST-- 885"default" filter 886--TEMPLATE-- 887Variable: 888{{ definedVar |default('default') is same as('default') ? 'ko' : 'ok' }} 889{{ zeroVar |default('default') is same as('default') ? 'ko' : 'ok' }} 890{{ emptyVar |default('default') is same as('default') ? 'ok' : 'ko' }} 891{{ nullVar |default('default') is same as('default') ? 'ok' : 'ko' }} 892{{ undefinedVar |default('default') is same as('default') ? 'ok' : 'ko' }} 893Array access: 894{{ nested.definedVar |default('default') is same as('default') ? 'ko' : 'ok' }} 895{{ nested['definedVar'] |default('default') is same as('default') ? 'ko' : 'ok' }} 896{{ nested.zeroVar |default('default') is same as('default') ? 'ko' : 'ok' }} 897{{ nested.emptyVar |default('default') is same as('default') ? 'ok' : 'ko' }} 898{{ nested.nullVar |default('default') is same as('default') ? 'ok' : 'ko' }} 899{{ nested.undefinedVar |default('default') is same as('default') ? 'ok' : 'ko' }} 900{{ nested['undefinedVar'] |default('default') is same as('default') ? 'ok' : 'ko' }} 901{{ undefinedVar.foo |default('default') is same as('default') ? 'ok' : 'ko' }} 902Plain values: 903{{ 'defined' |default('default') is same as('default') ? 'ko' : 'ok' }} 904{{ 0 |default('default') is same as('default') ? 'ko' : 'ok' }} 905{{ '' |default('default') is same as('default') ? 'ok' : 'ko' }} 906{{ null |default('default') is same as('default') ? 'ok' : 'ko' }} 907Precedence: 908{{ 'o' ~ nullVar |default('k') }} 909{{ 'o' ~ nested.nullVar |default('k') }} 910Object methods: 911{{ object.foo |default('default') is same as('default') ? 'ko' : 'ok' }} 912{{ object.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }} 913{{ object.getFoo() |default('default') is same as('default') ? 'ko' : 'ok' }} 914{{ object.getFoo('a') |default('default') is same as('default') ? 'ko' : 'ok' }} 915{{ object.undefinedMethod() |default('default') is same as('default') ? 'ok' : 'ko' }} 916{{ object.undefinedMethod('a') |default('default') is same as('default') ? 'ok' : 'ko' }} 917Deep nested: 918{{ nested.undefinedVar.foo.bar |default('default') is same as('default') ? 'ok' : 'ko' }} 919{{ nested.definedArray.0 |default('default') is same as('default') ? 'ko' : 'ok' }} 920{{ nested['definedArray'][0] |default('default') is same as('default') ? 'ko' : 'ok' }} 921{{ object.self.foo |default('default') is same as('default') ? 'ko' : 'ok' }} 922{{ object.self.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }} 923{{ object.undefinedMethod.self |default('default') is same as('default') ? 'ok' : 'ko' }} 924--DATA-- 925return array( 926 'definedVar' => 'defined', 927 'zeroVar' => 0, 928 'emptyVar' => '', 929 'nullVar' => null, 930 'nested' => array( 931 'definedVar' => 'defined', 932 'zeroVar' => 0, 933 'emptyVar' => '', 934 'nullVar' => null, 935 'definedArray' => array(0), 936 ), 937 'object' => new TwigTestFoo(), 938) 939--CONFIG-- 940return array('strict_variables' => false) 941--EXPECT-- 942Variable: 943ok 944ok 945ok 946ok 947ok 948Array access: 949ok 950ok 951ok 952ok 953ok 954ok 955ok 956ok 957Plain values: 958ok 959ok 960ok 961ok 962Precedence: 963ok 964ok 965Object methods: 966ok 967ok 968ok 969ok 970ok 971ok 972Deep nested: 973ok 974ok 975ok 976ok 977ok 978ok 979--DATA-- 980return array( 981 'definedVar' => 'defined', 982 'zeroVar' => 0, 983 'emptyVar' => '', 984 'nullVar' => null, 985 'nested' => array( 986 'definedVar' => 'defined', 987 'zeroVar' => 0, 988 'emptyVar' => '', 989 'nullVar' => null, 990 'definedArray' => array(0), 991 ), 992 'object' => new TwigTestFoo(), 993) 994--CONFIG-- 995return array('strict_variables' => true) 996--EXPECT-- 997Variable: 998ok 999ok 1000ok 1001ok 1002ok 1003Array access: 1004ok 1005ok 1006ok 1007ok 1008ok 1009ok 1010ok 1011ok 1012Plain values: 1013ok 1014ok 1015ok 1016ok 1017Precedence: 1018ok 1019ok 1020Object methods: 1021ok 1022ok 1023ok 1024ok 1025ok 1026ok 1027Deep nested: 1028ok 1029ok 1030ok 1031ok 1032ok 1033ok 1034--TEST-- 1035dynamic filter 1036--TEMPLATE-- 1037{{ 'bar'|foo_path }} 1038{{ 'bar'|a_foo_b_bar }} 1039--DATA-- 1040return array() 1041--EXPECT-- 1042foo/bar 1043a/b/bar 1044--TEST-- 1045"escape" filter does not escape with the html strategy when using the html_attr strategy 1046--TEMPLATE-- 1047{{ '<br />'|escape('html_attr') }} 1048--DATA-- 1049return array() 1050--EXPECT-- 1051<br /> 1052--TEST-- 1053"escape" filter 1054--TEMPLATE-- 1055{{ "愛していますか? <br />"|e }} 1056--DATA-- 1057return array() 1058--EXPECT-- 1059愛していますか? <br /> 1060--TEST-- 1061"escape" filter 1062--TEMPLATE-- 1063{{ "foo <br />"|e }} 1064--DATA-- 1065return array() 1066--EXPECT-- 1067foo <br /> 1068--TEST-- 1069"first" filter 1070--TEMPLATE-- 1071{{ [1, 2, 3, 4]|first }} 1072{{ {a: 1, b: 2, c: 3, d: 4}|first }} 1073{{ '1234'|first }} 1074{{ arr|first }} 1075{{ 'Ä€é'|first }} 1076{{ ''|first }} 1077--DATA-- 1078return array('arr' => new ArrayObject(array(1, 2, 3, 4))) 1079--EXPECT-- 10801 10811 10821 10831 1084Ä 1085--TEST-- 1086"escape" filter 1087--TEMPLATE-- 1088{% set foo %} 1089 foo<br /> 1090{% endset %} 1091 1092{{ foo|e('html') -}} 1093{{ foo|e('js') }} 1094{% autoescape true %} 1095 {{ foo }} 1096{% endautoescape %} 1097--DATA-- 1098return array() 1099--EXPECT-- 1100 foo<br /> 1101\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A 1102 foo<br /> 1103--TEST-- 1104"format" filter 1105--TEMPLATE-- 1106{{ string|format(foo, 3) }} 1107--DATA-- 1108return array('string' => '%s/%d', 'foo' => 'bar') 1109--EXPECT-- 1110bar/3 1111--TEST-- 1112"join" filter 1113--TEMPLATE-- 1114{{ ["foo", "bar"]|join(', ') }} 1115{{ foo|join(', ') }} 1116{{ bar|join(', ') }} 1117--DATA-- 1118return array('foo' => new TwigTestFoo(), 'bar' => new ArrayObject(array(3, 4))) 1119--EXPECT-- 1120foo, bar 11211, 2 11223, 4 1123--TEST-- 1124"json_encode" filter 1125--TEMPLATE-- 1126{{ "foo"|json_encode|raw }} 1127{{ foo|json_encode|raw }} 1128{{ [foo, "foo"]|json_encode|raw }} 1129--DATA-- 1130return array('foo' => new Twig_Markup('foo', 'UTF-8')) 1131--EXPECT-- 1132"foo" 1133"foo" 1134["foo","foo"] 1135--TEST-- 1136"last" filter 1137--TEMPLATE-- 1138{{ [1, 2, 3, 4]|last }} 1139{{ {a: 1, b: 2, c: 3, d: 4}|last }} 1140{{ '1234'|last }} 1141{{ arr|last }} 1142{{ 'Ä€é'|last }} 1143{{ ''|last }} 1144--DATA-- 1145return array('arr' => new ArrayObject(array(1, 2, 3, 4))) 1146--EXPECT-- 11474 11484 11494 11504 1151é 1152--TEST-- 1153"length" filter 1154--TEMPLATE-- 1155{{ array|length }} 1156{{ string|length }} 1157{{ number|length }} 1158{{ markup|length }} 1159--DATA-- 1160return array('array' => array(1, 4), 'string' => 'foo', 'number' => 1000, 'markup' => new Twig_Markup('foo', 'UTF-8')) 1161--EXPECT-- 11622 11633 11644 11653 1166--TEST-- 1167"length" filter 1168--CONDITION-- 1169function_exists('mb_get_info') 1170--TEMPLATE-- 1171{{ string|length }} 1172{{ markup|length }} 1173--DATA-- 1174return array('string' => 'été', 'markup' => new Twig_Markup('foo', 'UTF-8')) 1175--EXPECT-- 11763 11773 1178--TEST-- 1179"merge" filter 1180--TEMPLATE-- 1181{{ items|merge({'bar': 'foo'})|join }} 1182{{ items|merge({'bar': 'foo'})|keys|join }} 1183{{ {'bar': 'foo'}|merge(items)|join }} 1184{{ {'bar': 'foo'}|merge(items)|keys|join }} 1185{{ numerics|merge([4, 5, 6])|join }} 1186--DATA-- 1187return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3)) 1188--EXPECT-- 1189barfoo 1190foobar 1191foobar 1192barfoo 1193123456 1194--TEST-- 1195"nl2br" filter 1196--TEMPLATE-- 1197{{ "I like Twig.\nYou will like it too.\n\nEverybody like it!"|nl2br }} 1198{{ text|nl2br }} 1199--DATA-- 1200return array('text' => "If you have some <strong>HTML</strong>\nit will be escaped.") 1201--EXPECT-- 1202I like Twig.<br /> 1203You will like it too.<br /> 1204<br /> 1205Everybody like it! 1206If you have some <strong>HTML</strong><br /> 1207it will be escaped. 1208--TEST-- 1209"number_format" filter with defaults. 1210--TEMPLATE-- 1211{{ 20|number_format }} 1212{{ 20.25|number_format }} 1213{{ 20.25|number_format(1) }} 1214{{ 20.25|number_format(2, ',') }} 1215{{ 1020.25|number_format }} 1216{{ 1020.25|number_format(2, ',') }} 1217{{ 1020.25|number_format(2, ',', '.') }} 1218--DATA-- 1219$twig->getExtension('core')->setNumberFormat(2, '!', '='); 1220return array(); 1221--EXPECT-- 122220!00 122320!25 122420!3 122520,25 12261=020!25 12271=020,25 12281.020,25 1229--TEST-- 1230"number_format" filter 1231--TEMPLATE-- 1232{{ 20|number_format }} 1233{{ 20.25|number_format }} 1234{{ 20.25|number_format(2) }} 1235{{ 20.25|number_format(2, ',') }} 1236{{ 1020.25|number_format(2, ',') }} 1237{{ 1020.25|number_format(2, ',', '.') }} 1238--DATA-- 1239return array(); 1240--EXPECT-- 124120 124220 124320.25 124420,25 12451,020,25 12461.020,25 1247--TEST-- 1248"replace" filter 1249--TEMPLATE-- 1250{{ "I like %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }} 1251--DATA-- 1252return array() 1253--EXPECT-- 1254I like foo and bar. 1255--TEST-- 1256"reverse" filter 1257--TEMPLATE-- 1258{{ [1, 2, 3, 4]|reverse|join('') }} 1259{{ '1234évènement'|reverse }} 1260{{ arr|reverse|join('') }} 1261{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }} 1262{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }} 1263{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }} 1264--DATA-- 1265return array('arr' => new ArrayObject(array(1, 2, 3, 4))) 1266--EXPECT-- 12674321 1268tnemenèvé4321 12694321 1270a,c 1271a,c 1272a,c 1273--TEST-- 1274"round" filter 1275--TEMPLATE-- 1276{{ 2.7|round }} 1277{{ 2.1|round }} 1278{{ 2.1234|round(3, 'floor') }} 1279{{ 2.1|round(0, 'ceil') }} 1280 1281{{ 21.3|round(-1)}} 1282{{ 21.3|round(-1, 'ceil')}} 1283{{ 21.3|round(-1, 'floor')}} 1284--DATA-- 1285return array() 1286--EXPECT-- 12873 12882 12892.123 12903 1291 129220 129330 129420 1295--TEST-- 1296"slice" filter 1297--TEMPLATE-- 1298{{ [1, 2, 3, 4][1:2]|join('') }} 1299{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }} 1300{{ [1, 2, 3, 4][start:length]|join('') }} 1301{{ [1, 2, 3, 4]|slice(1, 2)|join('') }} 1302{{ [1, 2, 3, 4]|slice(1, 2)|keys|join('') }} 1303{{ [1, 2, 3, 4]|slice(1, 2, true)|keys|join('') }} 1304{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }} 1305{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|keys|join('') }} 1306{{ '1234'|slice(1, 2) }} 1307{{ '1234'[1:2] }} 1308{{ arr|slice(1, 2)|join('') }} 1309{{ arr[1:2]|join('') }} 1310 1311{{ [1, 2, 3, 4]|slice(1)|join('') }} 1312{{ [1, 2, 3, 4][1:]|join('') }} 1313{{ '1234'|slice(1) }} 1314{{ '1234'[1:] }} 1315{{ '1234'[:1] }} 1316--DATA-- 1317return array('start' => 1, 'length' => 2, 'arr' => new ArrayObject(array(1, 2, 3, 4))) 1318--EXPECT-- 131923 132023 132123 132223 132301 132412 132523 1326bc 132723 132823 132923 133023 1331 1332234 1333234 1334234 1335234 13361 1337--TEST-- 1338"sort" filter 1339--TEMPLATE-- 1340{{ array1|sort|join }} 1341{{ array2|sort|join }} 1342--DATA-- 1343return array('array1' => array(4, 1), 'array2' => array('foo', 'bar')) 1344--EXPECT-- 134514 1346barfoo 1347--TEST-- 1348"split" filter 1349--TEMPLATE-- 1350{{ "one,two,three,four,five"|split(',')|join('-') }} 1351{{ foo|split(',')|join('-') }} 1352{{ foo|split(',', 3)|join('-') }} 1353{{ baz|split('')|join('-') }} 1354{{ baz|split('', 2)|join('-') }} 1355{{ foo|split(',', -2)|join('-') }} 1356--DATA-- 1357return array('foo' => "one,two,three,four,five", 'baz' => '12345',) 1358--EXPECT-- 1359one-two-three-four-five 1360one-two-three-four-five 1361one-two-three,four,five 13621-2-3-4-5 136312-34-5 1364one-two-three--TEST-- 1365"trim" filter 1366--TEMPLATE-- 1367{{ " I like Twig. "|trim }} 1368{{ text|trim }} 1369{{ " foo/"|trim("/") }} 1370--DATA-- 1371return array('text' => " If you have some <strong>HTML</strong> it will be escaped. ") 1372--EXPECT-- 1373I like Twig. 1374If you have some <strong>HTML</strong> it will be escaped. 1375 foo 1376--TEST-- 1377"url_encode" filter for PHP < 5.4 and HHVM 1378--CONDITION-- 1379defined('PHP_QUERY_RFC3986') 1380--TEMPLATE-- 1381{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }} 1382{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }} 1383{{ {}|url_encode|default("default") }} 1384{{ 'spéßi%le%c0d@dspa ce'|url_encode }} 1385--DATA-- 1386return array() 1387--EXPECT-- 1388foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce= 1389foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce= 1390default 1391sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce 1392--TEST-- 1393"url_encode" filter 1394--CONDITION-- 1395defined('PHP_QUERY_RFC3986') 1396--TEMPLATE-- 1397{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }} 1398{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }} 1399{{ {}|url_encode|default("default") }} 1400{{ 'spéßi%le%c0d@dspa ce'|url_encode }} 1401--DATA-- 1402return array() 1403--EXPECT-- 1404foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce= 1405foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce= 1406default 1407sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce 1408--TEST-- 1409"attribute" function 1410--TEMPLATE-- 1411{{ attribute(obj, method) }} 1412{{ attribute(array, item) }} 1413{{ attribute(obj, "bar", ["a", "b"]) }} 1414{{ attribute(obj, "bar", arguments) }} 1415{{ attribute(obj, method) is defined ? 'ok' : 'ko' }} 1416{{ attribute(obj, nonmethod) is defined ? 'ok' : 'ko' }} 1417--DATA-- 1418return array('obj' => new TwigTestFoo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo', 'nonmethod' => 'xxx', 'arguments' => array('a', 'b')) 1419--EXPECT-- 1420foo 1421bar 1422bar_a-b 1423bar_a-b 1424ok 1425ko 1426--TEST-- 1427"block" function 1428--TEMPLATE-- 1429{% extends 'base.twig' %} 1430{% block bar %}BAR{% endblock %} 1431--TEMPLATE(base.twig)-- 1432{% block foo %}{{ block('bar') }}{% endblock %} 1433{% block bar %}BAR_BASE{% endblock %} 1434--DATA-- 1435return array() 1436--EXPECT-- 1437BARBAR 1438--TEST-- 1439"constant" function 1440--TEMPLATE-- 1441{{ constant('DATE_W3C') == expect ? 'true' : 'false' }} 1442{{ constant('ARRAY_AS_PROPS', object) }} 1443--DATA-- 1444return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi'))); 1445--EXPECT-- 1446true 14472 1448--TEST-- 1449"cycle" function 1450--TEMPLATE-- 1451{% for i in 0..6 %} 1452{{ cycle(array1, i) }}-{{ cycle(array2, i) }} 1453{% endfor %} 1454--DATA-- 1455return array('array1' => array('odd', 'even'), 'array2' => array('apple', 'orange', 'citrus')) 1456--EXPECT-- 1457odd-apple 1458even-orange 1459odd-citrus 1460even-apple 1461odd-orange 1462even-citrus 1463odd-apple 1464--TEST-- 1465"date" function 1466--TEMPLATE-- 1467{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }} 1468{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }} 1469--DATA-- 1470date_default_timezone_set('UTC'); 1471return array('date' => mktime(13, 45, 0, 10, 4, 2010)) 1472--EXPECT-- 147304/10/2010 09:45:00 -04:00 147404/10/2010 09:45:00 -04:00 1475--TEST-- 1476"date" function 1477--TEMPLATE-- 1478{{ date() == date('now') ? 'OK' : 'KO' }} 1479{{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} 1480{{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} 1481{{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} 1482{{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} 1483{{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }} 1484--DATA-- 1485date_default_timezone_set('UTC'); 1486return array( 1487 'date1' => mktime(13, 45, 0, 10, 4, 2010), 1488 'date2' => new DateTime('2010-10-04 13:45'), 1489 'date3' => '2010-10-04 13:45', 1490 'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT 1491 'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(), 1492) 1493--EXPECT-- 1494OK 1495OK 1496OK 1497OK 1498OK 1499OK 1500--TEST-- 1501"dump" function, xdebug is not loaded or xdebug <2.2-dev is loaded 1502--CONDITION-- 1503!extension_loaded('xdebug') || (($r = new ReflectionExtension('xdebug')) && version_compare($r->getVersion(), '2.2-dev', '<')) 1504--TEMPLATE-- 1505{{ dump() }} 1506--DATA-- 1507return array('foo' => 'foo', 'bar' => 'bar') 1508--CONFIG-- 1509return array('debug' => true, 'autoescape' => false); 1510--TEST-- 1511"dump" function 1512--CONDITION-- 1513!extension_loaded('xdebug') 1514--TEMPLATE-- 1515{{ dump('foo') }} 1516{{ dump('foo', 'bar') }} 1517--DATA-- 1518return array('foo' => 'foo', 'bar' => 'bar') 1519--CONFIG-- 1520return array('debug' => true, 'autoescape' => false); 1521--EXPECT-- 1522string(3) "foo" 1523 1524string(3) "foo" 1525string(3) "bar" 1526--TEST-- 1527dynamic function 1528--TEMPLATE-- 1529{{ foo_path('bar') }} 1530{{ a_foo_b_bar('bar') }} 1531--DATA-- 1532return array() 1533--EXPECT-- 1534foo/bar 1535a/b/bar 1536--TEST-- 1537"include" function 1538--TEMPLATE-- 1539{% set tmp = include("foo.twig") %} 1540 1541FOO{{ tmp }}BAR 1542--TEMPLATE(foo.twig)-- 1543FOOBAR 1544--DATA-- 1545return array() 1546--EXPECT-- 1547FOO 1548FOOBARBAR 1549--TEST-- 1550"include" function is safe for auto-escaping 1551--TEMPLATE-- 1552{{ include("foo.twig") }} 1553--TEMPLATE(foo.twig)-- 1554<p>Test</p> 1555--DATA-- 1556return array() 1557--EXPECT-- 1558<p>Test</p> 1559--TEST-- 1560"include" function 1561--TEMPLATE-- 1562FOO 1563{{ include("foo.twig") }} 1564 1565BAR 1566--TEMPLATE(foo.twig)-- 1567FOOBAR 1568--DATA-- 1569return array() 1570--EXPECT-- 1571FOO 1572 1573FOOBAR 1574 1575BAR 1576--TEST-- 1577"include" function allows expressions for the template to include 1578--TEMPLATE-- 1579FOO 1580{{ include(foo) }} 1581 1582BAR 1583--TEMPLATE(foo.twig)-- 1584FOOBAR 1585--DATA-- 1586return array('foo' => 'foo.twig') 1587--EXPECT-- 1588FOO 1589 1590FOOBAR 1591 1592BAR 1593--TEST-- 1594"include" function 1595--TEMPLATE-- 1596{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }} 1597{{ include("foo.twig", ignore_missing = true) }} 1598{{ include("foo.twig", ignore_missing = true, variables = {}) }} 1599{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }} 1600--DATA-- 1601return array() 1602--EXPECT-- 1603--TEST-- 1604"include" function 1605--TEMPLATE-- 1606{% extends "base.twig" %} 1607 1608{% block content %} 1609 {{ parent() }} 1610{% endblock %} 1611--TEMPLATE(base.twig)-- 1612{% block content %} 1613 {{ include("foo.twig") }} 1614{% endblock %} 1615--DATA-- 1616return array(); 1617--EXCEPTION-- 1618Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3. 1619--TEST-- 1620"include" function 1621--TEMPLATE-- 1622{{ include("foo.twig") }} 1623--DATA-- 1624return array(); 1625--EXCEPTION-- 1626Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2. 1627--TEST-- 1628"include" tag sandboxed 1629--TEMPLATE-- 1630{{ include("foo.twig", sandboxed = true) }} 1631--TEMPLATE(foo.twig)-- 1632{{ foo|e }} 1633--DATA-- 1634return array() 1635--EXCEPTION-- 1636Twig_Sandbox_SecurityError: Filter "e" is not allowed in "index.twig" at line 2. 1637--TEST-- 1638"include" function accepts Twig_Template instance 1639--TEMPLATE-- 1640{{ include(foo) }} FOO 1641--TEMPLATE(foo.twig)-- 1642BAR 1643--DATA-- 1644return array('foo' => $twig->loadTemplate('foo.twig')) 1645--EXPECT-- 1646BAR FOO 1647--TEST-- 1648"include" function 1649--TEMPLATE-- 1650{{ include(["foo.twig", "bar.twig"]) }} 1651{{- include(["bar.twig", "foo.twig"]) }} 1652--TEMPLATE(foo.twig)-- 1653foo 1654--DATA-- 1655return array() 1656--EXPECT-- 1657foo 1658foo 1659--TEST-- 1660"include" function accept variables and with_context 1661--TEMPLATE-- 1662{{ include("foo.twig") }} 1663{{- include("foo.twig", with_context = false) }} 1664{{- include("foo.twig", {'foo1': 'bar'}) }} 1665{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }} 1666--TEMPLATE(foo.twig)-- 1667{% for k, v in _context %}{{ k }},{% endfor %} 1668--DATA-- 1669return array('foo' => 'bar') 1670--EXPECT-- 1671foo,global,_parent, 1672global,_parent, 1673foo,global,foo1,_parent, 1674foo1,global,_parent, 1675--TEST-- 1676"include" function accept variables 1677--TEMPLATE-- 1678{{ include("foo.twig", {'foo': 'bar'}) }} 1679{{- include("foo.twig", vars) }} 1680--TEMPLATE(foo.twig)-- 1681{{ foo }} 1682--DATA-- 1683return array('vars' => array('foo' => 'bar')) 1684--EXPECT-- 1685bar 1686bar 1687--TEST-- 1688"max" function 1689--TEMPLATE-- 1690{{ max([2, 1, 3, 5, 4]) }} 1691{{ max(2, 1, 3, 5, 4) }} 1692{{ max({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }} 1693--DATA-- 1694return array() 1695--EXPECT-- 16965 16975 1698two 1699--TEST-- 1700"min" function 1701--TEMPLATE-- 1702{{ min(2, 1, 3, 5, 4) }} 1703{{ min([2, 1, 3, 5, 4]) }} 1704{{ min({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }} 1705--DATA-- 1706return array() 1707--EXPECT-- 17081 17091 1710five 1711--TEST-- 1712"range" function 1713--TEMPLATE-- 1714{{ range(low=0+1, high=10+0, step=2)|join(',') }} 1715--DATA-- 1716return array() 1717--EXPECT-- 17181,3,5,7,9 1719--TEST-- 1720"block" function recursively called in a parent template 1721--TEMPLATE-- 1722{% extends "ordered_menu.twig" %} 1723{% block label %}"{{ parent() }}"{% endblock %} 1724{% block list %}{% set class = 'b' %}{{ parent() }}{% endblock %} 1725--TEMPLATE(ordered_menu.twig)-- 1726{% extends "menu.twig" %} 1727{% block list %}{% set class = class|default('a') %}<ol class="{{ class }}">{{ block('children') }}</ol>{% endblock %} 1728--TEMPLATE(menu.twig)-- 1729{% extends "base.twig" %} 1730{% block list %}<ul>{{ block('children') }}</ul>{% endblock %} 1731{% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %} 1732{% block item %}<li>{% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %}</li>{% endblock %} 1733{% block label %}{{ item }}{{ block('unknown') }}{% endblock %} 1734--TEMPLATE(base.twig)-- 1735{{ block('list') }} 1736--DATA-- 1737return array('item' => array('1', '2', array('3.1', array('3.2.1', '3.2.2'), '3.4'))) 1738--EXPECT-- 1739<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> 1740--TEST-- 1741"source" function 1742--TEMPLATE-- 1743FOO 1744{{ source("foo.twig") }} 1745 1746BAR 1747--TEMPLATE(foo.twig)-- 1748{{ foo }}<br /> 1749--DATA-- 1750return array() 1751--EXPECT-- 1752FOO 1753 1754{{ foo }}<br /> 1755 1756BAR 1757--TEST-- 1758"template_from_string" function 1759--TEMPLATE-- 1760{% include template_from_string(template) %} 1761 1762{% include template_from_string("Hello {{ name }}") %} 1763{% include template_from_string('{% extends "parent.twig" %}{% block content %}Hello {{ name }}{% endblock %}') %} 1764--TEMPLATE(parent.twig)-- 1765{% block content %}{% endblock %} 1766--DATA-- 1767return array('name' => 'Fabien', 'template' => "Hello {{ name }}") 1768--EXPECT-- 1769Hello Fabien 1770Hello Fabien 1771Hello Fabien 1772--TEST-- 1773macro 1774--TEMPLATE-- 1775{% from _self import test %} 1776 1777{% macro test(a, b = 'bar') -%} 1778{{ a }}{{ b }} 1779{%- endmacro %} 1780 1781{{ test('foo') }} 1782{{ test('bar', 'foo') }} 1783--DATA-- 1784return array(); 1785--EXPECT-- 1786foobar 1787barfoo 1788--TEST-- 1789macro 1790--TEMPLATE-- 1791{% import _self as macros %} 1792 1793{% macro foo(data) %} 1794 {{ data }} 1795{% endmacro %} 1796 1797{% macro bar() %} 1798 <br /> 1799{% endmacro %} 1800 1801{{ macros.foo(macros.bar()) }} 1802--DATA-- 1803return array(); 1804--EXPECT-- 1805<br /> 1806--TEST-- 1807macro 1808--TEMPLATE-- 1809{% from _self import test %} 1810 1811{% macro test(this) -%} 1812 {{ this }} 1813{%- endmacro %} 1814 1815{{ test(this) }} 1816--DATA-- 1817return array('this' => 'foo'); 1818--EXPECT-- 1819foo 1820--TEST-- 1821macro 1822--TEMPLATE-- 1823{% import _self as test %} 1824{% from _self import test %} 1825 1826{% macro test(a, b) -%} 1827 {{ a|default('a') }}<br /> 1828 {{- b|default('b') }}<br /> 1829{%- endmacro %} 1830 1831{{ test.test() }} 1832{{ test() }} 1833{{ test.test(1, "c") }} 1834{{ test(1, "c") }} 1835--DATA-- 1836return array(); 1837--EXPECT-- 1838a<br />b<br /> 1839a<br />b<br /> 18401<br />c<br /> 18411<br />c<br /> 1842--TEST-- 1843macro with a filter 1844--TEMPLATE-- 1845{% import _self as test %} 1846 1847{% macro test() %} 1848 {% filter escape %}foo<br />{% endfilter %} 1849{% endmacro %} 1850 1851{{ test.test() }} 1852--DATA-- 1853return array(); 1854--EXPECT-- 1855foo<br /> 1856--TEST-- 1857Twig outputs 0 nodes correctly 1858--TEMPLATE-- 1859{{ foo }}0{{ foo }} 1860--DATA-- 1861return array('foo' => 'foo') 1862--EXPECT-- 1863foo0foo 1864--TEST-- 1865error in twig extension 1866--TEMPLATE-- 1867{{ object.region is not null ? object.regionChoices[object.region] }} 1868--EXPECT-- 1869house.region.s 1870--TEST-- 1871Twig is able to deal with SimpleXMLElement instances as variables 1872--CONDITION-- 1873version_compare(phpversion(), '5.3.0', '>=') 1874--TEMPLATE-- 1875Hello '{{ images.image.0.group }}'! 1876{{ images.image.0.group.attributes.myattr }} 1877{{ images.children().image.count() }} 1878{% for image in images %} 1879 - {{ image.group }} 1880{% endfor %} 1881--DATA-- 1882return array('images' => new SimpleXMLElement('<images><image><group myattr="example">foo</group></image><image><group>bar</group></image></images>')) 1883--EXPECT-- 1884Hello 'foo'! 1885example 18862 1887 - foo 1888 - bar 1889--TEST-- 1890Twig does not confuse strings with integers in getAttribute() 1891--TEMPLATE-- 1892{{ hash['2e2'] }} 1893--DATA-- 1894return array('hash' => array('2e2' => 'works')) 1895--EXPECT-- 1896works 1897--TEST-- 1898"autoescape" tag applies escaping on its children 1899--TEMPLATE-- 1900{% autoescape %} 1901{{ var }}<br /> 1902{% endautoescape %} 1903{% autoescape 'html' %} 1904{{ var }}<br /> 1905{% endautoescape %} 1906{% autoescape false %} 1907{{ var }}<br /> 1908{% endautoescape %} 1909{% autoescape true %} 1910{{ var }}<br /> 1911{% endautoescape %} 1912{% autoescape false %} 1913{{ var }}<br /> 1914{% endautoescape %} 1915--DATA-- 1916return array('var' => '<br />') 1917--EXPECT-- 1918<br /><br /> 1919<br /><br /> 1920<br /><br /> 1921<br /><br /> 1922<br /><br /> 1923--TEST-- 1924"autoescape" tag applies escaping on embedded blocks 1925--TEMPLATE-- 1926{% autoescape 'html' %} 1927 {% block foo %} 1928 {{ var }} 1929 {% endblock %} 1930{% endautoescape %} 1931--DATA-- 1932return array('var' => '<br />') 1933--EXPECT-- 1934<br /> 1935--TEST-- 1936"autoescape" tag does not double-escape 1937--TEMPLATE-- 1938{% autoescape 'html' %} 1939{{ var|escape }} 1940{% endautoescape %} 1941--DATA-- 1942return array('var' => '<br />') 1943--EXPECT-- 1944<br /> 1945--TEST-- 1946"autoescape" tag applies escaping after calling functions 1947--TEMPLATE-- 1948 1949autoescape false 1950{% autoescape false %} 1951 1952safe_br 1953{{ safe_br() }} 1954 1955unsafe_br 1956{{ unsafe_br() }} 1957 1958{% endautoescape %} 1959 1960autoescape 'html' 1961{% autoescape 'html' %} 1962 1963safe_br 1964{{ safe_br() }} 1965 1966unsafe_br 1967{{ unsafe_br() }} 1968 1969unsafe_br()|raw 1970{{ (unsafe_br())|raw }} 1971 1972safe_br()|escape 1973{{ (safe_br())|escape }} 1974 1975safe_br()|raw 1976{{ (safe_br())|raw }} 1977 1978unsafe_br()|escape 1979{{ (unsafe_br())|escape }} 1980 1981{% endautoescape %} 1982 1983autoescape js 1984{% autoescape 'js' %} 1985 1986safe_br 1987{{ safe_br() }} 1988 1989{% endautoescape %} 1990--DATA-- 1991return array() 1992--EXPECT-- 1993 1994autoescape false 1995 1996safe_br 1997<br /> 1998 1999unsafe_br 2000<br /> 2001 2002 2003autoescape 'html' 2004 2005safe_br 2006<br /> 2007 2008unsafe_br 2009<br /> 2010 2011unsafe_br()|raw 2012<br /> 2013 2014safe_br()|escape 2015<br /> 2016 2017safe_br()|raw 2018<br /> 2019 2020unsafe_br()|escape 2021<br /> 2022 2023 2024autoescape js 2025 2026safe_br 2027\x3Cbr\x20\x2F\x3E 2028--TEST-- 2029"autoescape" tag does not apply escaping on literals 2030--TEMPLATE-- 2031{% autoescape 'html' %} 2032 20331. Simple literal 2034{{ "<br />" }} 2035 20362. Conditional expression with only literals 2037{{ true ? "<br />" : "<br>" }} 2038 20393. Conditional expression with a variable 2040{{ true ? "<br />" : someVar }} 2041 20424. Nested conditionals with only literals 2043{{ true ? (true ? "<br />" : "<br>") : "\n" }} 2044 20455. Nested conditionals with a variable 2046{{ true ? (true ? "<br />" : someVar) : "\n" }} 2047 20486. Nested conditionals with a variable marked safe 2049{{ true ? (true ? "<br />" : someVar|raw) : "\n" }} 2050 2051{% endautoescape %} 2052--DATA-- 2053return array() 2054--EXPECT-- 2055 20561. Simple literal 2057<br /> 2058 20592. Conditional expression with only literals 2060<br /> 2061 20623. Conditional expression with a variable 2063<br /> 2064 20654. Nested conditionals with only literals 2066<br /> 2067 20685. Nested conditionals with a variable 2069<br /> 2070 20716. Nested conditionals with a variable marked safe 2072<br /> 2073--TEST-- 2074"autoescape" tags can be nested at will 2075--TEMPLATE-- 2076{{ var }} 2077{% autoescape 'html' %} 2078 {{ var }} 2079 {% autoescape false %} 2080 {{ var }} 2081 {% autoescape 'html' %} 2082 {{ var }} 2083 {% endautoescape %} 2084 {{ var }} 2085 {% endautoescape %} 2086 {{ var }} 2087{% endautoescape %} 2088{{ var }} 2089--DATA-- 2090return array('var' => '<br />') 2091--EXPECT-- 2092<br /> 2093 <br /> 2094 <br /> 2095 <br /> 2096 <br /> 2097 <br /> 2098<br /> 2099--TEST-- 2100"autoescape" tag applies escaping to object method calls 2101--TEMPLATE-- 2102{% autoescape 'html' %} 2103{{ user.name }} 2104{{ user.name|lower }} 2105{{ user }} 2106{% endautoescape %} 2107--EXPECT-- 2108Fabien<br /> 2109fabien<br /> 2110Fabien<br /> 2111--TEST-- 2112"autoescape" tag does not escape when raw is used as a filter 2113--TEMPLATE-- 2114{% autoescape 'html' %} 2115{{ var|raw }} 2116{% endautoescape %} 2117--DATA-- 2118return array('var' => '<br />') 2119--EXPECT-- 2120<br /> 2121--TEST-- 2122"autoescape" tag accepts an escaping strategy 2123--TEMPLATE-- 2124{% autoescape true js %}{{ var }}{% endautoescape %} 2125 2126{% autoescape true html %}{{ var }}{% endautoescape %} 2127 2128{% autoescape 'js' %}{{ var }}{% endautoescape %} 2129 2130{% autoescape 'html' %}{{ var }}{% endautoescape %} 2131--DATA-- 2132return array('var' => '<br />"') 2133--EXPECT-- 2134\x3Cbr\x20\x2F\x3E\x22 2135<br />" 2136\x3Cbr\x20\x2F\x3E\x22 2137<br />" 2138--TEST-- 2139escape types 2140--TEMPLATE-- 2141 21421. autoescape 'html' |escape('js') 2143 2144{% autoescape 'html' %} 2145<a onclick="alert("{{ msg|escape('js') }}")"></a> 2146{% endautoescape %} 2147 21482. autoescape 'html' |escape('js') 2149 2150{% autoescape 'html' %} 2151<a onclick="alert("{{ msg|escape('js') }}")"></a> 2152{% endautoescape %} 2153 21543. autoescape 'js' |escape('js') 2155 2156{% autoescape 'js' %} 2157<a onclick="alert("{{ msg|escape('js') }}")"></a> 2158{% endautoescape %} 2159 21604. no escape 2161 2162{% autoescape false %} 2163<a onclick="alert("{{ msg }}")"></a> 2164{% endautoescape %} 2165 21665. |escape('js')|escape('html') 2167 2168{% autoescape false %} 2169<a onclick="alert("{{ msg|escape('js')|escape('html') }}")"></a> 2170{% endautoescape %} 2171 21726. autoescape 'html' |escape('js')|escape('html') 2173 2174{% autoescape 'html' %} 2175<a onclick="alert("{{ msg|escape('js')|escape('html') }}")"></a> 2176{% endautoescape %} 2177 2178--DATA-- 2179return array('msg' => "<>\n'\"") 2180--EXPECT-- 2181 21821. autoescape 'html' |escape('js') 2183 2184<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a> 2185 21862. autoescape 'html' |escape('js') 2187 2188<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a> 2189 21903. autoescape 'js' |escape('js') 2191 2192<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a> 2193 21944. no escape 2195 2196<a onclick="alert("<> 2197'"")"></a> 2198 21995. |escape('js')|escape('html') 2200 2201<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a> 2202 22036. autoescape 'html' |escape('js')|escape('html') 2204 2205<a onclick="alert("\x3C\x3E\x0A\x27\x22")"></a> 2206 2207--TEST-- 2208"autoescape" tag do not applies escaping on filter arguments 2209--TEMPLATE-- 2210{% autoescape 'html' %} 2211{{ var|nl2br("<br />") }} 2212{{ var|nl2br("<br />"|escape) }} 2213{{ var|nl2br(sep) }} 2214{{ var|nl2br(sep|raw) }} 2215{{ var|nl2br(sep|escape) }} 2216{% endautoescape %} 2217--DATA-- 2218return array('var' => "<Fabien>\nTwig", 'sep' => '<br />') 2219--EXPECT-- 2220<Fabien><br /> 2221Twig 2222<Fabien><br /> 2223Twig 2224<Fabien><br /> 2225Twig 2226<Fabien><br /> 2227Twig 2228<Fabien><br /> 2229Twig 2230--TEST-- 2231"autoescape" tag applies escaping after calling filters 2232--TEMPLATE-- 2233{% autoescape 'html' %} 2234 2235(escape_and_nl2br is an escaper filter) 2236 22371. Don't escape escaper filter output 2238( var is escaped by |escape_and_nl2br, line-breaks are added, 2239 the output is not escaped ) 2240{{ var|escape_and_nl2br }} 2241 22422. Don't escape escaper filter output 2243( var is escaped by |escape_and_nl2br, line-breaks are added, 2244 the output is not escaped, |raw is redundant ) 2245{{ var|escape_and_nl2br|raw }} 2246 22473. Explicit escape 2248( var is escaped by |escape_and_nl2br, line-breaks are added, 2249 the output is explicitly escaped by |escape ) 2250{{ var|escape_and_nl2br|escape }} 2251 22524. Escape non-escaper filter output 2253( var is upper-cased by |upper, 2254 the output is auto-escaped ) 2255{{ var|upper }} 2256 22575. Escape if last filter is not an escaper 2258( var is escaped by |escape_and_nl2br, line-breaks are added, 2259 the output is upper-cased by |upper, 2260 the output is auto-escaped as |upper is not an escaper ) 2261{{ var|escape_and_nl2br|upper }} 2262 22636. Don't escape escaper filter output 2264( var is upper cased by upper, 2265 the output is escaped by |escape_and_nl2br, line-breaks are added, 2266 the output is not escaped as |escape_and_nl2br is an escaper ) 2267{{ var|upper|escape_and_nl2br }} 2268 22697. Escape if last filter is not an escaper 2270( the output of |format is "<b>" ~ var ~ "</b>", 2271 the output is auto-escaped ) 2272{{ "<b>%s</b>"|format(var) }} 2273 22748. Escape if last filter is not an escaper 2275( the output of |format is "<b>" ~ var ~ "</b>", 2276 |raw is redundant, 2277 the output is auto-escaped ) 2278{{ "<b>%s</b>"|raw|format(var) }} 2279 22809. Don't escape escaper filter output 2281( the output of |format is "<b>" ~ var ~ "</b>", 2282 the output is not escaped due to |raw filter at the end ) 2283{{ "<b>%s</b>"|format(var)|raw }} 2284 228510. Don't escape escaper filter output 2286( the output of |format is "<b>" ~ var ~ "</b>", 2287 the output is not escaped due to |raw filter at the end, 2288 the |raw filter on var is redundant ) 2289{{ "<b>%s</b>"|format(var|raw)|raw }} 2290 2291{% endautoescape %} 2292--DATA-- 2293return array('var' => "<Fabien>\nTwig") 2294--EXPECT-- 2295 2296(escape_and_nl2br is an escaper filter) 2297 22981. Don't escape escaper filter output 2299( var is escaped by |escape_and_nl2br, line-breaks are added, 2300 the output is not escaped ) 2301<Fabien><br /> 2302Twig 2303 23042. Don't escape escaper filter output 2305( var is escaped by |escape_and_nl2br, line-breaks are added, 2306 the output is not escaped, |raw is redundant ) 2307<Fabien><br /> 2308Twig 2309 23103. Explicit escape 2311( var is escaped by |escape_and_nl2br, line-breaks are added, 2312 the output is explicitly escaped by |escape ) 2313&lt;Fabien&gt;<br /> 2314Twig 2315 23164. Escape non-escaper filter output 2317( var is upper-cased by |upper, 2318 the output is auto-escaped ) 2319<FABIEN> 2320TWIG 2321 23225. Escape if last filter is not an escaper 2323( var is escaped by |escape_and_nl2br, line-breaks are added, 2324 the output is upper-cased by |upper, 2325 the output is auto-escaped as |upper is not an escaper ) 2326&LT;FABIEN&GT;<BR /> 2327TWIG 2328 23296. Don't escape escaper filter output 2330( var is upper cased by upper, 2331 the output is escaped by |escape_and_nl2br, line-breaks are added, 2332 the output is not escaped as |escape_and_nl2br is an escaper ) 2333<FABIEN><br /> 2334TWIG 2335 23367. Escape if last filter is not an escaper 2337( the output of |format is "<b>" ~ var ~ "</b>", 2338 the output is auto-escaped ) 2339<b><Fabien> 2340Twig</b> 2341 23428. Escape if last filter is not an escaper 2343( the output of |format is "<b>" ~ var ~ "</b>", 2344 |raw is redundant, 2345 the output is auto-escaped ) 2346<b><Fabien> 2347Twig</b> 2348 23499. Don't escape escaper filter output 2350( the output of |format is "<b>" ~ var ~ "</b>", 2351 the output is not escaped due to |raw filter at the end ) 2352<b><Fabien> 2353Twig</b> 2354 235510. Don't escape escaper filter output 2356( the output of |format is "<b>" ~ var ~ "</b>", 2357 the output is not escaped due to |raw filter at the end, 2358 the |raw filter on var is redundant ) 2359<b><Fabien> 2360Twig</b> 2361--TEST-- 2362"autoescape" tag applies escaping after calling filters, and before calling pre_escape filters 2363--TEMPLATE-- 2364{% autoescape 'html' %} 2365 2366(nl2br is pre_escaped for "html" and declared safe for "html") 2367 23681. Pre-escape and don't post-escape 2369( var|escape|nl2br ) 2370{{ var|nl2br }} 2371 23722. Don't double-pre-escape 2373( var|escape|nl2br ) 2374{{ var|escape|nl2br }} 2375 23763. Don't escape safe values 2377( var|raw|nl2br ) 2378{{ var|raw|nl2br }} 2379 23804. Don't escape safe values 2381( var|escape|nl2br|nl2br ) 2382{{ var|nl2br|nl2br }} 2383 23845. Re-escape values that are escaped for an other contexts 2385( var|escape_something|escape|nl2br ) 2386{{ var|escape_something|nl2br }} 2387 23886. Still escape when using filters not declared safe 2389( var|escape|nl2br|upper|escape ) 2390{{ var|nl2br|upper }} 2391 2392{% endautoescape %} 2393--DATA-- 2394return array('var' => "<Fabien>\nTwig") 2395--EXPECT-- 2396 2397(nl2br is pre_escaped for "html" and declared safe for "html") 2398 23991. Pre-escape and don't post-escape 2400( var|escape|nl2br ) 2401<Fabien><br /> 2402Twig 2403 24042. Don't double-pre-escape 2405( var|escape|nl2br ) 2406<Fabien><br /> 2407Twig 2408 24093. Don't escape safe values 2410( var|raw|nl2br ) 2411<Fabien><br /> 2412Twig 2413 24144. Don't escape safe values 2415( var|escape|nl2br|nl2br ) 2416<Fabien><br /><br /> 2417Twig 2418 24195. Re-escape values that are escaped for an other contexts 2420( var|escape_something|escape|nl2br ) 2421<FABIEN><br /> 2422TWIG 2423 24246. Still escape when using filters not declared safe 2425( var|escape|nl2br|upper|escape ) 2426&LT;FABIEN&GT;<BR /> 2427TWIG 2428 2429--TEST-- 2430"autoescape" tag handles filters preserving the safety 2431--TEMPLATE-- 2432{% autoescape 'html' %} 2433 2434(preserves_safety is preserving safety for "html") 2435 24361. Unsafe values are still unsafe 2437( var|preserves_safety|escape ) 2438{{ var|preserves_safety }} 2439 24402. Safe values are still safe 2441( var|escape|preserves_safety ) 2442{{ var|escape|preserves_safety }} 2443 24443. Re-escape values that are escaped for an other contexts 2445( var|escape_something|preserves_safety|escape ) 2446{{ var|escape_something|preserves_safety }} 2447 24484. Still escape when using filters not declared safe 2449( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape ) 2450{{ var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'}) }} 2451 2452{% endautoescape %} 2453--DATA-- 2454return array('var' => "<Fabien>\nTwig") 2455--EXPECT-- 2456 2457(preserves_safety is preserving safety for "html") 2458 24591. Unsafe values are still unsafe 2460( var|preserves_safety|escape ) 2461<FABIEN> 2462TWIG 2463 24642. Safe values are still safe 2465( var|escape|preserves_safety ) 2466<FABIEN> 2467TWIG 2468 24693. Re-escape values that are escaped for an other contexts 2470( var|escape_something|preserves_safety|escape ) 2471<FABIEN> 2472TWIG 2473 24744. Still escape when using filters not declared safe 2475( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape ) 2476&LT;FABPOT&GT; 2477TWIG 2478 2479--TEST-- 2480"block" tag 2481--TEMPLATE-- 2482{% block title1 %}FOO{% endblock %} 2483{% block title2 foo|lower %} 2484--TEMPLATE(foo.twig)-- 2485{% block content %}{% endblock %} 2486--DATA-- 2487return array('foo' => 'bar') 2488--EXPECT-- 2489FOObar 2490--TEST-- 2491"block" tag 2492--TEMPLATE-- 2493{% block content %} 2494 {% block content %} 2495 {% endblock %} 2496{% endblock %} 2497--DATA-- 2498return array() 2499--EXCEPTION-- 2500Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3 2501--TEST-- 2502"§" special chars in a block name 2503--TEMPLATE-- 2504{% block § %} 2505§ 2506{% endblock § %} 2507--DATA-- 2508return array() 2509--EXPECT-- 2510§ 2511--TEST-- 2512"embed" tag 2513--TEMPLATE-- 2514FOO 2515{% embed "foo.twig" %} 2516 {% block c1 %} 2517 {{ parent() }} 2518 block1extended 2519 {% endblock %} 2520{% endembed %} 2521 2522BAR 2523--TEMPLATE(foo.twig)-- 2524A 2525{% block c1 %} 2526 block1 2527{% endblock %} 2528B 2529{% block c2 %} 2530 block2 2531{% endblock %} 2532C 2533--DATA-- 2534return array() 2535--EXPECT-- 2536FOO 2537 2538A 2539 block1 2540 2541 block1extended 2542 B 2543 block2 2544C 2545BAR 2546--TEST-- 2547"embed" tag 2548--TEMPLATE(index.twig)-- 2549FOO 2550{% embed "foo.twig" %} 2551 {% block c1 %} 2552 {{ nothing }} 2553 {% endblock %} 2554{% endembed %} 2555BAR 2556--TEMPLATE(foo.twig)-- 2557{% block c1 %}{% endblock %} 2558--DATA-- 2559return array() 2560--EXCEPTION-- 2561Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5 2562--TEST-- 2563"embed" tag 2564--TEMPLATE-- 2565FOO 2566{% embed "foo.twig" %} 2567 {% block c1 %} 2568 {{ parent() }} 2569 block1extended 2570 {% endblock %} 2571{% endembed %} 2572 2573{% embed "foo.twig" %} 2574 {% block c1 %} 2575 {{ parent() }} 2576 block1extended 2577 {% endblock %} 2578{% endembed %} 2579 2580BAR 2581--TEMPLATE(foo.twig)-- 2582A 2583{% block c1 %} 2584 block1 2585{% endblock %} 2586B 2587{% block c2 %} 2588 block2 2589{% endblock %} 2590C 2591--DATA-- 2592return array() 2593--EXPECT-- 2594FOO 2595 2596A 2597 block1 2598 2599 block1extended 2600 B 2601 block2 2602C 2603 2604A 2605 block1 2606 2607 block1extended 2608 B 2609 block2 2610C 2611BAR 2612--TEST-- 2613"embed" tag 2614--TEMPLATE-- 2615{% embed "foo.twig" %} 2616 {% block c1 %} 2617 {{ parent() }} 2618 {% embed "foo.twig" %} 2619 {% block c1 %} 2620 {{ parent() }} 2621 block1extended 2622 {% endblock %} 2623 {% endembed %} 2624 2625 {% endblock %} 2626{% endembed %} 2627--TEMPLATE(foo.twig)-- 2628A 2629{% block c1 %} 2630 block1 2631{% endblock %} 2632B 2633{% block c2 %} 2634 block2 2635{% endblock %} 2636C 2637--DATA-- 2638return array() 2639--EXPECT-- 2640A 2641 block1 2642 2643 2644A 2645 block1 2646 2647 block1extended 2648 B 2649 block2 2650C 2651 B 2652 block2 2653C 2654--TEST-- 2655"embed" tag 2656--TEMPLATE-- 2657{% extends "base.twig" %} 2658 2659{% block c1 %} 2660 {{ parent() }} 2661 blockc1baseextended 2662{% endblock %} 2663 2664{% block c2 %} 2665 {{ parent() }} 2666 2667 {% embed "foo.twig" %} 2668 {% block c1 %} 2669 {{ parent() }} 2670 block1extended 2671 {% endblock %} 2672 {% endembed %} 2673{% endblock %} 2674--TEMPLATE(base.twig)-- 2675A 2676{% block c1 %} 2677 blockc1base 2678{% endblock %} 2679{% block c2 %} 2680 blockc2base 2681{% endblock %} 2682B 2683--TEMPLATE(foo.twig)-- 2684A 2685{% block c1 %} 2686 block1 2687{% endblock %} 2688B 2689{% block c2 %} 2690 block2 2691{% endblock %} 2692C 2693--DATA-- 2694return array() 2695--EXPECT-- 2696A 2697 blockc1base 2698 2699 blockc1baseextended 2700 blockc2base 2701 2702 2703 2704A 2705 block1 2706 2707 block1extended 2708 B 2709 block2 2710CB--TEST-- 2711"filter" tag applies a filter on its children 2712--TEMPLATE-- 2713{% filter upper %} 2714Some text with a {{ var }} 2715{% endfilter %} 2716--DATA-- 2717return array('var' => 'var') 2718--EXPECT-- 2719SOME TEXT WITH A VAR 2720--TEST-- 2721"filter" tag applies a filter on its children 2722--TEMPLATE-- 2723{% filter json_encode|raw %}test{% endfilter %} 2724--DATA-- 2725return array() 2726--EXPECT-- 2727"test" 2728--TEST-- 2729"filter" tags accept multiple chained filters 2730--TEMPLATE-- 2731{% filter lower|title %} 2732 {{ var }} 2733{% endfilter %} 2734--DATA-- 2735return array('var' => 'VAR') 2736--EXPECT-- 2737 Var 2738--TEST-- 2739"filter" tags can be nested at will 2740--TEMPLATE-- 2741{% filter lower|title %} 2742 {{ var }} 2743 {% filter upper %} 2744 {{ var }} 2745 {% endfilter %} 2746 {{ var }} 2747{% endfilter %} 2748--DATA-- 2749return array('var' => 'var') 2750--EXPECT-- 2751 Var 2752 Var 2753 Var 2754--TEST-- 2755"filter" tag applies the filter on "for" tags 2756--TEMPLATE-- 2757{% filter upper %} 2758{% for item in items %} 2759{{ item }} 2760{% endfor %} 2761{% endfilter %} 2762--DATA-- 2763return array('items' => array('a', 'b')) 2764--EXPECT-- 2765A 2766B 2767--TEST-- 2768"filter" tag applies the filter on "if" tags 2769--TEMPLATE-- 2770{% filter upper %} 2771{% if items %} 2772{{ items|join(', ') }} 2773{% endif %} 2774 2775{% if items.3 is defined %} 2776FOO 2777{% else %} 2778{{ items.1 }} 2779{% endif %} 2780 2781{% if items.3 is defined %} 2782FOO 2783{% elseif items.1 %} 2784{{ items.0 }} 2785{% endif %} 2786 2787{% endfilter %} 2788--DATA-- 2789return array('items' => array('a', 'b')) 2790--EXPECT-- 2791A, B 2792 2793B 2794 2795A 2796--TEST-- 2797"for" tag takes a condition 2798--TEMPLATE-- 2799{% for i in 1..5 if i is odd -%} 2800 {{ loop.index }}.{{ i }}{{ foo.bar }} 2801{% endfor %} 2802--DATA-- 2803return array('foo' => array('bar' => 'X')) 2804--CONFIG-- 2805return array('strict_variables' => false) 2806--EXPECT-- 28071.1X 28082.3X 28093.5X 2810--TEST-- 2811"for" tag keeps the context safe 2812--TEMPLATE-- 2813{% for item in items %} 2814 {% for item in items %} 2815 * {{ item }} 2816 {% endfor %} 2817 * {{ item }} 2818{% endfor %} 2819--DATA-- 2820return array('items' => array('a', 'b')) 2821--EXPECT-- 2822 * a 2823 * b 2824 * a 2825 * a 2826 * b 2827 * b 2828--TEST-- 2829"for" tag can use an "else" clause 2830--TEMPLATE-- 2831{% for item in items %} 2832 * {{ item }} 2833{% else %} 2834 no item 2835{% endfor %} 2836--DATA-- 2837return array('items' => array('a', 'b')) 2838--EXPECT-- 2839 * a 2840 * b 2841--DATA-- 2842return array('items' => array()) 2843--EXPECT-- 2844 no item 2845--DATA-- 2846return array() 2847--CONFIG-- 2848return array('strict_variables' => false) 2849--EXPECT-- 2850 no item 2851--TEST-- 2852"for" tag does not reset inner variables 2853--TEMPLATE-- 2854{% for i in 1..2 %} 2855 {% for j in 0..2 %} 2856 {{k}}{% set k = k+1 %} {{ loop.parent.loop.index }} 2857 {% endfor %} 2858{% endfor %} 2859--DATA-- 2860return array('k' => 0) 2861--EXPECT-- 2862 0 1 2863 1 1 2864 2 1 2865 3 2 2866 4 2 2867 5 2 2868--TEST-- 2869"for" tag can iterate over keys and values 2870--TEMPLATE-- 2871{% for key, item in items %} 2872 * {{ key }}/{{ item }} 2873{% endfor %} 2874--DATA-- 2875return array('items' => array('a', 'b')) 2876--EXPECT-- 2877 * 0/a 2878 * 1/b 2879--TEST-- 2880"for" tag can iterate over keys 2881--TEMPLATE-- 2882{% for key in items|keys %} 2883 * {{ key }} 2884{% endfor %} 2885--DATA-- 2886return array('items' => array('a', 'b')) 2887--EXPECT-- 2888 * 0 2889 * 1 2890--TEST-- 2891"for" tag adds a loop variable to the context locally 2892--TEMPLATE-- 2893{% for item in items %} 2894{% endfor %} 2895{% if loop is not defined %}WORKS{% endif %} 2896--DATA-- 2897return array('items' => array()) 2898--EXPECT-- 2899WORKS 2900--TEST-- 2901"for" tag adds a loop variable to the context 2902--TEMPLATE-- 2903{% for item in items %} 2904 * {{ loop.index }}/{{ loop.index0 }} 2905 * {{ loop.revindex }}/{{ loop.revindex0 }} 2906 * {{ loop.first }}/{{ loop.last }}/{{ loop.length }} 2907 2908{% endfor %} 2909--DATA-- 2910return array('items' => array('a', 'b')) 2911--EXPECT-- 2912 * 1/0 2913 * 2/1 2914 * 1//2 2915 2916 * 2/1 2917 * 1/0 2918 * /1/2 2919--TEST-- 2920"for" tag 2921--TEMPLATE-- 2922{% for i, item in items if loop.last > 0 %} 2923{% endfor %} 2924--DATA-- 2925return array('items' => array('a', 'b')) 2926--EXCEPTION-- 2927Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2 2928--TEST-- 2929"for" tag 2930--TEMPLATE-- 2931{% for i, item in items if i > 0 %} 2932 {{ loop.last }} 2933{% endfor %} 2934--DATA-- 2935return array('items' => array('a', 'b')) 2936--EXCEPTION-- 2937Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3 2938--TEST-- 2939"for" tag can use an "else" clause 2940--TEMPLATE-- 2941{% for item in items %} 2942 {% for item in items1 %} 2943 * {{ item }} 2944 {% else %} 2945 no {{ item }} 2946 {% endfor %} 2947{% else %} 2948 no item1 2949{% endfor %} 2950--DATA-- 2951return array('items' => array('a', 'b'), 'items1' => array()) 2952--EXPECT-- 2953no a 2954 no b 2955--TEST-- 2956"for" tag iterates over iterable and countable objects 2957--TEMPLATE-- 2958{% for item in items %} 2959 * {{ item }} 2960 * {{ loop.index }}/{{ loop.index0 }} 2961 * {{ loop.revindex }}/{{ loop.revindex0 }} 2962 * {{ loop.first }}/{{ loop.last }}/{{ loop.length }} 2963 2964{% endfor %} 2965 2966{% for key, value in items %} 2967 * {{ key }}/{{ value }} 2968{% endfor %} 2969 2970{% for key in items|keys %} 2971 * {{ key }} 2972{% endfor %} 2973--DATA-- 2974class ItemsIteratorCountable implements Iterator, Countable 2975{ 2976 protected $values = array('foo' => 'bar', 'bar' => 'foo'); 2977 public function current() { return current($this->values); } 2978 public function key() { return key($this->values); } 2979 public function next() { return next($this->values); } 2980 public function rewind() { return reset($this->values); } 2981 public function valid() { return false !== current($this->values); } 2982 public function count() { return count($this->values); } 2983} 2984return array('items' => new ItemsIteratorCountable()) 2985--EXPECT-- 2986 * bar 2987 * 1/0 2988 * 2/1 2989 * 1//2 2990 2991 * foo 2992 * 2/1 2993 * 1/0 2994 * /1/2 2995 2996 2997 * foo/bar 2998 * bar/foo 2999 3000 * foo 3001 * bar 3002--TEST-- 3003"for" tag iterates over iterable objects 3004--TEMPLATE-- 3005{% for item in items %} 3006 * {{ item }} 3007 * {{ loop.index }}/{{ loop.index0 }} 3008 * {{ loop.first }} 3009 3010{% endfor %} 3011 3012{% for key, value in items %} 3013 * {{ key }}/{{ value }} 3014{% endfor %} 3015 3016{% for key in items|keys %} 3017 * {{ key }} 3018{% endfor %} 3019--DATA-- 3020class ItemsIterator implements Iterator 3021{ 3022 protected $values = array('foo' => 'bar', 'bar' => 'foo'); 3023 public function current() { return current($this->values); } 3024 public function key() { return key($this->values); } 3025 public function next() { return next($this->values); } 3026 public function rewind() { return reset($this->values); } 3027 public function valid() { return false !== current($this->values); } 3028} 3029return array('items' => new ItemsIterator()) 3030--EXPECT-- 3031 * bar 3032 * 1/0 3033 * 1 3034 3035 * foo 3036 * 2/1 3037 * 3038 3039 3040 * foo/bar 3041 * bar/foo 3042 3043 * foo 3044 * bar 3045--TEST-- 3046"for" tags can be nested 3047--TEMPLATE-- 3048{% for key, item in items %} 3049* {{ key }} ({{ loop.length }}): 3050{% for value in item %} 3051 * {{ value }} ({{ loop.length }}) 3052{% endfor %} 3053{% endfor %} 3054--DATA-- 3055return array('items' => array('a' => array('a1', 'a2', 'a3'), 'b' => array('b1'))) 3056--EXPECT-- 3057* a (2): 3058 * a1 (3) 3059 * a2 (3) 3060 * a3 (3) 3061* b (2): 3062 * b1 (1) 3063--TEST-- 3064"for" tag iterates over item values 3065--TEMPLATE-- 3066{% for item in items %} 3067 * {{ item }} 3068{% endfor %} 3069--DATA-- 3070return array('items' => array('a', 'b')) 3071--EXPECT-- 3072 * a 3073 * b 3074--TEST-- 3075global variables 3076--TEMPLATE-- 3077{% include "included.twig" %} 3078{% from "included.twig" import foobar %} 3079{{ foobar() }} 3080--TEMPLATE(included.twig)-- 3081{% macro foobar() %} 3082called foobar 3083{% endmacro %} 3084--DATA-- 3085return array(); 3086--EXPECT-- 3087called foobar 3088--TEST-- 3089"if" creates a condition 3090--TEMPLATE-- 3091{% if a is defined %} 3092 {{ a }} 3093{% elseif b is defined %} 3094 {{ b }} 3095{% else %} 3096 NOTHING 3097{% endif %} 3098--DATA-- 3099return array('a' => 'a') 3100--EXPECT-- 3101 a 3102--DATA-- 3103return array('b' => 'b') 3104--EXPECT-- 3105 b 3106--DATA-- 3107return array() 3108--EXPECT-- 3109 NOTHING 3110--TEST-- 3111"if" takes an expression as a test 3112--TEMPLATE-- 3113{% if a < 2 %} 3114 A1 3115{% elseif a > 10 %} 3116 A2 3117{% else %} 3118 A3 3119{% endif %} 3120--DATA-- 3121return array('a' => 1) 3122--EXPECT-- 3123 A1 3124--DATA-- 3125return array('a' => 12) 3126--EXPECT-- 3127 A2 3128--DATA-- 3129return array('a' => 7) 3130--EXPECT-- 3131 A3 3132--TEST-- 3133"include" tag 3134--TEMPLATE-- 3135FOO 3136{% include "foo.twig" %} 3137 3138BAR 3139--TEMPLATE(foo.twig)-- 3140FOOBAR 3141--DATA-- 3142return array() 3143--EXPECT-- 3144FOO 3145 3146FOOBAR 3147BAR 3148--TEST-- 3149"include" tag allows expressions for the template to include 3150--TEMPLATE-- 3151FOO 3152{% include foo %} 3153 3154BAR 3155--TEMPLATE(foo.twig)-- 3156FOOBAR 3157--DATA-- 3158return array('foo' => 'foo.twig') 3159--EXPECT-- 3160FOO 3161 3162FOOBAR 3163BAR 3164--TEST-- 3165"include" tag 3166--TEMPLATE-- 3167{% include ["foo.twig", "bar.twig"] ignore missing %} 3168{% include "foo.twig" ignore missing %} 3169{% include "foo.twig" ignore missing with {} %} 3170{% include "foo.twig" ignore missing with {} only %} 3171--DATA-- 3172return array() 3173--EXPECT-- 3174--TEST-- 3175"include" tag 3176--TEMPLATE-- 3177{% extends "base.twig" %} 3178 3179{% block content %} 3180 {{ parent() }} 3181{% endblock %} 3182--TEMPLATE(base.twig)-- 3183{% block content %} 3184 {% include "foo.twig" %} 3185{% endblock %} 3186--DATA-- 3187return array(); 3188--EXCEPTION-- 3189Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3. 3190--TEST-- 3191"include" tag 3192--TEMPLATE-- 3193{% include "foo.twig" %} 3194--DATA-- 3195return array(); 3196--EXCEPTION-- 3197Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2. 3198--TEST-- 3199"include" tag accept variables and only 3200--TEMPLATE-- 3201{% include "foo.twig" %} 3202{% include "foo.twig" only %} 3203{% include "foo.twig" with {'foo1': 'bar'} %} 3204{% include "foo.twig" with {'foo1': 'bar'} only %} 3205--TEMPLATE(foo.twig)-- 3206{% for k, v in _context %}{{ k }},{% endfor %} 3207--DATA-- 3208return array('foo' => 'bar') 3209--EXPECT-- 3210foo,global,_parent, 3211global,_parent, 3212foo,global,foo1,_parent, 3213foo1,global,_parent, 3214--TEST-- 3215"include" tag accepts Twig_Template instance 3216--TEMPLATE-- 3217{% include foo %} FOO 3218--TEMPLATE(foo.twig)-- 3219BAR 3220--DATA-- 3221return array('foo' => $twig->loadTemplate('foo.twig')) 3222--EXPECT-- 3223BAR FOO 3224--TEST-- 3225"include" tag 3226--TEMPLATE-- 3227{% include ["foo.twig", "bar.twig"] %} 3228{% include ["bar.twig", "foo.twig"] %} 3229--TEMPLATE(foo.twig)-- 3230foo 3231--DATA-- 3232return array() 3233--EXPECT-- 3234foo 3235foo 3236--TEST-- 3237"include" tag accept variables 3238--TEMPLATE-- 3239{% include "foo.twig" with {'foo': 'bar'} %} 3240{% include "foo.twig" with vars %} 3241--TEMPLATE(foo.twig)-- 3242{{ foo }} 3243--DATA-- 3244return array('vars' => array('foo' => 'bar')) 3245--EXPECT-- 3246bar 3247bar 3248--TEST-- 3249"extends" tag 3250--TEMPLATE-- 3251{% extends "foo.twig" %} 3252 3253{% block content %} 3254FOO 3255{% endblock %} 3256--TEMPLATE(foo.twig)-- 3257{% block content %}{% endblock %} 3258--DATA-- 3259return array() 3260--EXPECT-- 3261FOO 3262--TEST-- 3263block_expr2 3264--TEMPLATE-- 3265{% extends "base2.twig" %} 3266 3267{% block element -%} 3268 Element: 3269 {{- parent() -}} 3270{% endblock %} 3271--TEMPLATE(base2.twig)-- 3272{% extends "base.twig" %} 3273--TEMPLATE(base.twig)-- 3274{% spaceless %} 3275{% block element -%} 3276 <div> 3277 {%- if item.children is defined %} 3278 {%- for item in item.children %} 3279 {{- block('element') -}} 3280 {% endfor %} 3281 {%- endif -%} 3282 </div> 3283{%- endblock %} 3284{% endspaceless %} 3285--DATA-- 3286return array( 3287 'item' => array( 3288 'children' => array( 3289 null, 3290 null, 3291 ) 3292 ) 3293) 3294--EXPECT-- 3295Element:<div>Element:<div></div>Element:<div></div></div> 3296--TEST-- 3297block_expr 3298--TEMPLATE-- 3299{% extends "base.twig" %} 3300 3301{% block element -%} 3302 Element: 3303 {{- parent() -}} 3304{% endblock %} 3305--TEMPLATE(base.twig)-- 3306{% spaceless %} 3307{% block element -%} 3308 <div> 3309 {%- if item.children is defined %} 3310 {%- for item in item.children %} 3311 {{- block('element') -}} 3312 {% endfor %} 3313 {%- endif -%} 3314 </div> 3315{%- endblock %} 3316{% endspaceless %} 3317--DATA-- 3318return array( 3319 'item' => array( 3320 'children' => array( 3321 null, 3322 null, 3323 ) 3324 ) 3325) 3326--EXPECT-- 3327Element:<div>Element:<div></div>Element:<div></div></div> 3328--TEST-- 3329"extends" tag 3330--TEMPLATE-- 3331{% extends standalone ? foo : 'bar.twig' %} 3332 3333{% block content %}{{ parent() }}FOO{% endblock %} 3334--TEMPLATE(foo.twig)-- 3335{% block content %}FOO{% endblock %} 3336--TEMPLATE(bar.twig)-- 3337{% block content %}BAR{% endblock %} 3338--DATA-- 3339return array('foo' => 'foo.twig', 'standalone' => true) 3340--EXPECT-- 3341FOOFOO 3342--TEST-- 3343"extends" tag 3344--TEMPLATE-- 3345{% extends foo %} 3346 3347{% block content %} 3348FOO 3349{% endblock %} 3350--TEMPLATE(foo.twig)-- 3351{% block content %}{% endblock %} 3352--DATA-- 3353return array('foo' => 'foo.twig') 3354--EXPECT-- 3355FOO 3356--TEST-- 3357"extends" tag 3358--TEMPLATE-- 3359{% extends "foo.twig" %} 3360--TEMPLATE(foo.twig)-- 3361{% block content %}FOO{% endblock %} 3362--DATA-- 3363return array() 3364--EXPECT-- 3365FOO 3366--TEST-- 3367"extends" tag 3368--TEMPLATE-- 3369{% extends ["foo.twig", "bar.twig"] %} 3370--TEMPLATE(bar.twig)-- 3371{% block content %} 3372foo 3373{% endblock %} 3374--DATA-- 3375return array() 3376--EXPECT-- 3377foo 3378--TEST-- 3379"extends" tag 3380--TEMPLATE-- 3381{% extends "layout.twig" %}{% block content %}{{ parent() }}index {% endblock %} 3382--TEMPLATE(layout.twig)-- 3383{% extends "base.twig" %}{% block content %}{{ parent() }}layout {% endblock %} 3384--TEMPLATE(base.twig)-- 3385{% block content %}base {% endblock %} 3386--DATA-- 3387return array() 3388--EXPECT-- 3389base layout index 3390--TEST-- 3391"block" tag 3392--TEMPLATE-- 3393{% block content %} 3394 CONTENT 3395 {%- block subcontent -%} 3396 SUBCONTENT 3397 {%- endblock -%} 3398 ENDCONTENT 3399{% endblock %} 3400--TEMPLATE(foo.twig)-- 3401--DATA-- 3402return array() 3403--EXPECT-- 3404CONTENTSUBCONTENTENDCONTENT 3405--TEST-- 3406"block" tag 3407--TEMPLATE-- 3408{% extends "foo.twig" %} 3409 3410{% block content %} 3411 {% block subcontent %} 3412 {% block subsubcontent %} 3413 SUBSUBCONTENT 3414 {% endblock %} 3415 {% endblock %} 3416{% endblock %} 3417--TEMPLATE(foo.twig)-- 3418{% block content %} 3419 {% block subcontent %} 3420 SUBCONTENT 3421 {% endblock %} 3422{% endblock %} 3423--DATA-- 3424return array() 3425--EXPECT-- 3426SUBSUBCONTENT 3427--TEST-- 3428"extends" tag 3429--TEMPLATE-- 3430{% extends "layout.twig" %} 3431{% block inside %}INSIDE{% endblock inside %} 3432--TEMPLATE(layout.twig)-- 3433{% extends "base.twig" %} 3434{% block body %} 3435 {% block inside '' %} 3436{% endblock body %} 3437--TEMPLATE(base.twig)-- 3438{% block body '' %} 3439--DATA-- 3440return array() 3441--EXPECT-- 3442INSIDE 3443--TEST-- 3444"extends" tag 3445--TEMPLATE-- 3446{% extends foo ? 'foo.twig' : 'bar.twig' %} 3447--TEMPLATE(foo.twig)-- 3448FOO 3449--TEMPLATE(bar.twig)-- 3450BAR 3451--DATA-- 3452return array('foo' => true) 3453--EXPECT-- 3454FOO 3455--DATA-- 3456return array('foo' => false) 3457--EXPECT-- 3458BAR 3459--TEST-- 3460"extends" tag 3461--TEMPLATE-- 3462{% block content %} 3463 {% extends "foo.twig" %} 3464{% endblock %} 3465--EXCEPTION-- 3466Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3 3467--TEST-- 3468"extends" tag 3469--TEMPLATE-- 3470{% extends "base.twig" %} 3471{% block content %}{% include "included.twig" %}{% endblock %} 3472 3473{% block footer %}Footer{% endblock %} 3474--TEMPLATE(included.twig)-- 3475{% extends "base.twig" %} 3476{% block content %}Included Content{% endblock %} 3477--TEMPLATE(base.twig)-- 3478{% block content %}Default Content{% endblock %} 3479 3480{% block footer %}Default Footer{% endblock %} 3481--DATA-- 3482return array() 3483--EXPECT-- 3484Included Content 3485Default Footer 3486Footer 3487--TEST-- 3488"extends" tag 3489--TEMPLATE-- 3490{% extends "foo.twig" %} 3491 3492{% block content %} 3493 {% block inside %} 3494 INSIDE OVERRIDDEN 3495 {% endblock %} 3496 3497 BEFORE 3498 {{ parent() }} 3499 AFTER 3500{% endblock %} 3501--TEMPLATE(foo.twig)-- 3502{% block content %} 3503 BAR 3504{% endblock %} 3505--DATA-- 3506return array() 3507--EXPECT-- 3508 3509INSIDE OVERRIDDEN 3510 3511 BEFORE 3512 BAR 3513 3514 AFTER 3515--TEST-- 3516"extends" tag 3517--TEMPLATE-- 3518{% extends "foo.twig" %} 3519 3520{% block content %}{{ parent() }}FOO{{ parent() }}{% endblock %} 3521--TEMPLATE(foo.twig)-- 3522{% block content %}BAR{% endblock %} 3523--DATA-- 3524return array() 3525--EXPECT-- 3526BARFOOBAR 3527--TEST-- 3528"parent" tag 3529--TEMPLATE-- 3530{% use 'foo.twig' %} 3531 3532{% block content %} 3533 {{ parent() }} 3534{% endblock %} 3535--TEMPLATE(foo.twig)-- 3536{% block content %}BAR{% endblock %} 3537--DATA-- 3538return array() 3539--EXPECT-- 3540BAR 3541--TEST-- 3542"parent" tag 3543--TEMPLATE-- 3544{% block content %} 3545 {{ parent() }} 3546{% endblock %} 3547--EXCEPTION-- 3548Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3 3549--TEST-- 3550"extends" tag accepts Twig_Template instance 3551--TEMPLATE-- 3552{% extends foo %} 3553 3554{% block content %} 3555{{ parent() }}FOO 3556{% endblock %} 3557--TEMPLATE(foo.twig)-- 3558{% block content %}BAR{% endblock %} 3559--DATA-- 3560return array('foo' => $twig->loadTemplate('foo.twig')) 3561--EXPECT-- 3562BARFOO 3563--TEST-- 3564"parent" function 3565--TEMPLATE-- 3566{% extends "parent.twig" %} 3567 3568{% use "use1.twig" %} 3569{% use "use2.twig" %} 3570 3571{% block content_parent %} 3572 {{ parent() }} 3573{% endblock %} 3574 3575{% block content_use1 %} 3576 {{ parent() }} 3577{% endblock %} 3578 3579{% block content_use2 %} 3580 {{ parent() }} 3581{% endblock %} 3582 3583{% block content %} 3584 {{ block('content_use1_only') }} 3585 {{ block('content_use2_only') }} 3586{% endblock %} 3587--TEMPLATE(parent.twig)-- 3588{% block content_parent 'content_parent' %} 3589{% block content_use1 'content_parent' %} 3590{% block content_use2 'content_parent' %} 3591{% block content '' %} 3592--TEMPLATE(use1.twig)-- 3593{% block content_use1 'content_use1' %} 3594{% block content_use2 'content_use1' %} 3595{% block content_use1_only 'content_use1_only' %} 3596--TEMPLATE(use2.twig)-- 3597{% block content_use2 'content_use2' %} 3598{% block content_use2_only 'content_use2_only' %} 3599--DATA-- 3600return array() 3601--EXPECT-- 3602 content_parent 3603 content_use1 3604 content_use2 3605 content_use1_only 3606 content_use2_only 3607--TEST-- 3608"macro" tag 3609--TEMPLATE-- 3610{% import _self as macros %} 3611 3612{{ macros.input('username') }} 3613{{ macros.input('password', null, 'password', 1) }} 3614 3615{% macro input(name, value, type, size) %} 3616 <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}"> 3617{% endmacro %} 3618--DATA-- 3619return array() 3620--EXPECT-- 3621 <input type="text" name="username" value="" size="20"> 3622 3623 <input type="password" name="password" value="" size="1"> 3624--TEST-- 3625"macro" tag supports name for endmacro 3626--TEMPLATE-- 3627{% import _self as macros %} 3628 3629{{ macros.foo() }} 3630{{ macros.bar() }} 3631 3632{% macro foo() %}foo{% endmacro %} 3633{% macro bar() %}bar{% endmacro bar %} 3634--DATA-- 3635return array() 3636--EXPECT-- 3637foo 3638bar 3639 3640--TEST-- 3641"macro" tag 3642--TEMPLATE-- 3643{% import 'forms.twig' as forms %} 3644 3645{{ forms.input('username') }} 3646{{ forms.input('password', null, 'password', 1) }} 3647--TEMPLATE(forms.twig)-- 3648{% macro input(name, value, type, size) %} 3649 <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}"> 3650{% endmacro %} 3651--DATA-- 3652return array() 3653--EXPECT-- 3654 <input type="text" name="username" value="" size="20"> 3655 3656 <input type="password" name="password" value="" size="1"> 3657--TEST-- 3658"macro" tag 3659--TEMPLATE-- 3660{% from 'forms.twig' import foo %} 3661{% from 'forms.twig' import foo as foobar, bar %} 3662 3663{{ foo('foo') }} 3664{{ foobar('foo') }} 3665{{ bar('foo') }} 3666--TEMPLATE(forms.twig)-- 3667{% macro foo(name) %}foo{{ name }}{% endmacro %} 3668{% macro bar(name) %}bar{{ name }}{% endmacro %} 3669--DATA-- 3670return array() 3671--EXPECT-- 3672foofoo 3673foofoo 3674barfoo 3675--TEST-- 3676"macro" tag 3677--TEMPLATE-- 3678{% from 'forms.twig' import foo %} 3679 3680{{ foo('foo') }} 3681{{ foo() }} 3682--TEMPLATE(forms.twig)-- 3683{% macro foo(name) %}{{ name|default('foo') }}{{ global }}{% endmacro %} 3684--DATA-- 3685return array() 3686--EXPECT-- 3687fooglobal 3688fooglobal 3689--TEST-- 3690"macro" tag 3691--TEMPLATE-- 3692{% import _self as forms %} 3693 3694{{ forms.input('username') }} 3695{{ forms.input('password', null, 'password', 1) }} 3696 3697{% macro input(name, value, type, size) %} 3698 <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}"> 3699{% endmacro %} 3700--DATA-- 3701return array() 3702--EXPECT-- 3703 <input type="text" name="username" value="" size="20"> 3704 3705 <input type="password" name="password" value="" size="1"> 3706--TEST-- 3707"raw" tag 3708--TEMPLATE-- 3709{% raw %} 3710{{ foo }} 3711{% endraw %} 3712--DATA-- 3713return array() 3714--EXPECT-- 3715{{ foo }} 3716--TEST-- 3717"raw" tag 3718--TEMPLATE-- 3719{% raw %} 3720{{ foo }} 3721{% endverbatim %} 3722--DATA-- 3723return array() 3724--EXCEPTION-- 3725Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2 3726--TEST-- 3727"raw" tag 3728--TEMPLATE-- 37291*** 3730 3731{%- raw %} 3732 {{ 'bla' }} 3733{% endraw %} 3734 37351*** 37362*** 3737 3738{%- raw -%} 3739 {{ 'bla' }} 3740{% endraw %} 3741 37422*** 37433*** 3744 3745{%- raw -%} 3746 {{ 'bla' }} 3747{% endraw -%} 3748 37493*** 37504*** 3751 3752{%- raw -%} 3753 {{ 'bla' }} 3754{%- endraw %} 3755 37564*** 37575*** 3758 3759{%- raw -%} 3760 {{ 'bla' }} 3761{%- endraw -%} 3762 37635*** 3764--DATA-- 3765return array() 3766--EXPECT-- 37671*** 3768 {{ 'bla' }} 3769 3770 37711*** 37722***{{ 'bla' }} 3773 3774 37752*** 37763***{{ 'bla' }} 37773*** 37784***{{ 'bla' }} 3779 37804*** 37815***{{ 'bla' }}5*** 3782--TEST-- 3783sandbox tag 3784--TEMPLATE-- 3785{%- sandbox %} 3786 {%- include "foo.twig" %} 3787 a 3788{%- endsandbox %} 3789--TEMPLATE(foo.twig)-- 3790foo 3791--EXCEPTION-- 3792Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4 3793--TEST-- 3794sandbox tag 3795--TEMPLATE-- 3796{%- sandbox %} 3797 {%- include "foo.twig" %} 3798 3799 {% if 1 %} 3800 {%- include "foo.twig" %} 3801 {% endif %} 3802{%- endsandbox %} 3803--TEMPLATE(foo.twig)-- 3804foo 3805--EXCEPTION-- 3806Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5 3807--TEST-- 3808sandbox tag 3809--TEMPLATE-- 3810{%- sandbox %} 3811 {%- include "foo.twig" %} 3812{%- endsandbox %} 3813 3814{%- sandbox %} 3815 {%- include "foo.twig" %} 3816 {%- include "foo.twig" %} 3817{%- endsandbox %} 3818 3819{%- sandbox %}{% include "foo.twig" %}{% endsandbox %} 3820--TEMPLATE(foo.twig)-- 3821foo 3822--DATA-- 3823return array() 3824--EXPECT-- 3825foo 3826foo 3827foo 3828foo 3829--TEST-- 3830"set" tag 3831--TEMPLATE-- 3832{% set foo = 'foo' %} 3833{% set bar = 'foo<br />' %} 3834 3835{{ foo }} 3836{{ bar }} 3837 3838{% set foo, bar = 'foo', 'bar' %} 3839 3840{{ foo }}{{ bar }} 3841--DATA-- 3842return array() 3843--EXPECT-- 3844foo 3845foo<br /> 3846 3847 3848foobar 3849--TEST-- 3850"set" tag block empty capture 3851--TEMPLATE-- 3852{% set foo %}{% endset %} 3853 3854{% if foo %}FAIL{% endif %} 3855--DATA-- 3856return array() 3857--EXPECT-- 3858--TEST-- 3859"set" tag block capture 3860--TEMPLATE-- 3861{% set foo %}f<br />o<br />o{% endset %} 3862 3863{{ foo }} 3864--DATA-- 3865return array() 3866--EXPECT-- 3867f<br />o<br />o 3868--TEST-- 3869"set" tag 3870--TEMPLATE-- 3871{% set foo, bar = 'foo' ~ 'bar', 'bar' ~ 'foo' %} 3872 3873{{ foo }} 3874{{ bar }} 3875--DATA-- 3876return array() 3877--EXPECT-- 3878foobar 3879barfoo 3880--TEST-- 3881"spaceless" tag removes whites between HTML tags 3882--TEMPLATE-- 3883{% spaceless %} 3884 3885 <div> <div> foo </div> </div> 3886 3887{% endspaceless %} 3888--DATA-- 3889return array() 3890--EXPECT-- 3891<div><div> foo </div></div> 3892--TEST-- 3893"§" custom tag 3894--TEMPLATE-- 3895{% § %} 3896--DATA-- 3897return array() 3898--EXPECT-- 3899§ 3900--TEST-- 3901Whitespace trimming on tags. 3902--TEMPLATE-- 3903{{ 5 * '{#-'|length }} 3904{{ '{{-'|length * 5 + '{%-'|length }} 3905 3906Trim on control tag: 3907{% for i in range(1, 9) -%} 3908 {{ i }} 3909{%- endfor %} 3910 3911 3912Trim on output tag: 3913{% for i in range(1, 9) %} 3914 {{- i -}} 3915{% endfor %} 3916 3917 3918Trim comments: 3919 3920{#- Invisible -#} 3921 3922After the comment. 3923 3924Trim leading space: 3925{% if leading %} 3926 3927 {{- leading }} 3928{% endif %} 3929 3930{%- if leading %} 3931 {{- leading }} 3932 3933{%- endif %} 3934 3935 3936Trim trailing space: 3937{% if trailing -%} 3938 {{ trailing -}} 3939 3940{% endif -%} 3941 3942Combined: 3943 3944{%- if both -%} 3945<ul> 3946 <li> {{- both -}} </li> 3947</ul> 3948 3949{%- endif -%} 3950 3951end 3952--DATA-- 3953return array('leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both') 3954--EXPECT-- 395515 395618 3957 3958Trim on control tag: 3959123456789 3960 3961Trim on output tag: 3962123456789 3963 3964Trim comments:After the comment. 3965 3966Trim leading space: 3967leading space 3968leading space 3969 3970Trim trailing space: 3971trailing spaceCombined:<ul> 3972 <li>both</li> 3973</ul>end 3974--TEST-- 3975"use" tag 3976--TEMPLATE-- 3977{% use "blocks.twig" with content as foo %} 3978 3979{{ block('foo') }} 3980--TEMPLATE(blocks.twig)-- 3981{% block content 'foo' %} 3982--DATA-- 3983return array() 3984--EXPECT-- 3985foo 3986--TEST-- 3987"use" tag 3988--TEMPLATE-- 3989{% use "blocks.twig" %} 3990 3991{{ block('content') }} 3992--TEMPLATE(blocks.twig)-- 3993{% block content 'foo' %} 3994--DATA-- 3995return array() 3996--EXPECT-- 3997foo 3998--TEST-- 3999"use" tag 4000--TEMPLATE-- 4001{% use "foo.twig" %} 4002--TEMPLATE(foo.twig)-- 4003{% use "bar.twig" %} 4004--TEMPLATE(bar.twig)-- 4005--DATA-- 4006return array() 4007--EXPECT-- 4008--TEST-- 4009"use" tag 4010--TEMPLATE-- 4011{% use "foo.twig" %} 4012 4013{{ block('content') }} 4014{{ block('foo') }} 4015{{ block('bar') }} 4016--TEMPLATE(foo.twig)-- 4017{% use "bar.twig" %} 4018 4019{% block content 'foo' %} 4020{% block foo 'foo' %} 4021--TEMPLATE(bar.twig)-- 4022{% block content 'bar' %} 4023{% block bar 'bar' %} 4024--DATA-- 4025return array() 4026--EXPECT-- 4027foo 4028foo 4029bar 4030--TEST-- 4031"use" tag 4032--TEMPLATE-- 4033{% use "ancestor.twig" %} 4034{% use "parent.twig" %} 4035 4036{{ block('container') }} 4037--TEMPLATE(parent.twig)-- 4038{% block sub_container %} 4039 <div class="overriden_sub_container">overriden sub_container</div> 4040{% endblock %} 4041--TEMPLATE(ancestor.twig)-- 4042{% block container %} 4043 <div class="container">{{ block('sub_container') }}</div> 4044{% endblock %} 4045 4046{% block sub_container %} 4047 <div class="sub_container">sub_container</div> 4048{% endblock %} 4049--DATA-- 4050return array() 4051--EXPECT-- 4052<div class="container"> <div class="overriden_sub_container">overriden sub_container</div> 4053</div> 4054--TEST-- 4055"use" tag 4056--TEMPLATE-- 4057{% use "parent.twig" %} 4058 4059{{ block('container') }} 4060--TEMPLATE(parent.twig)-- 4061{% use "ancestor.twig" %} 4062 4063{% block sub_container %} 4064 <div class="overriden_sub_container">overriden sub_container</div> 4065{% endblock %} 4066--TEMPLATE(ancestor.twig)-- 4067{% block container %} 4068 <div class="container">{{ block('sub_container') }}</div> 4069{% endblock %} 4070 4071{% block sub_container %} 4072 <div class="sub_container">sub_container</div> 4073{% endblock %} 4074--DATA-- 4075return array() 4076--EXPECT-- 4077<div class="container"> <div class="overriden_sub_container">overriden sub_container</div> 4078</div> 4079--TEST-- 4080"use" tag 4081--TEMPLATE-- 4082{% use "foo.twig" with content as foo_content %} 4083{% use "bar.twig" %} 4084 4085{{ block('content') }} 4086{{ block('foo') }} 4087{{ block('bar') }} 4088{{ block('foo_content') }} 4089--TEMPLATE(foo.twig)-- 4090{% block content 'foo' %} 4091{% block foo 'foo' %} 4092--TEMPLATE(bar.twig)-- 4093{% block content 'bar' %} 4094{% block bar 'bar' %} 4095--DATA-- 4096return array() 4097--EXPECT-- 4098bar 4099foo 4100bar 4101foo 4102--TEST-- 4103"use" tag 4104--TEMPLATE-- 4105{% use "foo.twig" %} 4106{% use "bar.twig" %} 4107 4108{{ block('content') }} 4109{{ block('foo') }} 4110{{ block('bar') }} 4111--TEMPLATE(foo.twig)-- 4112{% block content 'foo' %} 4113{% block foo 'foo' %} 4114--TEMPLATE(bar.twig)-- 4115{% block content 'bar' %} 4116{% block bar 'bar' %} 4117--DATA-- 4118return array() 4119--EXPECT-- 4120bar 4121foo 4122bar 4123--TEST-- 4124"use" tag 4125--TEMPLATE-- 4126{% use 'file2.html.twig'%} 4127{% block foobar %} 4128 {{- parent() -}} 4129 Content of block (second override) 4130{% endblock foobar %} 4131--TEMPLATE(file2.html.twig)-- 4132{% use 'file1.html.twig' %} 4133{% block foobar %} 4134 {{- parent() -}} 4135 Content of block (first override) 4136{% endblock foobar %} 4137--TEMPLATE(file1.html.twig)-- 4138{% block foobar -%} 4139 Content of block 4140{% endblock foobar %} 4141--DATA-- 4142return array() 4143--EXPECT-- 4144Content of block 4145Content of block (first override) 4146Content of block (second override) 4147--TEST-- 4148"use" tag 4149--TEMPLATE-- 4150{% use 'file2.html.twig' %} 4151{% use 'file1.html.twig' with foo %} 4152{% block foo %} 4153 {{- parent() -}} 4154 Content of foo (second override) 4155{% endblock foo %} 4156{% block bar %} 4157 {{- parent() -}} 4158 Content of bar (second override) 4159{% endblock bar %} 4160--TEMPLATE(file2.html.twig)-- 4161{% use 'file1.html.twig' %} 4162{% block foo %} 4163 {{- parent() -}} 4164 Content of foo (first override) 4165{% endblock foo %} 4166{% block bar %} 4167 {{- parent() -}} 4168 Content of bar (first override) 4169{% endblock bar %} 4170--TEMPLATE(file1.html.twig)-- 4171{% block foo -%} 4172 Content of foo 4173{% endblock foo %} 4174{% block bar -%} 4175 Content of bar 4176{% endblock bar %} 4177--DATA-- 4178return array() 4179--EXPECT-- 4180Content of foo 4181Content of foo (first override) 4182Content of foo (second override) 4183Content of bar 4184Content of bar (second override) 4185--TEST-- 4186"use" tag 4187--TEMPLATE-- 4188{% use 'file2.html.twig' with foobar as base_base_foobar %} 4189{% block foobar %} 4190 {{- block('base_base_foobar') -}} 4191 Content of block (second override) 4192{% endblock foobar %} 4193--TEMPLATE(file2.html.twig)-- 4194{% use 'file1.html.twig' with foobar as base_foobar %} 4195{% block foobar %} 4196 {{- block('base_foobar') -}} 4197 Content of block (first override) 4198{% endblock foobar %} 4199--TEMPLATE(file1.html.twig)-- 4200{% block foobar -%} 4201 Content of block 4202{% endblock foobar %} 4203--DATA-- 4204return array() 4205--EXPECT-- 4206Content of block 4207Content of block (first override) 4208Content of block (second override) 4209--TEST-- 4210"verbatim" tag 4211--TEMPLATE-- 4212{% verbatim %} 4213{{ foo }} 4214{% endverbatim %} 4215--DATA-- 4216return array() 4217--EXPECT-- 4218{{ foo }} 4219--TEST-- 4220"verbatim" tag 4221--TEMPLATE-- 4222{% verbatim %} 4223{{ foo }} 4224{% endraw %} 4225--DATA-- 4226return array() 4227--EXCEPTION-- 4228Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2 4229--TEST-- 4230"verbatim" tag 4231--TEMPLATE-- 42321*** 4233 4234{%- verbatim %} 4235 {{ 'bla' }} 4236{% endverbatim %} 4237 42381*** 42392*** 4240 4241{%- verbatim -%} 4242 {{ 'bla' }} 4243{% endverbatim %} 4244 42452*** 42463*** 4247 4248{%- verbatim -%} 4249 {{ 'bla' }} 4250{% endverbatim -%} 4251 42523*** 42534*** 4254 4255{%- verbatim -%} 4256 {{ 'bla' }} 4257{%- endverbatim %} 4258 42594*** 42605*** 4261 4262{%- verbatim -%} 4263 {{ 'bla' }} 4264{%- endverbatim -%} 4265 42665*** 4267--DATA-- 4268return array() 4269--EXPECT-- 42701*** 4271 {{ 'bla' }} 4272 4273 42741*** 42752***{{ 'bla' }} 4276 4277 42782*** 42793***{{ 'bla' }} 42803*** 42814***{{ 'bla' }} 4282 42834*** 42845***{{ 'bla' }}5*** 4285--TEST-- 4286array index test 4287--TEMPLATE-- 4288{% for key, value in days %} 4289{{ key }} 4290{% endfor %} 4291--DATA-- 4292return array('days' => array( 4293 1 => array('money' => 9), 4294 2 => array('money' => 21), 4295 3 => array('money' => 38), 4296 4 => array('money' => 6), 4297 18 => array('money' => 6), 4298 19 => array('money' => 3), 4299 31 => array('money' => 11), 4300)); 4301--EXPECT-- 43021 43032 43043 43054 430618 430719 430831 4309--TEST-- 4310"const" test 4311--TEMPLATE-- 4312{{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }} 4313{{ 'bar' is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }} 4314{{ value is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }} 4315{{ 2 is constant('ARRAY_AS_PROPS', object) ? 'ok' : 'no' }} 4316--DATA-- 4317return array('value' => 'bar', 'object' => new ArrayObject(array('hi'))); 4318--EXPECT-- 4319ok 4320ok 4321ok 4322ok--TEST-- 4323"defined" test 4324--TEMPLATE-- 4325{{ definedVar is defined ? 'ok' : 'ko' }} 4326{{ definedVar is not defined ? 'ko' : 'ok' }} 4327{{ undefinedVar is defined ? 'ko' : 'ok' }} 4328{{ undefinedVar is not defined ? 'ok' : 'ko' }} 4329{{ zeroVar is defined ? 'ok' : 'ko' }} 4330{{ nullVar is defined ? 'ok' : 'ko' }} 4331{{ nested.definedVar is defined ? 'ok' : 'ko' }} 4332{{ nested['definedVar'] is defined ? 'ok' : 'ko' }} 4333{{ nested.definedVar is not defined ? 'ko' : 'ok' }} 4334{{ nested.undefinedVar is defined ? 'ko' : 'ok' }} 4335{{ nested['undefinedVar'] is defined ? 'ko' : 'ok' }} 4336{{ nested.undefinedVar is not defined ? 'ok' : 'ko' }} 4337{{ nested.zeroVar is defined ? 'ok' : 'ko' }} 4338{{ nested.nullVar is defined ? 'ok' : 'ko' }} 4339{{ nested.definedArray.0 is defined ? 'ok' : 'ko' }} 4340{{ nested['definedArray'][0] is defined ? 'ok' : 'ko' }} 4341{{ object.foo is defined ? 'ok' : 'ko' }} 4342{{ object.undefinedMethod is defined ? 'ko' : 'ok' }} 4343{{ object.getFoo() is defined ? 'ok' : 'ko' }} 4344{{ object.getFoo('a') is defined ? 'ok' : 'ko' }} 4345{{ object.undefinedMethod() is defined ? 'ko' : 'ok' }} 4346{{ object.undefinedMethod('a') is defined ? 'ko' : 'ok' }} 4347{{ object.self.foo is defined ? 'ok' : 'ko' }} 4348{{ object.self.undefinedMethod is defined ? 'ko' : 'ok' }} 4349{{ object.undefinedMethod.self is defined ? 'ko' : 'ok' }} 4350--DATA-- 4351return array( 4352 'definedVar' => 'defined', 4353 'zeroVar' => 0, 4354 'nullVar' => null, 4355 'nested' => array( 4356 'definedVar' => 'defined', 4357 'zeroVar' => 0, 4358 'nullVar' => null, 4359 'definedArray' => array(0), 4360 ), 4361 'object' => new TwigTestFoo(), 4362); 4363--EXPECT-- 4364ok 4365ok 4366ok 4367ok 4368ok 4369ok 4370ok 4371ok 4372ok 4373ok 4374ok 4375ok 4376ok 4377ok 4378ok 4379ok 4380ok 4381ok 4382ok 4383ok 4384ok 4385ok 4386ok 4387ok 4388ok 4389--DATA-- 4390return array( 4391 'definedVar' => 'defined', 4392 'zeroVar' => 0, 4393 'nullVar' => null, 4394 'nested' => array( 4395 'definedVar' => 'defined', 4396 'zeroVar' => 0, 4397 'nullVar' => null, 4398 'definedArray' => array(0), 4399 ), 4400 'object' => new TwigTestFoo(), 4401); 4402--CONFIG-- 4403return array('strict_variables' => false) 4404--EXPECT-- 4405ok 4406ok 4407ok 4408ok 4409ok 4410ok 4411ok 4412ok 4413ok 4414ok 4415ok 4416ok 4417ok 4418ok 4419ok 4420ok 4421ok 4422ok 4423ok 4424ok 4425ok 4426ok 4427ok 4428ok 4429ok 4430--TEST-- 4431"empty" test 4432--TEMPLATE-- 4433{{ foo is empty ? 'ok' : 'ko' }} 4434{{ bar is empty ? 'ok' : 'ko' }} 4435{{ foobar is empty ? 'ok' : 'ko' }} 4436{{ array is empty ? 'ok' : 'ko' }} 4437{{ zero is empty ? 'ok' : 'ko' }} 4438{{ string is empty ? 'ok' : 'ko' }} 4439{{ countable_empty is empty ? 'ok' : 'ko' }} 4440{{ countable_not_empty is empty ? 'ok' : 'ko' }} 4441{{ markup_empty is empty ? 'ok' : 'ko' }} 4442{{ markup_not_empty is empty ? 'ok' : 'ko' }} 4443--DATA-- 4444 4445class CountableStub implements Countable 4446{ 4447 private $items; 4448 4449 public function __construct(array $items) 4450 { 4451 $this->items = $items; 4452 } 4453 4454 public function count() 4455 { 4456 return count($this->items); 4457 } 4458} 4459return array( 4460 'foo' => '', 'bar' => null, 'foobar' => false, 'array' => array(), 'zero' => 0, 'string' => '0', 4461 'countable_empty' => new CountableStub(array()), 'countable_not_empty' => new CountableStub(array(1, 2)), 4462 'markup_empty' => new Twig_Markup('', 'UTF-8'), 'markup_not_empty' => new Twig_Markup('test', 'UTF-8'), 4463); 4464--EXPECT-- 4465ok 4466ok 4467ok 4468ok 4469ko 4470ko 4471ok 4472ko 4473ok 4474ko 4475--TEST-- 4476"even" test 4477--TEMPLATE-- 4478{{ 1 is even ? 'ko' : 'ok' }} 4479{{ 2 is even ? 'ok' : 'ko' }} 4480{{ 1 is not even ? 'ok' : 'ko' }} 4481{{ 2 is not even ? 'ko' : 'ok' }} 4482--DATA-- 4483return array() 4484--EXPECT-- 4485ok 4486ok 4487ok 4488ok 4489--TEST-- 4490Twig supports the in operator 4491--TEMPLATE-- 4492{% if bar in foo %} 4493TRUE 4494{% endif %} 4495{% if not (bar in foo) %} 4496{% else %} 4497TRUE 4498{% endif %} 4499{% if bar not in foo %} 4500{% else %} 4501TRUE 4502{% endif %} 4503{% if 'a' in bar %} 4504TRUE 4505{% endif %} 4506{% if 'c' not in bar %} 4507TRUE 4508{% endif %} 4509{% if '' not in bar %} 4510TRUE 4511{% endif %} 4512{% if '' in '' %} 4513TRUE 4514{% endif %} 4515{% if '0' not in '' %} 4516TRUE 4517{% endif %} 4518{% if 'a' not in '0' %} 4519TRUE 4520{% endif %} 4521{% if '0' in '0' %} 4522TRUE 4523{% endif %} 4524{{ false in [0, 1] ? 'TRUE' : 'FALSE' }} 4525{{ true in [0, 1] ? 'TRUE' : 'FALSE' }} 4526{{ '0' in [0, 1] ? 'TRUE' : 'FALSE' }} 4527{{ '' in [0, 1] ? 'TRUE' : 'FALSE' }} 4528{{ 0 in ['', 1] ? 'TRUE' : 'FALSE' }} 4529{{ '' in 'foo' ? 'TRUE' : 'FALSE' }} 4530{{ 0 in 'foo' ? 'TRUE' : 'FALSE' }} 4531{{ false in 'foo' ? 'TRUE' : 'FALSE' }} 4532{{ true in '100' ? 'TRUE' : 'FALSE' }} 4533{{ [] in 'Array' ? 'TRUE' : 'FALSE' }} 4534{{ [] in [true, false] ? 'TRUE' : 'FALSE' }} 4535{{ [] in [true, ''] ? 'TRUE' : 'FALSE' }} 4536{{ [] in [true, []] ? 'TRUE' : 'FALSE' }} 4537{{ dir_object in 'foo'~dir_name ? 'TRUE' : 'FALSE' }} 4538{{ 5 in 125 ? 'TRUE' : 'FALSE' }} 4539--DATA-- 4540return array('bar' => 'bar', 'foo' => array('bar' => 'bar'), 'dir_name' => dirname(__FILE__), 'dir_object' => new SplFileInfo(dirname(__FILE__))) 4541--EXPECT-- 4542TRUE 4543TRUE 4544TRUE 4545TRUE 4546TRUE 4547TRUE 4548TRUE 4549TRUE 4550TRUE 4551FALSE 4552FALSE 4553FALSE 4554FALSE 4555FALSE 4556TRUE 4557FALSE 4558FALSE 4559FALSE 4560FALSE 4561FALSE 4562FALSE 4563TRUE 4564FALSE 4565FALSE 4566--TEST-- 4567Twig supports the in operator when using objects 4568--TEMPLATE-- 4569{% if object in object_list %} 4570TRUE 4571{% endif %} 4572--DATA-- 4573$foo = new TwigTestFoo(); 4574$foo1 = new TwigTestFoo(); 4575 4576$foo->position = $foo1; 4577$foo1->position = $foo; 4578 4579return array( 4580 'object' => $foo, 4581 'object_list' => array($foo1, $foo), 4582); 4583--EXPECT-- 4584TRUE 4585--TEST-- 4586"iterable" test 4587--TEMPLATE-- 4588{{ foo is iterable ? 'ok' : 'ko' }} 4589{{ traversable is iterable ? 'ok' : 'ko' }} 4590{{ obj is iterable ? 'ok' : 'ko' }} 4591{{ val is iterable ? 'ok' : 'ko' }} 4592--DATA-- 4593return array( 4594 'foo' => array(), 4595 'traversable' => new ArrayIterator(array()), 4596 'obj' => new stdClass(), 4597 'val' => 'test', 4598); 4599--EXPECT-- 4600ok 4601ok 4602ko 4603ko--TEST-- 4604"odd" test 4605--TEMPLATE-- 4606{{ 1 is odd ? 'ok' : 'ko' }} 4607{{ 2 is odd ? 'ko' : 'ok' }} 4608--DATA-- 4609return array() 4610--EXPECT-- 4611ok 4612ok 4613