|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- ==================================
- Twig Template Engine Specification
- ==================================
-
-
- This specification specifies a simple cross-language template engine for at least
- PHP, Python and Ruby.
-
-
- Purpose
- =======
-
- A language independent and simple template engine is useful for applications that
- use code which is written in more than one programming language. Good Examples
- are portal systems which use a blog written in Ruby, a forum software written in
- PHP and a planet system written in Python.
-
-
- Inspiration
- ===========
-
- Twig uses a syntax similar to the Genshi text templates which in turn were
- inspired by django which also inspired Jinja (all three of them python template
- engines) which inspired the Twig runtime environment.
-
-
- Undefined Behavior
- ==================
-
- To simplify porting the template language to different platforms in a couple of
- situations the behavior is undefined. Template authors may never take advantage
- of such a situation!
-
-
- Syntax
- ======
-
- I'm too lazy to write down the syntax as BNF diagram but the following snippet
- should explain the syntax elements::
-
- <!DOCTYPE HTML>
- {# This is a comment #}
- <title>{% block title %}Page Title Goes Here{% endblock %}</title>
- {% if show_navigation %}
- <nav>
- <ul>
- {% for item in navigation %}
- <li><a href="${item.href|e}">$item.caption</a></li>
- {% endfor %}
- </ul>
- </nav>
- {% endif %}
- <article>{% block body %}{% endblock %}</article>
-
-
- Comments and Whitespace
- -----------------------
-
- Everything between ``{#`` and ``#}`` is ignored by the lexer. Inside blocks and
- variable sections the Lexer has to remove whitespace too.
-
-
- Output Expressions
- ------------------
-
- To output expressions two syntaxes exist. Simple variable output or full
- expression output::
-
- $this.is.a.variable.output
- ${ expression | goes | here }
-
- The former is what we call a variable expression, the second a full expression.
- Variable expressions must not contain whitespace, whereas a full expression
- must print the output of the full wrapped expression.
-
-
- Expressions
- -----------
-
- Expressions allow basic string manipulation and arithmetic calculations. It is
- an infix syntax with the following operators in this precedence:
-
- =========== ==============================================================
- Operator Description
- =========== ==============================================================
- ``+`` Convert both arguments into a number and add them up.
- ``-`` Convert both arguments into a number and substract them.
- ``*`` Convert both arguments into a number and multiply them.
- ``/`` Convert both arguments into a number and divide them.
- ``%`` Convert both arguments into a number and calculate the rest
- of the integer division.
- ``~`` Convert both arguments into a string and concatenate them.
- ``or`` True if the left or the right expression is true.
- ``and`` True if the left and the right expression is true.
- ``not`` negate the expression
- =========== ==============================================================
-
- All number conversions have an undefined precision but the implementations
- should try to select the best possible type. For example, if the implementation
- sees an integer and a float that looks like an integer it may convert the
- latter into a long and add them.
-
- Use parentheses to group expressions.
-
- If an object cannot be compared the implementation might raise an error or fail
- silently. Template authors may never apply mathematical operators to untrusted
- data. This is especially true for the php implementation where the following
- outputs ``42``::
-
- ${ "foo41" + 1 }
-
- This is undefined behavior and will break on different implementations or
- return ``0`` as ``"foo41"`` is not a valid number.
-
- Types
- ~~~~~
-
- The following types exist:
-
- =========== =============== ==============================================
- Type Literal Description
- =========== =============== ==============================================
- ``integer`` `\d+` One of the two numeric types. Which of them
- is used and when is up to the implementation.
- ``float`` `\d+\.\d+` Floating point values.
- ``string`` see below A unicode string. The PHP implementation has
- to use bytestrings here and may use mb_string.
- ``bool`` `(true|false)` Represents boolean values.
- ``none`` `none` This type is returned on missing variables or
- attributes.
- =========== =============== ==============================================
-
- String regex::
-
- (?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')(?sm)
-
- Attribute Lookup
- ~~~~~~~~~~~~~~~~
-
- There are two ways to look up attributes on objects. The dot and the
- subscript syntax, both inspired by JavaScript. Basically the following
- expressions do the very same::
-
- foo.name.0
- foo['name'][0]
-
- This is useful to dynamically get attributes from objects::
-
- foo[bar]
-
- The underlaying implementation is free to specify on it's own what an attribute
- lookup means. The PHP reference implementation for example performs these
- actions on ``foo.bar``:
-
- - try ``$foo['bar']``
- - try ``$foo->bar()`` (if they're using ``foo.bar`` and not ``foo[bar]``)
- - try ``$foo->bar``
- - try ``$foo->getBar()`` (if they're using ``foo.bar`` and not ``foo[bar]``)
-
- The first match returns the object, attribute access to nonexisting attributes
- returns `none`.
-
- Filtering
- ~~~~~~~~~
-
- The template language does not specify function calls, but filters can be used
- to further modify variables using functions the template engine provides.
-
- The following snippet shows how filters are translated to function calls::
-
- ${ 42 | foo(1, 2) | bar | baz }
- -> baz(bar(foo(42, 1, 2)))
-
- The following filters must be provided by the implementation:
-
- =================== ======================================================
- Name Description
- =================== ======================================================
- `date` Format the timestamp using the PHP date formatting
- rules. This may sound like a nonstandard way of
- formatting dates but it's a way very popular among
- template designers and also used by django.
- `strftime` Format the timestamp using standard strftime rules.
- `numberformat` Apply number formatting on the string. This may or
- may not use local specific rules.
- `moneyformat` Like `numberformat` but for money.
- `filesizeformat` Takes a number of bytes and displays it as KB/MB/GB
- `format` Applies `sprintf` formatting on the string::
- ${ "%s %2f" | format(string, float) }
- `even` Is the number even?
- `odd` Is the number odd?
- `escape` Apply HTML escaping on a string. This also has to
- convert `"` to `" but leave `'` unmodified.
- `e` Alias for `escape`.
- `urlencode` URL encode the string. If the second parameter is
- true this function should encode for path sections,
- otherwise for query strings.
- `quotes` Escape quotes (\', \", etc.)
- `title` Make the string lowercase and upper case the first
- characters of all words.
- `capitalize` Like `title` but capitalizes only the first char of
- the whole string.
- `upper` Convert the string to uppercase.
- `lower` Convert the string to lowercase.
- `strip` Trim leading and trailing whitespace.
- `lstrip` Trim leading whitespace.
- `rstrip` Trim trailing whitespace.
- `translate` Translate the string using either the "theme" domain
- or the "chyrp" domain if in Admin. (Chyrp-specific)
- `translate_plural` Translate the (singular) string, or the plural string
- if the number passed is not 1.
- `normalize` Convert all excessive whitespace (including linebreaks)
- into a single space.
- `truncate` Truncate a string, providing ellipsis, if it is longer
- than the passed length. Keeps words in tact by default,
- but with a second boolean parameter will be strict.
- `replace` Replaces the occurrence of the first argument with the
- second argument in the string.
- `linebreaks` Convert linebreaks to <br />'s.
- `camelize` Convert string to camelcase.
- `strip_tags` Strip HTML from the string.
- `pluralize` Return the pluralization of a string, or if a number
- is passed and it is 1, don't pluralize.
- `sanitize` Remove special characters from a string.
- `join` Concatenate the array items and join them with the
- string provided (or commas by default).
- `split` Split a string into an array at the given breakpoints.
- `first` First entry of an Array.
- `offset` Entry at Array[offset].
- `last` Last entry of an Array.
- `reverse` Reverse the Array items.
- `count` Count the number of items in an array or string
- characters.
- `length` Alias for `count`.
- `default` If the value is `none` the first argument is returned
- `keys` Keys of an Array.
- `items` Items of an Array.
- `inspect` Dumps the variable or value.
- `fallback` If the value is empty or `none`, return this value.
- `selected` If the first argument is the same as the value, output
- `class="selected"`, or `selected` if the second
- argument is `true`.
- `option_selected` Same as `selected`, but for `selected="selected"`.
- `checked` Same as `selected`, but for `checked="checked"`.
- =================== ======================================================
-
- Additionally, if a filter is missing (say, ${ foo | bar_filter }, in Chyrp it
- checks for an associated Trigger filter by that filter's name.
-
-
- For Loops
- ---------
-
- Iteration works via for loops. Loops work a bit like their Python counterparts,
- except that they don't support multilevel tuple unpacking and that they add a new
- layer to the context. Thus at the end of the iteration all the modifications on
- the context disappear. Additionally, inside loops you have access to a special
- `loop` object which provides runtime information:
-
- ====================== ===================================================
- Variable Description
- ====================== ===================================================
- ``loop.index`` The current iteration of the loop (1-indexed)
- ``loop.index0`` The current iteration of the loop (0-indexed)
- ``loop.revindex`` The number of iterations from the end of the
- loop (1-indexed)
- ``loop.revindex0`` The number of iterations from the end of the
- loop (0-indexed)
- ``loop.first`` True if this is the first time through the loop
- ``loop.last`` True if this is the last time through the loop
- ``loop.parent`` For nested loops, this is the loop "above" the
- current one
- ====================== ===================================================
-
- Additionally for loops can have an `else` section that is executed if no
- iteration took place.
-
- Example
- ~~~~~~~
-
- ::
-
- <ul>
- {% for user in users %}
- <li><a href="$user.href">${ user.username | escape }</a></li>
- {% else %}
- <li><em>No users found!</em></li>
- {% endfor %}
- </ul>
-
-
- Notes on Iteration
- ~~~~~~~~~~~~~~~~~~
-
- Because we have to cope with PHP too, which has problematic arrays that are
- neither hashmaps nor lists, we have no support for associative array iteration
- at all. How do you iterate over associative arrays then? Using a filter::
-
- {% for key, value in array | items %}
- ...
- {% endfor %}
-
- To iterate over the keys only::
-
- {% for key in array | keys %}
- ...
- {% endfor %}
-
-
- If Conditions
- -------------
-
- If conditions work like like Ruby, PHP and Python, just that we use PHP
- keywords. Also, use `elseif` and not `else if`::
-
- {% if expr1 %}
- ...
- {% elseif expr2 %}
- ...
- {% else %}
- ...
- {% endif %}
-
-
- Inheritance
- -----------
-
- Template inheritance allows you to build a base "skeleton" template that
- contains all the common elements of your site and defines **blocks** that
- child templates can override.
-
- Here a small template inheritance example::
-
- <!DOCTYPE HTML>
- <html lang="en">
- <link rel="stylesheet" href="style.css">
- <title>{% block title %}My site{% endblock %}</title>
- <div id="sidebar">
- {% block sidebar %}
- <ul>
- <li><a href="/">Home</a></li>
- <li><a href="/blog/">Blog</a></li>
- </ul>
- {% endblock %}
- </div>
- <div id="content">
- {% block content %}{% endblock %}
- </div>
- </html>
-
- If we call that template "base.html" a "index.html" template could override
- it and fill in the blocks::
-
- {% extends "base.html" %}
- {% block title %}Foo — {% super %}{% endblock %}
- {% block content %}
- This is the content
- {% endblock %}
-
- By using `{% super %}` you can render the parent's block. The template
- filenames must be constant strings (we don't support dynamic inheritance
- for simplicity) and are relative to the loader folder, not the current
- template.
|