├── php ├── test │ ├── templates │ │ ├── helper.html │ │ ├── layout.html │ │ └── index.html │ └── index.php ├── Twig │ ├── exceptions.php │ ├── compiler.php │ ├── api.php │ ├── runtime.php │ ├── lexer.php │ ├── ast.php │ └── parser.php └── Twig.php ├── README └── spec ├── spec.txt └── spec.html /php/test/templates/helper.html: -------------------------------------------------------------------------------- 1 |
Hello World
5 |{% super %}
11 |This specification specifies a simple cross-language template engine for at least 296 | PHP, Python and Ruby.
297 |A language independent and simple template engine is useful for applications that 300 | use code which is written in more than one programming language. Good Examples 301 | are portal systems which use a blog written in Ruby, a forum software written in 302 | PHP and a planet system written in Python.
303 |Twig uses a syntax similar to the Genshi text templates which in turn were 307 | inspired by django which also inspired Jinja (all three of them python template 308 | engines) which inspired the Twig runtime environment.
309 |To simplify porting the template language to different platforms in a couple of 313 | situations the behavior is undefined. Template authors may never take advantage 314 | of such a situation!
315 |I'm too lazy to write down the syntax as BNF diagram but the following snippet 319 | should explain the syntax elements:
320 |
321 | <!DOCTYPE HTML>
322 | {# This is a comment #}
323 | <title>{% block title %}Page Title Goes Here{% endblock %}</title>
324 | {% if show_navigation %}
325 | <nav>
326 | <ul>
327 | {% for item in navigation %}
328 | <li><a href="${item.href|e}">$item.caption</a></li>
329 | {% endfor %}
330 | </ul>
331 | </nav>
332 | {% endif %}
333 | <article>{% block body %}{% endblock %}</article>
334 |
335 | Everything between {# and #} is ignored by the lexer. Inside blocks and 338 | variable sections the Lexer has to remove whitespace too.
339 |To output expressions two syntaxes exists. Simple variable output or full 343 | expression output:
344 |
345 | $this.is.a.variable.output
346 | ${expression|goes|here}
347 |
348 | The former is what we call a variable expression, the second a full expression. 349 | Variable expressions might not contain whitespace where as a full expression 350 | must print the output of the full wrapped expression.
351 |Expressions allow basic string manipulation and arithmetic calculations. It 355 | an infix syntax with the following operators in this precedence:
356 |357 |399 |358 |
398 |359 | 362 | 363 |360 | 361 | 366 | 367 | 368 | Operator 364 |Description 365 |371 | + 369 |Convert both arguments into a number and add them up. 370 |374 | - 372 |Convert both arguments into a number and substract them. 373 |377 | * 375 |Convert both arguments into a number and multiply them. 376 |380 | / 378 |Convert both arguments into a number and divide them. 379 |384 | % 381 |Convert both arguments into a number and calculate the rest 382 | of the integer division. 383 |387 | ~ 385 |Convert both arguments into a string and concatenate them. 386 |390 | or 388 |True if the left or the right expression is true. 389 |393 | and 391 |True if the left and the right expression is true. 392 |396 | 397 | not 394 |negate the expression 395 |
All number conversions have an undefined precision but the implementations 400 | should try to select the best possible type. For example if the implementation 401 | sees an integer and a float that looks like an integer it may convert the 402 | latter into a long and add them.
403 |Use parentheses to group expressions.
404 |If an object cannot be compared the implementation might raise an error or fail 405 | silently. Template authors may never apply mathematical operators to untrusted 406 | data. This is especially true for the php implementation where the following 407 | outputs 42:
408 |
409 | ${"foo41" + 1}
410 |
411 | This is undefined behavior and will break on different implementations or 412 | return 0 as "foo41" is not a valid number.
413 |The following types exist:
416 |417 |456 |418 |
455 |419 | 423 | 424 |420 | 421 | 422 | 428 | 429 | 430 | Type 425 |Literal 426 |Description 427 |435 | integer 431 |d+ 432 |one of the two numeric types. Which of them 433 | is used when is up to the implementation. 434 |439 | float` 436 |d+.d+ 437 |floating point values. 438 |444 | string 440 |see below 441 |a unicode string. The PHP implementation has 442 | to use bytestrings here and may use mb_string 443 |448 | bool 445 |(true|false) 446 |Represents boolean values 447 |453 | 454 | none 449 |none 450 |This type is returned on missing variables or 451 | attributes. 452 |
string regex:
457 |458 | (?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')(?sm) 459 |460 |
There are two ways to look up attributes on objects. The dot and the 464 | subscript syntax, both inspired by JavaScript. Basically the following 465 | expressions do the very same:
466 |467 | foo.name.0 468 | foo['name'][0] 469 |470 |
This is useful to dynamically get attributes from objects:
471 |472 | foo[bar] 473 |474 |
The underlaying implementation is free to specify on it's own what an attribute 475 | lookup means. The PHP reference implementation for example performs those 476 | actions on foo.bar:
477 |The first match returns the object, attribute access to not existing attributes 484 | returns none.
485 |The template language does not specify function calls but filters can be used 489 | to further modify variables using functions the template engine provides.
490 |The following snippet shows how filters are translated to function calls:
491 |
492 | ${42|foo(1, 2)|bar|baz}
493 | -> baz(bar(foo(42, 1, 2)))
494 |
495 | The following filters must be provided by the implementation:
496 |497 |592 |498 |
591 |499 | 502 | 503 |500 | 501 | 506 | 507 | 508 | Name 504 |Description 505 |514 | date 509 |Format the date using the PHP date formatting rules. 510 | This may sound like a nonstandard way of formatting 511 | dates but it's a way very popular among template 512 | designers and also used by django. 513 |518 | numberformat 515 |Apply number formatting on the string. This may or 516 | may not use local specific rules. 517 |521 | moneyformat 519 |Like numberformat but for money. 520 |524 | filesizeformat 522 |Takes a number of bytes and displays it as KB/MB/GB 523 |531 | format 525 |530 | applies sprintf formatting on the string:
526 |527 | ${"%s %2f" | format(string, float)} 528 |529 |535 | escape 532 |apply HTML escaping on a string. This also has to 533 | convert " to " but leave `' unmodified. 534 |538 | e 536 |alias for escape 537 |543 | urlencode 539 |URL encode the string. If the second parameter is 540 | true this function should encode for path sections, 541 | otherwise for query strings. 542 |547 | title 544 |Make the string lowercase and upper case the first 545 | characters of all words. 546 |551 | capitalize 548 |Like title but capitalizes only the first char of 549 | the whole string. 550 |554 | upper 552 |Convert the string to uppercase 553 |557 | lower 555 |Convert the string to lowercase 556 |560 | strip 558 |Trim leading and trailing whitespace 559 |563 | lstrip 561 |Trim leading whitespace 562 |566 | rstrip 564 |Trim trailing whitespace 565 |570 | join 567 |Concatenate the array items and join them with the 568 | string provided or an empty string. 569 |573 | reverse 571 |Reverse the Array items 572 |577 | count 574 |Count the number of items in an array or string 575 | characters. 576 |580 | length 578 |alias for count. 579 |583 | default 581 |If the value is none the first argument is returned 582 |586 | even 584 |Is the number even? 585 |589 | 590 | odd 587 |Is the number odd? 588 |
Iteration works via for loops. Loops work a bit like their python counterparts 597 | just that they don't support multilevel tuple unpacking and that they add a new 598 | layer to the context. Thus at the end of the iteration all the modifications on 599 | the context disappear. Additionally inside loops you have access to a special 600 | loop object which provides runtime information:
601 |602 | ====================== =================================================== 603 | Variable Description 604 | ====================== =================================================== 605 | ``loop.index`` The current iteration of the loop (1-indexed) 606 | ``loop.index0`` The current iteration of the loop (0-indexed) 607 | ``loop.revindex`` The number of iterations from the end of the 608 | loop (1-indexed) 609 | ``loop.revindex0`` The number of iterations from the end of the 610 | loop (0-indexed) 611 | ``loop.first`` True if this is the first time through the loop 612 | ``loop.last`` True if this is the last time through the loop 613 | ``loop.parent`` For nested loops, this is the loop "above" the 614 | current one 615 | ====================== =================================================== 616 |617 |
Additionally for loops can have an else section that is executed if no 618 | iteration took place.
619 |
622 | <ul>
623 | {% for user in users %}
624 | <li><a href="$user.href">${user.username|e}</a></li>
625 | {% else %}
626 | <li><em>no users found!</em></li>
627 | {% endfor %}
628 | </ul>
629 |
630 | Because we have to cope with PHP too which has problematic arrays which are 634 | neither hashmaps nor lists we have a no support for associativ array iteration 635 | at all. How to iterate over associative array then? Using a filter:
636 |
637 | {% for key, value in array|items %}
638 | ...
639 | {% endfor %}
640 |
641 | To iterate over the keys only:
642 |
643 | {% for key in array|key %}
644 | ...
645 | {% endfor %}
646 |
647 | If conditions work like like Ruby, PHP and Python just that we use PHP 652 | keywords. And it's elseif and not else if:
653 |
654 | {% if expr1 %}
655 | ...
656 | {% elseif expr2 %}
657 | ...
658 | {% else %}
659 | ...
660 | {% endif %}
661 |
662 | emplate inheritance allows you to build a base "skeleton" template that 666 | contains all the common elements of your site and defines blocks that 667 | child templates can override.
668 |Here a small template inheritance example:
669 |
670 | <!DOCTYPE HTML>
671 | <html lang="en">
672 | <link rel="stylesheet" href="style.css">
673 | <title>{% block title %}My site{% endblock %}</title>
674 | <div id="sidebar">
675 | {% block sidebar %}
676 | <ul>
677 | <li><a href="/">Home</a></li>
678 | <li><a href="/blog/">Blog</a></li>
679 | </ul>
680 | {% endblock %}
681 | </div>
682 | <div id="content">
683 | {% block content %}{% endblock %}
684 | </div>
685 | </html>
686 |
687 | If we call that template "base.html" a "index.html" template could override 688 | it and fill in the blocks:
689 |
690 | {% extends "base.html" %}
691 | {% block title %}Foo — {% super %}{% endblock %}
692 | {% block content %}
693 | This is the content
694 | {% endblock %}
695 |
696 |