From a855b1898a844c0653d440147a738a88c8f6e6b1 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 15 Dec 2010 12:25:16 +0100 Subject: [PATCH] converted documentation to rst --- doc/01-Introduction.markdown | 94 -- doc/02-Twig-for-Template-Designers.markdown | 1263 ------------------------ doc/03-Twig-for-Developers.markdown | 464 --------- doc/04-Extending-Twig.markdown | 556 ----------- doc/05-Hacking-Twig.markdown | 196 ---- doc/06-Recipes.markdown | 208 ---- doc/advanced.rst | 558 +++++++++++ doc/api.rst | 467 +++++++++ doc/hacking.rst | 184 ++++ doc/index.rst | 12 + doc/intro.rst | 99 ++ doc/recipes.rst | 172 ++++ doc/templates.rst | 1408 +++++++++++++++++++++++++++ 13 files changed, 2900 insertions(+), 2781 deletions(-) delete mode 100644 doc/01-Introduction.markdown delete mode 100644 doc/02-Twig-for-Template-Designers.markdown delete mode 100644 doc/03-Twig-for-Developers.markdown delete mode 100644 doc/04-Extending-Twig.markdown delete mode 100644 doc/05-Hacking-Twig.markdown delete mode 100644 doc/06-Recipes.markdown create mode 100644 doc/advanced.rst create mode 100644 doc/api.rst create mode 100644 doc/hacking.rst create mode 100644 doc/index.rst create mode 100644 doc/intro.rst create mode 100644 doc/recipes.rst create mode 100644 doc/templates.rst diff --git a/doc/01-Introduction.markdown b/doc/01-Introduction.markdown deleted file mode 100644 index 0666d3d..0000000 --- a/doc/01-Introduction.markdown +++ /dev/null @@ -1,94 +0,0 @@ -Introduction -============ - -This is the documentation for Twig, the flexible, fast, and secure template -engine for PHP. - -If you have any exposure to other text-based template languages, such as -Smarty, Django, or Jinja, you should feel right at home with Twig. It's both -designer and developer friendly by sticking to PHP's principles and adding -functionality useful for templating environments. - -The key-features are... - - * *Fast*: Twig compiles templates down to plain optimized PHP code. The - overhead compared to regular PHP code was reduced to the very minimum. - - * *Secure*: Twig has a sandbox mode to evaluate untrusted template code. This - allows Twig to be used as a template language for applications where users - may modify the template design. - - * *Flexible*: Twig is powered by a flexible lexer and parser. This allows the - developer to define its own custom tags and filters, and create its own - DSL. - -Prerequisites -------------- - -Twig needs at least **PHP 5.2.4** to run. - -Installation ------------- - -You have multiple ways to install Twig. If you are unsure what to do, go with -the tarball. - -### From the tarball release - - 1. Download the most recent tarball from the [download page](http://www.twig-project.org/installation) - 2. Unpack the tarball - 3. Move the files somewhere in your project - -### Installing the development version - - 1. Install Subversion or Git - 2. For Subversion: `svn co http://svn.twig-project.org/trunk/ twig`, for Git: - `git clone git://github.com/fabpot/Twig.git` - -### Installing the PEAR package - - 1. Install PEAR - 2. pear channel-discover pear.twig-project.org - 3. pear install twig/Twig (or pear install twig/Twig-beta) - -Basic API Usage ---------------- - -This section gives you a brief introduction to the PHP API for Twig. - -The first step to use Twig is to register its autoloader: - - [php] - require_once '/path/to/lib/Twig/Autoloader.php'; - Twig_Autoloader::register(); - -Replace the `/path/to/lib/` path with the path you used for Twig installation. - ->**NOTE** ->Twig follows the PEAR convention names for its classes, which means you can ->easily integrate Twig classes loading in your own autoloader. - - [php] - $loader = new Twig_Loader_String(); - $twig = new Twig_Environment($loader); - - $template = $twig->loadTemplate('Hello {{ name }}!'); - - $template->display(array('name' => 'Fabien')); - -Twig uses a loader (`Twig_Loader_String`) to locate templates, and an -environment (`Twig_Environment`) to store the configuration. - -The `loadTemplate()` method uses the loader to locate and load the template -and returns a template object (`Twig_Template`) which is suitable for -rendering with the `display()` method. - -Twig also comes with a filesystem loader: - - [php] - $loader = new Twig_Loader_Filesystem('/path/to/templates'); - $twig = new Twig_Environment($loader, array( - 'cache' => '/path/to/compilation_cache', - )); - - $template = $twig->loadTemplate('index.html'); diff --git a/doc/02-Twig-for-Template-Designers.markdown b/doc/02-Twig-for-Template-Designers.markdown deleted file mode 100644 index a554f69..0000000 --- a/doc/02-Twig-for-Template-Designers.markdown +++ /dev/null @@ -1,1263 +0,0 @@ -Twig for Template Designers -=========================== - -This document describes the syntax and semantics of the template engine and -will be most useful as reference to those creating Twig templates. - -Synopsis --------- - -A template is simply a text file. It can generate any text-based format (HTML, -XML, CSV, LaTeX, etc.). It doesn't have a specific extension, `.html` or -`.xml` are just fine. - -A template contains **variables** or **expressions**, which get replaced with -values when the template is evaluated, and tags, which control the logic of -the template. - -Below is a minimal template that illustrates a few basics. We will cover the -details later in that document: - - [twig] - - - - My Webpage - - - - -

My Webpage

- {{ a_variable }} - - - -There are two kinds of delimiters: `{% ... %}` and `{{ ... }}`. The first one -is used to execute statements such as for-loops, the latter prints the result -of an expression to the template. - -IDEs Integration ----------------- - -Modern IDEs support syntax highlighting and auto-completion for a large range -of languages. As Twig syntax is quite similar to Jinja and Django templates, -IDEs that support these two Python templating systems should also support -Twig. - -If you use Textmate, you can use the -[Jinja](http://jinja.pocoo.org/2/documentation/integration) bundle or the -[Django](http://code.djangoproject.com/wiki/TextMate) one. - -If you use Vim, you can use the -[Jinja](http://jinja.pocoo.org/2/documentation/integration) syntax plugin. - -Variables ---------- - -The application passes variables to the templates you can mess around in the -template. Variables may have attributes or elements on them you can access -too. How a variable looks like, heavily depends on the application providing -those. - -You can use a dot (`.`) to access attributes of a variable, alternative the -so-called "subscript" syntax (`[]`) can be used. The following lines do the -same: - - [twig] - {{ foo.bar }} - {{ foo['bar'] }} - ->**NOTE** ->It's important to know that the curly braces are *not* part of the variable ->but the print statement. If you access variables inside tags don't put the ->braces around. - -If a variable or attribute does not exist you will get back a `null` value -(which can be tested with the `none` expression). - ->**SIDEBAR** ->Implementation -> ->For convenience sake `foo.bar` does the following things on ->the PHP layer: -> -> * check if `foo` is an array and `bar` a valid element; -> * if not, and if `foo` is an object, check that `bar` is a valid property; -> * if not, and if `foo` is an object, check that `bar` is a valid method -> (even if `bar` is the constructor - use `__construct()` instead); -> * if not, and if `foo` is an object, check that `getBar` is a valid method; -> * if not, and if `foo` is an object, check that `isBar` is a valid method (as of Twig 0.9.9); -> * if not, return a `null` value. -> ->`foo['bar']` on the other hand works mostly the same with the a small ->difference in the order: -> -> * check if `foo` is an array and `bar` a valid element; -> * if not, return a `null` value. -> ->Using the alternative syntax is also useful to dynamically get attributes ->from arrays: -> -> [twig] -> foo[bar] - -Twig always references the following variables: - - * `_self`: references the current template (was `self` before 0.9.9); - * `_context`: references the current context; - * `_charset`: references the current charset (as of 0.9.9). - -Filters -------- - -Variables can by modified by **filters**. Filters are separated from the -variable by a pipe symbol (`|`) and may have optional arguments in -parentheses. Multiple filters can be chained. The output of one filter is -applied to the next. - -`{{ name|striptags|title }}` for example will remove all HTML tags from the -`name` and title-cases it. Filters that accept arguments have parentheses -around the arguments, like a function call. This example will join a list by -commas: `{{ list|join(', ') }}`. - -The built-in filters section below describes all the built-in filters. - -Comments --------- - -To comment-out part of a line in a template, use the comment syntax `{# ... #}`. -This is useful to comment out parts of the template for debugging or to -add information for other template designers or yourself: - - [twig] - {# note: disabled template because we no longer use this - {% for user in users %} - ... - {% endfor %} - #} - -Whitespace Control ------------------- - -The first newline after a template tag is removed automatically (like in PHP.) -Whitespace is not further modified by the template engine, so each whitespace -(spaces, tabs, newlines etc.) is returned unchanged. - -Use the `spaceless` tag to remove whitespace between HTML tags: - - [twig] - {% spaceless %} -
- foo -
- {% endspaceless %} - - {# output will be
foo
#} - -Escaping --------- - -It is sometimes desirable or even necessary to have Twig ignore parts it would -otherwise handle as variables or blocks. For example if the default syntax is -used and you want to use `{{` as raw string in the template and not start a -variable you have to use a trick. - -The easiest way is to output the variable delimiter (`{{`) by using a variable -expression: - - [twig] - {{ '{{' }} - -For bigger sections it makes sense to mark a block `raw`. For example to put -Twig syntax as example into a template you can use this snippet: - - [twig] - {% raw %} - - {% endraw %} - -Template Inheritance --------------------- - -The most powerful part of Twig is template 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. - -Sounds complicated but is very basic. It's easiest to understand it by -starting with an example. - -### Base Template - -This template, which we'll call `base.html`, defines a simple HTML skeleton -document that you might use for a simple two-column page. It's the job of -"child" templates to fill the empty blocks with content: - - [twig] - - - - {% block head %} - - {% block title %}{% endblock %} - My Webpage - {% endblock %} - - -
{% block content %}{% endblock %}
- - - - -In this example, the `{% block %}` tags define four blocks that child -templates can fill in. All the `block` tag does is to tell the template engine -that a child template may override those portions of the template. - -### Child Template - -A child template might look like this: - - [twig] - {% extends "base.html" %} - - {% block title %}Index{% endblock %} - {% block head %} - {% parent %} - - {% endblock %} - {% block content %} -

Index

-

- Welcome on my awesome homepage. -

- {% endblock %} - -The `{% extends %}` tag is the key here. It tells the template engine that -this template "extends" another template. When the template system evaluates -this template, first it locates the parent. The extends tag should be the -first tag in the template. - -The filename of the template depends on the template loader. For example the -`Twig_Loader_Filesystem` allows you to access other templates by giving the -filename. You can access templates in subdirectories with a slash: - - [twig] - {% extends "layout/default.html" %} - -But this behavior can depend on the application embedding Twig. Note that -since the child template doesn't define the `footer` block, the value from the -parent template is used instead. - -You can't define multiple `{% block %}` tags with the same name in the same -template. This limitation exists because a block tag works in "both" -directions. That is, a block tag doesn't just provide a hole to fill - it also -defines the content that fills the hole in the *parent*. If there were two -similarly-named `{% block %}` tags in a template, that template's parent -wouldn't know which one of the blocks' content to use. - -If you want to print a block multiple times you can however use the `display` -tag: - - [twig] - {% block title %}{% endblock %} -

{% display title %}

- {% block body %}{% endblock %} - -Like PHP, Twig does not support multiple inheritance. So you can only have one -extends tag called per rendering. - -### Parent Blocks - -It's possible to render the contents of the parent block by using the `parent` -tag. This gives back the results of the parent block: - - [twig] - {% block sidebar %} -

Table Of Contents

- ... - {% parent %} - {% endblock %} - -### Named Block End-Tags - -Twig allows you to put the name of the block after the end tag for better -readability: - - [twig] - {% block sidebar %} - {% block inner_sidebar %} - ... - {% endblock inner_sidebar %} - {% endblock sidebar %} - -However the name after the `endblock` word must match the block name. - -### Block Nesting and Scope - -Blocks can be nested for more complex layouts. Per default, blocks have access -to variables from outer scopes: - - [twig] - {% for item in seq %} -
  • {% block loop_item %}{{ item }}{% endblock %}
  • - {% endfor %} - -### Block Shortcuts - -For blocks with few content, it's possible to have a shortcut syntax. The -following constructs do the same: - - [twig] - {% block title %} - {{ page_title|title }} - {% endblock %} - -- - - [twig] - {% block title page_title|title %} - -### Dynamic Inheritance (as of Twig 0.9.7) - -Twig supports dynamic inheritance by using a variable as the base template: - - [twig] - {% extends some_var %} - -If the variable evaluates to a `Twig_Template` object, Twig will use it as the -parent template: - - // {% extends layout %} - - $layout = $twig->loadTemplate('some_layout_template.twig'); - - $twig->display('template.twig', array('layout' => $layout)); - -### Conditional Inheritance (as of Twig 0.9.7) - -As a matter of fact, the template name can be any valid expression. So, it's -also possible to make the inheritance mechanism conditional: - - [twig] - {% extends standalone ? "minimum.html" : "base.html" %} - -In this example, the template will extend the "minimum.html" layout template -if the `standalone` variable evaluates to `true`, and "base.html" otherwise. - -Import Context Behavior ------------------------ - -Per default included templates are passed the current context. - -The context that is passed to the included template includes variables defined -in the template: - - [twig] - {% for box in boxes %} - {% include "render_box.html" %} - {% endfor %} - -The included template `render_box.html` is able to access `box`. - -HTML Escaping -------------- - -When generating HTML from templates, there's always a risk that a variable -will include characters that affect the resulting HTML. There are two -approaches: manually escaping each variable or automatically escaping -everything by default. - -Twig supports both, automatic escaping is enabled by default. - ->**NOTE** ->Automatic escaping is only supported if the *escaper* extension ->has been enabled (which is the default). - -### Working with Manual Escaping - -If manual escaping is enabled it's **your** responsibility to escape variables -if needed. What to escape? If you have a variable that *may* include any of -the following chars (`>`, `<`, `&`, or `"`) you **have to** escape it unless -the variable contains well-formed and trusted HTML. Escaping works by piping -the variable through the `|e` filter: `{{ user.username|e }}`. - -### Working with Automatic Escaping - -Whether automatic escaping is enabled or not, you can mark a section of a -template to be escaped or not by using the `autoescape` tag: - - [twig] - {% autoescape on %} - Everything will be automatically escaped in this block - {% endautoescape %} - - {% autoescape off %} - Everything will be outputed as is in this block - {% endautoescape %} - - {% autoescape on js %} - Everything will be automatically escaped in this block - using the js escaping strategy - {% endautoescape %} - -When automatic escaping is enabled everything is escaped by default except for -values explicitly marked as safe. Those can be marked in the template by using -the `|raw` filter. - -Functions returning template data (like macros and `parent`) always return -safe markup. - ->**NOTE** ->Twig is smart enough to not escape an already escaped value by the `escape` ->filter. - -- - ->**NOTE** ->The chapter for developers give more information about when and how ->automatic escaping is applied. - -List of Control Structures --------------------------- - -A control structure refers to all those things that control the flow of a -program - conditionals (i.e. `if`/`elseif`/`else`), `for`-loops, as well as -things like blocks. Control structures appear inside `{% ... %}` blocks. - -### For - -Loop over each item in a sequence. For example, to display a list of users -provided in a variable called `users`: - - [twig] -

    Members

    - - ->**NOTE** ->A sequence can be either an array or an object implementing the `Iterator` ->interface. - -If you do need to iterate over a sequence of numbers, you can use the `..` -operator (as of Twig 0.9.5): - - [twig] - {% for i in 0..10 %} - * {{ i }} - {% endfor %} - -The above snippet of code would print all numbers from 0 to 10. - -It can be also useful with letters: - - [twig] - {% for letter in 'a'..'z' %} - * {{ letter }} - {% endfor %} - -The `..` operator can take any expression at both sides: - - [twig] - {% for letter in 'a'|upper..'z'|upper %} - * {{ letter }} - {% endfor %} - -If you need a step different from 1, you can use the `range` filter instead: - - [twig] - {% for i in 0|range(10, 2) %} - * {{ i }} - {% endfor %} - -Inside of a `for` loop block you can access some special variables: - -| 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 first iteration -| `loop.last` | True if last iteration -| `loop.length` | The number of items in the sequence -| `loop.parent` | The parent context - ->**NOTE** ->The `loop.length`, `loop.revindex`, `loop.revindex0`, and `loop.last` ->variables are only available for PHP arrays, or objects that implement the ->`Countable` interface (as of Twig 0.9.7). - -- - ->**NOTE** ->Unlike in PHP it's not possible to `break` or `continue` in a loop. - -If no iteration took place because the sequence was empty, you can render a -replacement block by using `else`: - - [twig] - - -By default, a loop iterates over the values of the sequence. You can iterate -on keys by using the `keys` filter: - - [twig] -

    Members

    - - -You can also access both keys and values: - - [twig] -

    Members

    - - ->**NOTE** ->On Twig before 0.9.3, you need to use the `items` filter to access both the ->keys and values (`{% for key, value in users|items %}`). - -To conveniently display comma-separated lists or things alike, you can use -`joined by ", "` at the end of the loop statement (as of Twig 0.9.10). Of -course this is not limited to commas: - - [twig] -

    Members

    -

    {% for user in users joined by ", " %}{{ user }}{% endfor %}

    - - {# generates a string like

    Fabien, Jordi, Thomas, Lucas

    #} - ->**NOTE** ->This way you don't have to check if the item is the last one, the comma will ->only be added in between two items. - -### If - -The `if` statement in Twig is comparable with the if statements of PHP. In the -simplest form you can use it to test if a variable is defined, not empty or -not false: - - [twig] - {% if users %} - - {% endif %} - -For multiple branches `elseif` and `else` can be used like in PHP. You can use -more complex `expressions` there too: - - {% if kenny.sick %} - Kenny is sick. - {% elseif kenny.dead %} - You killed Kenny! You bastard!!! - {% else %} - Kenny looks okay --- so far - {% endif %} - -### Macros - -Macros are comparable with functions in regular programming languages. They -are useful to put often used HTML idioms into reusable elements to not repeat -yourself. - -Here is a small example of a macro that renders a form element: - - [twig] - {% macro input(name, value, type, size) %} - - {% endmacro %} - -Macros differs from native PHP functions in a few ways: - - * Default argument values are defined by using the `default` filter in the - macro body; - - * Arguments of a macro are always optional. - -But as PHP functions, macros don't have access to the current template -variables. - ->**TIP** ->You can pass the whole context as an argument by using the special `_context` ->variable. - -Macros can be defined in any template, and need to be "imported" before being -used (see the Import section for more information): - - [twig] - {% import "forms.html" as forms %} - -The above `import` call imports the "forms.html" file (which can contain only -macros, or a template and some macros), and import the functions as items of -the `forms` variable. - -The macro can then be called at will: - - [twig] -

    {{ forms.input('username') }}

    -

    {{ forms.input('password', none, 'password') }}

    - -If macros are defined and used in the same template, you can use the -special `_self` variable, without importing them: - - [twig] -

    {{ _self.input('username') }}

    - -When you want to use a macro in another one from the same file, use the `_self` -variable: - - [twig] - {% macro input(name, value, type, size) %} - - {% endmacro %} - - {% macro wrapped_input(name, value, type, size) %} -
    - {{ _self.input(name, value, type, size) }} -
    - {% endmacro %} - -When the macro is defined in another file, you need to import it: - - [twig] - {# forms.html #} - - {% macro input(name, value, type, size) %} - - {% endmacro %} - - {# shortcuts.html #} - - {% macro wrapped_input(name, value, type, size) %} - {% import "forms.html" as forms %} -
    - {{ forms.input(name, value, type, size) }} -
    - {% endmacro %} - -### Filters - -Filter sections allow you to apply regular Twig filters on a block of template -data. Just wrap the code in the special `filter` section: - - [twig] - {% filter upper %} - This text becomes uppercase - {% endfilter %} - -You can also chain filters: - - [twig] - {% filter lower|escape %} - SOME TEXT - {% endfilter %} - -It should returns `<strong>some text</strong>`. - -### Assignments - -Inside code blocks you can also assign values to variables. Assignments use -the `set` tag and can have multiple targets: - - [twig] - {% set foo = 'foo' %} - - {% set foo = [1, 2] %} - - {% set foo = {'foo': 'bar'} %} - - {% set foo = 'foo' ~ 'bar' %} - - {% set foo, bar = 'foo', 'bar' %} - -The `set` tag can also be used to 'capture' chunks of HTML (new in Twig -0.9.6): - - [twig] - {% set foo %} - - {% endset %} - -### Extends - -The `extends` tag can be used to extend a template from another one. You can -have multiple of them in a file but only one of them may be executed at the -time. There is no support for multiple inheritance. See the section about -Template inheritance above for more information. - -### Block - -Blocks are used for inheritance and act as placeholders and replacements at -the same time. They are documented in detail as part of the section about -Template inheritance above. - -### Include - -The `include` statement is useful to include a template and return the -rendered content of that file into the current namespace: - - [twig] - {% include 'header.html' %} - Body - {% include 'footer.html' %} - -Included templates have access to the variables of the active context. - -You can add additional variables by passing them after the `with` keyword: - - [twig] - {# the foo template will have access to the variables from the current context and the foo one #} - {% include 'foo' with ['foo': 'bar'] %} - - {% set vars = {'foo': 'bar'} %} - {% include 'foo' with vars %} - -You can disable access to the context by appending the `only` keyword: - - [twig] - {# only the foo variable will be accessible #} - {% include 'foo' with ['foo': 'bar'] only %} - - [twig] - {# no variable will be accessible #} - {% include 'foo' only %} - ->**NOTE** ->The `with` keyword is supported as of Twig 0.9.5. ->The `only` keyword is supported as of Twig 0.9.9. - -- - ->**TIP** ->When including a template created by an end user, you should consider ->sandboxing it. More information in the "Twig for Developers" chapter. - -The template name can be any valid Twig expression: - - [twig] - {% include some_var %} - {% include ajax ? 'ajax.html' : 'not_ajax.html' %} - -And if the variable evaluates to a `Twig_Template` object, Twig will use it -directly: - - // {% include template %} - - $template = $twig->loadTemplate('some_template.twig'); - - $twig->display('template.twig', array('template' => $template)); - -### Import - -Twig supports putting often used code into macros. These macros can go into -different templates and get imported from there. - -Imagine we have a helper module that renders forms (called `forms.html`): - - [twig] - {% macro input(name, value, type, size) %} - - {% endmacro %} - - {% macro textarea(name, value, rows) %} - - {% endmacro %} - -Importing these macros in a template is as easy as using the `import` tag: - - [twig] - {% import 'forms.html' as forms %} - -
    -
    Username
    -
    {{ forms.input('username') }}
    -
    Password
    -
    {{ forms.input('password', none, 'password') }}
    -
    -

    {{ forms.textarea('comment') }}

    - -Importing is not needed if the macros and the template are defined in the file; -use the special `_self` variable instead: - - [twig] - {# index.html template #} - - {% macro textarea(name, value, rows) %} - - {% endmacro %} - -

    {{ _self.textarea('comment') }}

    - -But you can still create an alias by importing from the `_self` variable: - - [twig] - {# index.html template #} - - {% macro textarea(name, value, rows) %} - - {% endmacro %} - - {% import _self as forms %} - -

    {{ forms.textarea('comment') }}

    - -Expressions ------------ - -Twig allows basic expressions everywhere. These work very similar to regular -PHP and even if you're not working with PHP you should feel comfortable with -it. - -The operator precedence is as follows, with the lowest-precedence operators -listed first: `or`, `and`, `==`, `!=`, `<`, `>`, `>=`, `<=`, `in`, `+`, `-`, -`~`, `*`, `/`, `%`, `//`, `is`, `..`, and `**`. - ->**CAUTION** ->When compiling deep-nested arrays or math expressions with Xdebug enabled, ->Twig can easily reach the default maximum nesting level set by Xdebug via the ->`xdebug.max_nesting_level` setting; changing the default (100) to a bigger ->value solves the issue. - -### Literals - -The simplest form of expressions are literals. Literals are representations -for PHP types such as strings, numbers, and arrays. The following literals -exist: - - * `"Hello World"`: Everything between two double or single quotes is a - string. They are useful whenever you need a string in the template (for - example as arguments to function calls, filters or just to extend or - include a template). - - * `42` / `42.23`: Integers and floating point numbers are created by just - writing the number down. If a dot is present the number is a float, - otherwise an integer. - - * `["foo", "bar"]` (new in Twig 0.9.5): Arrays are defined by a sequence of - expressions separated by a comma (`,`) and wrapped with squared brackets - (`[]`). - - * `{"foo": "bar"}` (new in Twig 0.9.10): Hashes are defined by a list of keys - and values separated by a comma (`,`) and wrapped with curly braces (`{}`). - A value can be any valid expression. - - * `true` / `false`: `true` represents the true value, `false` - represents the false value. - - * `none`: `none` represents no specific value (the equivalent of `null` in - PHP). This is the value returned when a variable does not exist. - -Arrays and hashes can be nested: - - [Twig] - {% set foo = [1, {"foo": "bar"}] %} - -### Math - -Twig allows you to calculate with values. This is rarely useful in templates -but exists for completeness' sake. The following operators are supported: - - * `+`: Adds two objects together (the operands are casted to numbers). - `{{ 1 + 1 }}` is `2`. - - * `-`: Substracts the second number from the first one. `{{ 3 - 2 }}` is `1`. - - * `/`: Divides two numbers. The return value will be a floating point number. - `{{ 1 / 2 }}` is `{{ 0.5 }}`. - - * `%`: Calculates the remainder of an integer division. `{{ 11 % 7 }}` is `4`. - - * `//`: Divides two numbers and returns the truncated integer result. `{{ 20 // - 7 }}` is `2`. - - * `*`: Multiplies the left operand with the right one. `{{ 2 * 2 }}` would - return `4`. - - * `**`: Raises the left operand to the power of the right operand. `{{ 2**3 - }}` would return `8`. - -### Logic - -For `if` statements, `for` filtering or `if` expressions it can be useful to -combine multiple expressions: - - * `and`: Returns true if the left and the right operands are both true. - - * `or`: Returns true if the left or the right operand is true. - - * `not`: Negates a statement. - - * `(expr)`: Groups an expression. - -### Comparisons - -The following comparison operators are supported in any expression: `==`, -`!=`, `<`, `>`, `>=`, and `<=`. - -### Containment Operator (new in Twig 0.9.5) - -The `in` operator performs containment test. - -It returns `true` if the left operand is contained in the right: - - [twig] - {# returns true #} - - {{ 1 in [1, 2, 3] }} - - {{ 'cd' in 'abcde' }} - -.. tip:: - - You can use this filter to perform a containment test on strings, arrays, - or objects implementing the `Traversable` interface. - -To perform a negative test, use the `not in` operator: - - [twig] - {% if 1 not in [1, 2, 3] %} - - {# is equivalent to #} - {% if not (1 in [1, 2, 3]) %} - -### Tests (new in Twig 0.9.9) - -The `is` operator performs tests. Tests can be used to test a variable against -a common expression. The right operand is name of the test: - - [twig] - {# find out if a variable is odd #} - - {{ name is odd }} - -Tests can accept arguments too: - - [twig] - {% if loop.index is divisibleby(3) %} - -Tests can be negated by using the `not in` operator: - - [twig] - {% if loop.index is not divisibleby(3) %} - - {# is equivalent to #} - {% if not (loop.index is divisibleby(3)) %} - -The built-in tests section below describes all the built-in tests. - -### Other Operators - -The following operators are very useful but don't fit into any of the other -two categories: - - * `..` (new in Twig 0.9.5): Creates a sequence based on the operand before - and after the operator (see the `for` tag for some usage examples). - - * `|`: Applies a filter. - - * `~`: Converts all operands into strings and concatenates them. `{{ "Hello " - ~ name ~ "!" }}` would return (assuming `name` is `'John'`) `Hello John!`. - - * `.`, `[]`: Gets an attribute of an object. - - * `?:`: Twig supports the PHP ternary operator: - - [twig] - {{ foo ? 'yes' : 'no' }} - -List of built-in Filters ------------------------- - -### `date` - -The `date` filter is able to format a date to a given format: - - [twig] - {{ post.published_at|date("m/d/Y") }} - -The `date` filter accepts any date format supported by -[`DateTime`](http://www.php.net/manual/en/datetime.construct.php) and -`DateTime` instances. For instance, to display the current date, filter the -word "now": - - [twig] - {{ "now"|date("m/d/Y") }} - -### `format` - -The `format` filter formats a given string by replacing the placeholders -(placeholders follows the `printf` notation): - - [twig] - {{ "I like %s and %s."|format(foo, "bar") }} - - {# returns I like foo and bar. (if the foo parameter equals to the foo string) #} - -### `replace` (new in Twig 0.9.9) - -The `replace` filter formats a given string by replacing the placeholders -(placeholders are free-form): - - [twig] - {{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }} - - {# returns I like foo and bar. (if the foo parameter equals to the foo string) #} - -### `cycle` - -The `cycle` filter can be used to cycle between an array of values: - - [twig] - {% for i in 0..10 %} - {{ ['odd', 'even']|cycle(i) }} - {% endfor %} - -The array can contain any number of values: - - [twig] - {% set fruits = ['apple', 'orange', 'citrus'] %} - - {% for i in 0..10 %} - {{ fruits|cycle(i) }} - {% endfor %} - -### `url_encode` - -The `url_encode` filter URL encodes a given string. - -### `json_encode` - -The `json_encode` filter returns the JSON representation of a string. - -### `title` - -The `title` filter returns a titlecased version of the value. I.e. words will -start with uppercase letters, all remaining characters are lowercase. - -### `capitalize` - -The `capitalize` filter capitalizes a value. The first character will be -uppercase, all others lowercase. - -### `upper` - -The `upper` filter converts a value to uppercase. - -### `lower` - -The `lower` filter converts a value to lowercase. - -### `striptags` - -The `striptags` filter strips SGML/XML tags and replace adjacent whitespace by -one space. - -### `join` - -The `join` filter returns a string which is the concatenation of the strings -in the sequence. The separator between elements is an empty string per -default, you can define it with the optional parameter: - - [twig] - {{ [1, 2, 3]|join('|') }} - {# returns 1|2|3 #} - - {{ [1, 2, 3]|join }} - {# returns 123 #} - -### `reverse` - -The `reverse` filter reverses an array or an object if it implements the -`Iterator` interface. - -### `length` - -The `length` filters returns the number of items of a sequence or mapping, or -the length of a string. - -### `sort` - -The `sort` filter sorts an array. - -### `range` (new in Twig 0.9.5) - -Returns a list containing a sequence of numbers. The left side of the filter -represents the low value. The first argument of the filter is mandatory and -represents the high value. The second argument is optional and represents the -step (which defaults to `1`). - -If you do need to iterate over a sequence of numbers: - - [twig] - {% for i in 0|range(10) %} - * {{ i }} - {% endfor %} - ->**TIP** ->The `range` filter works as the native PHP `range` function. - -The `..` operator (see above) is a syntactic sugar for the `range` filter -(with a step of 1): - - [twig] - {% for i in 0|range(10) %} - * {{ i }} - {% endfor %} - - {# is equivalent to #} - - {% for i in 0..10 %} - * {{ i }} - {% endfor %} - -### `default` - -The `default` filter returns the passed default value if the value is -undefined, otherwise the value of the variable: - - [twig] - {{ my_variable|default('my_variable is not defined') }} - -### `keys` - -The `keys` filter returns the keys of an array. It is useful when you want to -iterate over the keys of an array: - - [twig] - {% for key in array|keys %} - ... - {% endfor %} - -### `escape`, `e` - -The `escape` filter converts the characters `&`, `<`, `>`, `'`, and `"` in -strings to HTML-safe sequences. Use this if you need to display text that -might contain such characters in HTML. - ->**NOTE** ->Internally, `escape` uses the PHP `htmlspecialchars` function. - -### `raw` - -The `raw` filter marks the value as safe which means that in an environment -with automatic escaping enabled this variable will not be escaped if `raw` is -the last filter applied to it. - - [twig] - {% autoescape on } - {{ var|raw }} {# var won't be escaped #} - {% autoescape off %} - -### `constant` (new in Twig 0.9.9) - -`constant` returns the constant value for a given string: - - [twig] - {{ some_date|date('DATE_W3C'|constant) }} - -List of built-in Tests (new in Twig 0.9.9) ------------------------------------------- - -### `divisibleby` - -`divisibleby` checks if a variable is divisible by a number: - - [twig] - {% if loop.index is divisibleby(3) %} - -### `none` - -`none` returns `true` if the variable is `none`: - - [twig] - {{ var is none }} - -### `even` - -`even` returns `true` if the given number is even: - - [twig] - {{ var is even }} - -### `odd` - -`odd` returns `true` if the given number is odd: - - [twig] - {{ var is odd }} - -### `sameas` - -`sameas` checks if a variable points to the same memory address than another -variable: - - [twig] - {% if foo.attribute is sameas(false) %} - the foo attribute really is the `false` PHP value - {% endif %} - -### `constant` - -`constant` checks if a variable has the exact same value as a constant. You -can use either global constants or class constants: - - [twig] - {% if post.status is constant('Post::PUBLISHED') %} - the status attribute is exactly the same as Post::PUBLISHED - {% endif %} - -### `defined` - -`defined` checks if a variable is defined in the current context. This is very -useful if you use the `strict_variables` option: - - [twig] - {# defined works with variable names #} - {% if foo is defined %} - ... - {% endif %} - - {# and attributes on variables names #} - {% if foo.bar is defined %} - ... - {% endif %} - -Extensions ----------- - -Twig can be easily extended. If you are looking for new tags or filters, have -a look at the Twig official extension repository: -http://github.com/fabpot/Twig-extensions. diff --git a/doc/03-Twig-for-Developers.markdown b/doc/03-Twig-for-Developers.markdown deleted file mode 100644 index 4ced847..0000000 --- a/doc/03-Twig-for-Developers.markdown +++ /dev/null @@ -1,464 +0,0 @@ -Twig for Developers -=================== - -This chapter describes the API to Twig and not the template language. It will -be most useful as reference to those implementing the template interface to -the application and not those who are creating Twig templates. - -Basics ------- - -Twig uses a central object called the **environment** (of class -`Twig_Environment`). Instances of this class are used to store the -configuration and extensions, and are used to load templates from the file -system or other locations. - -Most applications will create one `Twig_Environment` object on application -initialization and use that to load templates. In some cases it's however -useful to have multiple environments side by side, if different configurations -are in use. - -The simplest way to configure Twig to load templates for your application -looks roughly like this: - - [php] - require_once '/path/to/lib/Twig/Autoloader.php'; - Twig_Autoloader::register(); - - $loader = new Twig_Loader_Filesystem('/path/to/templates'); - $twig = new Twig_Environment($loader, array( - 'cache' => '/path/to/compilation_cache', - )); - -This will create a template environment with the default settings and a loader -that looks up the templates in the `/path/to/templates/` folder. Different -loaders are available and you can also write your own if you want to load -templates from a database or other resources. - ->**CAUTION** ->Before Twig 0.9.3, the `cache` option did not exist, and the cache directory ->was passed as a second argument of the loader. - -- - ->**NOTE** ->Notice that the second argument of the environment is an array of options. ->The `cache` option is a compilation cache directory, where Twig caches the ->compiled templates to avoid the parsing phase for sub-sequent requests. It ->is very different from the cache you might want to add for the evaluated ->templates. For such a need, you can use any available PHP cache library. - -To load a template from this environment you just have to call the -`loadTemplate()` method which then returns a `Twig_Template` instance: - - [php] - $template = $twig->loadTemplate('index.html'); - -To render the template with some variables, call the `render()` method: - - [php] - echo $template->render(array('the' => 'variables', 'go' => 'here')); - ->**NOTE** ->The `display()` method is a shortcut to output the template directly. - -Environment Options -------------------- - -When creating a new `Twig_Environment` instance, you can pass an array of -options as the constructor second argument: - - [php] - $twig = new Twig_Environment($loader, array('debug' => true)); - -The following options are available: - - * `debug`: When set to `true`, the generated templates have a `__toString()` - method that you can use to display the generated nodes (default to - `false`). - - * `charset`: The charset used by the templates (default to `utf-8`). - - * `base_template_class`: The base template class to use for generated - templates (default to `Twig_Template`). - - * `cache`: An absolute path where to store the compiled templates, or false - to disable caching (which is the default). - - * `auto_reload`: When developing with Twig, it's useful to recompile the - template whenever the source code changes. If you don't provide a value for - the `auto_reload` option, it will be determined automatically based on the - `debug` value. - - * `strict_variables` (new in Twig 0.9.7): If set to `false`, Twig will - silently ignore invalid variables (variables and or attributes/methods that - do not exist) and replace them with a `null` value. When set to `true`, - Twig throws an exception instead (default to `false`). - - * `autoescape` (new in Twig 0.9.10): If set to `true`, auto-escaping will be - enabled by default for all templates (default to `true`). - - * `optimizations` (new in Twig 0.9.10): A flag that indicates which - optimizations to apply (default to `-1` -- all optimizations are enabled; - set it to `0` to disable). - ->**CAUTION** ->Before Twig 0.9.3, the `cache` and `auto_reload` options did not exist. They ->were passed as a second and third arguments of the filesystem loader ->respectively. - -Loaders -------- - ->**CAUTION** ->This section describes the loaders as implemented in Twig version 0.9.4 and ->above. - -Loaders are responsible for loading templates from a resource such as the file -system. - -### Compilation Cache - -All template loaders can cache the compiled templates on the filesystem for -future reuse. It speeds up Twig a lot as templates are only compiled once; -and the performance boost is even larger if you use a PHP accelerator such as -APC. See the `cache` and `auto_reload` options of `Twig_Environment` above for -more information. - -### Built-in Loaders - -Here is a list of the built-in loaders Twig provides: - - * `Twig_Loader_Filesystem`: Loads templates from the file system. This loader - can find templates in folders on the file system and is the preferred way - to load them. - - [php] - $loader = new Twig_Loader_Filesystem($templateDir); - - It can also look for templates in an array of directories: - - [php] - $loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2)); - - With such a configuration, Twig will first look for templates in - `$templateDir1` and if they do not exist, it will fallback to look for them - in the `$templateDir2`. - - * `Twig_Loader_String`: Loads templates from a string. It's a dummy loader as - you pass it the source code directly. - - [php] - $loader = new Twig_Loader_String(); - - * `Twig_Loader_Array`: Loads a template from a PHP array. It's passed an - array of strings bound to template names. This loader is useful for unit - testing. - - [php] - $loader = new Twig_Loader_Array($templates); - ->**TIP** ->When using the `Array` or `String` loaders with a cache mechanism, you should ->know that a new cache key is generated each time a template content "changes" ->(the cache key being the source code of the template). If you don't want to ->see your cache grows out of control, you need to take care of clearing the old ->cache file by yourself. - -### Create your own Loader - -All loaders implement the `Twig_LoaderInterface`: - - [php] - interface Twig_LoaderInterface - { - /** - * Gets the source code of a template, given its name. - * - * @param string $name string The name of the template to load - * - * @return string The template source code - */ - public function getSource($name); - - /** - * Gets the cache key to use for the cache for a given template name. - * - * @param string $name string The name of the template to load - * - * @return string The cache key - */ - public function getCacheKey($name); - - /** - * Returns true if the template is still fresh. - * - * @param string $name The template name - * @param timestamp $time The last modification time of the cached template - */ - public function isFresh($name, $time); - } - -As an example, here is how the built-in `Twig_Loader_String` reads: - - [php] - class Twig_Loader_String implements Twig_LoaderInterface - { - public function getSource($name) - { - return $name; - } - - public function getCacheKey($name) - { - return $name; - } - - public function isFresh($name, $time) - { - return false; - } - } - -The `isFresh()` method must return `true` if the current cached template is -still fresh, given the last modification time, or `false` otherwise. - -Using Extensions ----------------- - -Twig extensions are packages that adds new features to Twig. Using an -extension is as simple as using the `addExtension()` method: - - [php] - $twig->addExtension(new Twig_Extension_Sandbox()); - -Twig comes bundled with the following extensions: - - * *Twig_Extension_Core*: Defines all the core features of Twig and is automatically - registered when you create a new environment. - - * *Twig_Extension_Escaper*: Adds automatic output-escaping and the possibility to - escape/unescape blocks of code. - - * *Twig_Extension_Sandbox*: Adds a sandbox mode to the default Twig environment, making it - safe to evaluated untrusted code. - - * *Twig_Extension_Optimizer*: Optimizers the node tree before compilation (as - of Twig 0.9.10). - -The core, escaper, and optimizer extensions do not need to be added to the -Twig environment, as they are registered by default. You can disable an -already registered extension: - - [php] - $twig->removeExtension('escaper'); - -Built-in Extensions -------------------- - -This section describes the features added by the built-in extensions. - ->**TIP** ->Read the chapter about extending Twig to learn how to create your own ->extensions. - -### Core Extension - -The `core` extension defines all the core features of Twig: - - * Tags: - - * `for` - * `if` - * `extends` - * `include` - * `block` - * `parent` - * `display` - * `filter` - * `macro` - * `import` - * `set` - - * Filters: - - * `date` - * `format` - * `even` - * `odd` - * `urlencode` - * `title` - * `capitalize` - * `upper` - * `lower` - * `striptags` - * `join` - * `reverse` - * `length` - * `sort` - * `in` - * `range` - * `cycle` - * `default` - * `keys` - * `items` - * `escape` - * `e` - -### Escaper Extension - -The `escaper` extension adds automatic output escaping to Twig. It defines a -new tag, `autoescape`, and a new filter, `raw`. - -When creating the escaper extension, you can switch on or off the global -output escaping strategy: - - [php] - $escaper = new Twig_Extension_Escaper(true); - $twig->addExtension($escaper); - -If set to `true`, all variables in templates are escaped, except those using -the `raw` filter: - - [twig] - {{ article.to_html|raw }} - -You can also change the escaping mode locally by using the `autoescape` tag: - - [twig] - {% autoescape on %} - {% var %} - {% var|raw %} {# var won't be escaped #} - {% var|escape %} {# var won't be doubled-escaped #} - {% endautoescape %} - ->**WARNING** ->The `autoescape` tag has no effect on included files. - -The escaping rules are implemented as follows (it describes the behavior of -Twig 0.9.9 and above): - - * Literals (integers, booleans, arrays, ...) used in the template directly as - variables or filter arguments are never automatically escaped: - - [twig] - {{ "Twig
    " }} {# won't be escaped #} - - {% set text = "Twig
    " %} - {{ text }} {# will be escaped #} - - * Expressions which the result is always a literal or a variable marked safe - are never automatically escaped: - - [twig] - {{ foo ? "Twig
    " : "
    Twig" }} {# won't be escaped #} - - {% set text = "Twig
    " %} - {{ foo ? text : "
    Twig" }} {# will be escaped #} - - {% set text = "Twig
    " %} - {{ foo ? text|raw : "
    Twig" }} {# won't be escaped #} - - {% set text = "Twig
    " %} - {{ foo ? text|escape : "
    Twig" }} {# the result of the expression won't be escaped #} - - * Escaping is applied before printing, after any other filter is applied: - - [twig] - {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #} - - * The `raw` filter should only be used at the end of the filter chain: - - [twig] - {{ var|raw|upper }} {# will be escaped #} - - [twig] - {{ var|upper|raw }} {# won't be escaped #} - - * Automatic escaping is not applied if the last filter in the chain is marked - safe for the current context (e.g. `html` or `js`). `escaper` and - `escaper('html')` are marked safe for html, `escaper('js')` is marked safe - for javascript, `raw` is marked safe for everything. - - [twig] - {% autoescape js on %} - {{ var|escape('html') }} {# will be escaped for html and javascript #} - {{ var }} {# will be escaped for javascript #} - {{ var|escape('js') }} {# won't be double-escaped #} - {% endautoescape %} - -### Sandbox Extension - -The `sandbox` extension can be used to evaluate untrusted code. Access to -unsafe attributes and methods is prohibited. The sandbox security is managed -by a policy instance. By default, Twig comes with one policy class: -`Twig_Sandbox_SecurityPolicy`. This class allows you to white-list some tags, -filters, properties, and methods: - - [php] - $tags = array('if'); - $filters = array('upper'); - $methods = array( - 'Article' => array('getTitle', 'getBody'), - ); - $properties = array( - 'Article' => array('title', 'body), - ); - $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties); - -With the previous configuration, the security policy will only allow usage of -the `if` tag, and the `upper` filter. Moreover, the templates will only be -able to call the `getTitle()` and `getBody()` methods on `Article` objects, -and the `title` and `body` public properties. Everything else won't be allowed -and will generate a `Twig_Sandbox_SecurityError` exception. - -The policy object is the first argument of the sandbox constructor: - - [php] - $sandbox = new Twig_Extension_Sandbox($policy); - $twig->addExtension($sandbox); - -By default, the sandbox mode is disabled and should be enabled when including -untrusted template code by using the `sandbox` tag: - - [twig] - {% sandbox %} - {% include 'user.html' %} - {% endsandbox %} - -You can sandbox all templates by passing `true` as the second argument of the -extension constructor: - - [php] - $sandbox = new Twig_Extension_Sandbox($policy, true); - -### Optimizer Extension (as of Twig 0.9.10) - -The `optimizer` extension optimizes the node tree before compilation: - - [php] - $twig->addExtension(new Twig_Extension_Optimizer()); - -By default, all optimizations are turned on. You can select the ones you want -to enable by passing them to the constructor: - - [php] - $optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR); - - $twig->addExtension($optimizer); - -Exceptions ----------- - -Twig can throw exceptions: - - * `Twig_Error`: The base exception for all template errors. - - * `Twig_SyntaxError`: Thrown to tell the user that there is a problem with - the template syntax. - - * `Twig_RuntimeError`: Thrown when an error occurs at runtime (when a filter - does not exist for instance). - - * `Twig_Sandbox_SecurityError`: Thrown when an unallowed tag, filter, or - method is called in a sandboxed template. diff --git a/doc/04-Extending-Twig.markdown b/doc/04-Extending-Twig.markdown deleted file mode 100644 index 02a60ac..0000000 --- a/doc/04-Extending-Twig.markdown +++ /dev/null @@ -1,556 +0,0 @@ -Extending Twig -============== - -Twig supports extensions that can add extra tags, filters, or even extend the -parser itself with node visitor classes. The main motivation for writing -an extension is to move often used code into a reusable class like adding -support for internationalization. - -Most of the time, it is useful to create a single extension for your project, -to host all the specific tags and filters you want to add to Twig. - ->**NOTE** ->Before writing your own extensions, have a look at the Twig official extension ->repository: http://github.com/fabpot/Twig-extensions. - -Extending without an Extension (new in Twig 0.9.7) --------------------------------------------------- - -If you just need to register a small amount of tags and/or filters, you can -register them without creating an extension: - - [php] - $twig = new Twig_Environment($loader); - $twig->addTokenParser(new CustomTokenParser()); - $twig->addFilter('upper', new Twig_Filter_Function('strtoupper')); - -Anatomy of an Extension ------------------------ - -An extension is a class that implements the following interface: - - [php] - interface Twig_ExtensionInterface - { - /** - * Initializes the runtime environment. - * - * This is where you can load some file that contains filter functions for instance. - */ - public function initRuntime(); - - /** - * Returns the token parser instances to add to the existing list. - * - * @return array An array of Twig_TokenParser instances - */ - public function getTokenParsers(); - - /** - * Returns the node visitor instances to add to the existing list. - * - * @return array An array of Twig_NodeVisitorInterface instances - */ - public function getNodeVisitors(); - - /** - * Returns a list of filters to add to the existing list. - * - * @return array An array of filters - */ - public function getFilters(); - - /** - * Returns a list of tests to add to the existing list. - * - * @return array An array of tests - */ - public function getTests(); - - /** - * Returns the name of the extension. - * - * @return string The extension name - */ - public function getName(); - } - -To keep your extension class clean and lean, it can inherit from the built-in -`Twig_Extension` class instead of implementing the whole interface. That -way, you just need to implement the `getName()` method as the -`Twig_Extension` provides empty implementations for all other methods. - -The `getName()` method must return a unique identifier for your extension. - -Now, with this information in mind, let's create the most basic extension -possible: - - [php] - class Project_Twig_Extension extends Twig_Extension - { - public function getName() - { - return 'project'; - } - } - ->**NOTE** ->Of course, this extension does nothing for now. We will add tags and filters ->in the coming sections. - -Twig does not care where you save your extension on the filesystem, as all -extensions must be registered explicitly to be available in your templates. - -You can register an extension by using the `addExtension()` method on your -main `Environment` object: - - [php] - $twig = new Twig_Environment($loader); - $twig->addExtension(new Project_Twig_Extension()); - -Of course, you need to first load the extension file by either using -`require_once()` or by using an autoloader (see -[`spl_autoload_register()`](http://www.php.net/spl_autoload_register)). - ->**TIP** ->The bundled extensions are great examples of how extensions work. - -Defining new Filters --------------------- - ->**CAUTION** ->This section describes the creation of new filters for Twig 0.9.5 and above. - -The most common element you will want to add to Twig is filters. A filter is -just a regular PHP function or a method that takes the left side of the filter -(before the pipe `|`) as first argument and the extra arguments passed to the -filter (within parentheses `()`) as extra arguments. - -For instance, let's say you have the following code in a template: - - [twig] - {{ 'TWIG'|lower }} - -When compiling this template to PHP, Twig will first look for the PHP function -associated with the `lower` filter. The `lower` filter is a built-in Twig -filter, and it is simply mapped to the PHP `strtolower()` function. After -compilation, the generated PHP code is roughly equivalent to: - - [php] - - -As you can see, the `'TWIG'` string is passed as a first argument to the -PHP function. - -A filter can also take extra arguments like in the following example: - - [twig] - {{ now|date('d/m/Y') }} - -In this case, the extra arguments are passed to the function after the main -argument, and the compiled code is equivalent to: - - [php] - - -### Function Filters - -Let's see how to create a new filter. - -In this section, we will create a `rot13` filter, which should return the -[rot13](http://www.php.net/manual/en/function.str-rot13.php) transformation of -a string. Here is an example of its usage and the expected output: - - [twig] - {{ "Twig"|rot13 }} - - {# should displays Gjvt #} - -Adding a filter is as simple as calling the `addFilter` method of the -`Twig_Environment` instance (new in Twig 0.9.7): - - [php] - $twig = new Twig_Environment($loader); - $twig->addFilter('upper', new Twig_Filter_Function('strtoupper')); - -To add a filter to an extension, you need to override the `getFilters()` -method. This method must return an array of filters to add to the Twig -environment: - - [php] - class Project_Twig_Extension extends Twig_Extension - { - public function getFilters() - { - return array( - 'rot13' => new Twig_Filter_Function('str_rot13'), - ); - } - - public function getName() - { - return 'project'; - } - } - -As you can see in the above code, the `getFilters()` method returns an array -where keys are the name of the filters (`rot13`) and the values the definition -of the filter (`new Twig_Filter_Function('str_rot13')`). - -The definition of a filter is always an object. For this first example, we -have defined a filter as an object of the `Twig_Filter_Function` class. - -The `Twig_Filter_Function` class is to be used when you need to define a -filter implemented as a function. The first argument passed to the -`Twig_Filter_Function` constructor is the name of the function to call, here -`str_rot13`, a native PHP function. - -Let's say I now want to be able to add a prefix before the converted string: - - [twig] - {{ "Twig"|rot13('prefix_') }} - - {# should displays prefix_Gjvt #} - -As the PHP `str_rot13()` function does not support this requirement, let's -create a new PHP function: - - [php] - function project_compute_rot13($string, $prefix = '') - { - return $prefix.str_rot13($string); - } - -As you can see, the `prefix` argument of the filter is passed as an extra -argument to the `project_compute_rot13()` function. - ->**NOTE** ->This function can declared anywhere by it is a good idea to define it in the ->same file as the extension class. - -The new extension code looks very similar to the previous one: - - [php] - class Project_Twig_Extension extends Twig_Extension - { - public function getFilters() - { - return array( - 'rot13' => new Twig_Filter_Function('project_compute_rot13'), - ); - } - - public function getName() - { - return 'project'; - } - } - -### Class Method Filters - -Instead of creating a function to define a filter as we have done before, you -can also create a static method in a class for better encapsulation. - -The `Twig_Filter_Function` class can also be used to register such static -methods as filters: - - [php] - class Project_Twig_Extension extends Twig_Extension - { - public function getFilters() - { - return array( - 'rot13' => new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'), - ); - } - - static public function rot13Filter($string) - { - return str_rot13($string); - } - - public function getName() - { - return 'project'; - } - } - -### Object Method Filters - -Defining static methods is one step towards better encapsulation, but defining -filters as methods of your extension class is probably the best solution. - -This is possible by using `Twig_Filter_Method` instead of -`Twig_Filter_Function` when defining a filter: - - [php] - class Project_Twig_Extension extends Twig_Extension - { - public function getFilters() - { - return array( - 'rot13' => new Twig_Filter_Method($this, 'rot13Filter'), - ); - } - - public function rot13Filter($string) - { - return str_rot13($string); - } - - public function getName() - { - return 'project'; - } - } - -The first argument of the `Twig_Filter_Method` constructor is always `$this`, -the current extension object. The second one is the name of the method to -call. - -Using methods for filters is a great way to package your filter without -polluting the global namespace. This also gives the developer more flexibility -at the cost of a small overhead. - -### Environment aware Filters - -The `Twig_Filter` classes take options as their last argument. For instance, if -you want access to the current environment instance in your filter, set the -`needs_environment` option to `true`: - - [php] - $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true)); - -Twig will then pass the current environment as the first argument to the -filter call: - - [php] - function twig_compute_rot13(Twig_Environment $env, $string) - { - // get the current charset for instance - $charset = $env->getCharset(); - - return str_rot13($string); - } - -### Automatic Escaping - -If automatic escaping is enabled, the output of the filter may be escaped before -printing. If your filter acts as an escaper (or explicitly outputs html or -javascript code), you will want the raw output to be printed. In such a case, -set the `is_safe` option: - - [php] - $filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html'))); - -Some advanced filters may have to work on already escaped or safe values. In -such a case, set the `pre_escape` option: - - [php] - $filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html'))); - -Overriding default Filters --------------------------- - ->**CAUTION** ->This section describes how to override default filters for Twig 0.9.5 and ->above. - -If some default core filters do not suit your needs, you can easily override -them by creating your own core extension. Of course, you don't need to copy -and paste the whole core extension code of Twig. Instead, you can just extends -it and override the filter(s) you want by overriding the `getFilters()` -method: - - [php] - class MyCoreExtension extends Twig_Extension_Core - { - public function getFilters() - { - return array_merge( - parent::getFilters(), - array( - 'date' => Twig_Filter_Method($this, 'dateFilter') - ) - ); - } - - public function dateFilter($timestamp, $format = 'F j, Y H:i') - { - return '...'.twig_date_format_filter($timestamp, $format); - } - } - -Here, we override the `date` filter with a custom one. Using this new core -extension is as simple as registering the `MyCoreExtension` extension by -calling the `addExtension()` method on the environment instance: - - [php] - $twig = new Twig_Environment($loader); - $twig->addExtension(new MyCoreExtension()); - -But I can already hear some people wondering how it can work as the Core -extension is loaded by default. That's true, but the trick is that both -extensions share the same unique identifier (`core` - defined in the -`getName()` method). By registering an extension with the same name as an -existing one, you have actually overridden the default one, even if it is -already registered: - - [php] - $twig->addExtension(new Twig_Extension_Core()); - $twig->addExtension(new MyCoreExtension()); - -Defining new Tags ------------------ - -One of the most exciting feature of a template engine like Twig is the -possibility to define new language constructs. - -Let's create a simple `set` tag that allows the definition of simple variables -from within a template. The tag can be used like follows: - - [twig] - {% set name = "value" %} - - {{ name }} - - {# should output value #} - ->**NOTE** ->The `set` tag is part of the Core extension and as such is always available. ->The built-in version is slightly more powerful and supports multiple ->assignments by default (cf. the template designers chapter for more ->information). - -Three steps are needed to define a new tag: - - * Defining a Token Parser class (responsible for parsing the template code) - - * Defining a Node class (responsible for converting the parsed code to PHP) - - * Registering the tag in an extension - -### Registering a new tag - -Adding a tag in an extension can be done by overriding the `getTokenParsers()` -method. This method must return an array of tags to add to the Twig -environment: - - [php] - class Project_Twig_Extension extends Twig_Extension - { - public function getTokenParsers() - { - return array(new Project_Set_TokenParser()); - } - - // ... - } - -In the above code, we have added a single new tag, defined by the -`Project_Set_TokenParser` class. The `Project_Set_TokenParser` class is -responsible for parsing the tag and compiling it to PHP. - -### Defining a Token Parser - -Now, let's see the actual code of this class: - - [php] - class Project_Set_TokenParser extends Twig_TokenParser - { - public function parse(Twig_Token $token) - { - $lineno = $token->getLine(); - $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(); - $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '='); - $value = $this->parser->getExpressionParser()->parseExpression(); - - $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); - - return new Project_Set_Node($name, $value, $lineno, $this->getTag()); - } - - public function getTag() - { - return 'set'; - } - } - -The `getTag()` method must return the tag we want to parse, here `set`. - -The `parse()` method is invoked whenever the parser encounters a `set` tag. It -should return a `Twig_Node` instance that represents the node (the -`Project_Set_Node` calls creating is explained in the next section). - -The parsing process is simplified thanks to a bunch of methods you can call -from the token stream (`$this->parser->getStream()`): - - * `test()`: Tests the type and optionally the value of the next token and - returns it. - - * `expect()`: Expects a token and returns it (like `test()`) or throw a - syntax error if not found (the second argument is the expected value of the - token). - - * `look()`: Looks a the next token. This is how you can have a look at the - next token without consuming it (after you are done with `look()`, you must - use `rewind()`). - -Parsing expressions is done by calling the `parseExpression()` like we did for -the `set` tag. - ->**TIP** ->Reading the existing `TokenParser` classes is the best way to learn all the ->nitty-gritty details of the parsing process. - -### Defining a Node - -The `Project_Set_Node` class itself is rather simple: - - [php] - class Project_Set_Node extends Twig_Node - { - public function __construct($name, Twig_Node_Expression $value, $lineno) - { - parent::__construct(array('value' => $value), array('name' => $name), $lineno); - } - - public function compile($compiler) - { - $compiler - ->addDebugInfo($this) - ->write('$context[\''.$this->getAttribute('name').'\'] = ') - ->subcompile($this->getNode('value')) - ->raw(";\n") - ; - } - } - -The compiler implements a fluid interface and provides methods that helps the -developer generate beautiful and readable PHP code: - - * `subcompile()`: Compiles a node. - - * `raw()`: Writes the given string as is. - - * `write()`: Writes the given string by adding indentation at the beginning - of each line. - - * `string()`: Writes a quoted string. - - * `repr()`: Writes a PHP representation of a given value (see `Twig_Node_For` - for a usage example). - - * `addDebugInfo()`: Adds the line of the original template file related to - the current node as a comment. - - * `indent()`: Indents the generated code (see `Twig_Node_Block` for a usage - example). - - * `outdent()`: Outdents the generated code (see `Twig_Node_Block` for a usage - example). - -Creating a Node Visitor ------------------------ - -To be written... diff --git a/doc/05-Hacking-Twig.markdown b/doc/05-Hacking-Twig.markdown deleted file mode 100644 index 74afd0d..0000000 --- a/doc/05-Hacking-Twig.markdown +++ /dev/null @@ -1,196 +0,0 @@ -Hacking Twig -============ - -Twig is very extensible and you can easily hack it. Keep in mind that you -should probably try to create an extension before hacking the core, as most -features and enhancements can be done with extensions. This chapter is also -useful for people who want to understand how Twig works under the hood. - -How Twig works? ---------------- - -The rendering of a Twig template can be summarized into four key steps: - - * **Load** the template: If the template is already compiled, load it and go - to the *evaluation* step, otherwise: - - * First, the **lexer** tokenizes the template source code into small pieces - for easier processing; - - * Then, the **parser** converts the token stream into a meaningful tree - of nodes (the Abstract Syntax Tree); - - * Eventually, the *compiler* transforms the AST into PHP code; - - * **Evaluate** the template: It basically means calling the `display()` - method of the compiled template and passing it the context. - -The Lexer ---------- - -The Twig lexer goal is to tokenize a source code into a token stream (each -token is of class `Token`, and the stream is an instance of -`Twig_TokenStream`). The default lexer recognizes nine different token types: - - * `Twig_Token::TEXT_TYPE` - * `Twig_Token::BLOCK_START_TYPE` - * `Twig_Token::VAR_START_TYPE` - * `Twig_Token::BLOCK_END_TYPE` - * `Twig_Token::VAR_END_TYPE` - * `Twig_Token::NAME_TYPE` - * `Twig_Token::NUMBER_TYPE` - * `Twig_Token::STRING_TYPE` - * `Twig_Token::OPERATOR_TYPE` - * `Twig_Token::EOF_TYPE` - -You can manually convert a source code into a token stream by calling the -`tokenize()` of an environment: - - [php] - $stream = $twig->tokenize($source, $identifier); - -As the stream has a `__toString()` method, you can have a textual -representation of it by echoing the object: - - [php] - echo $stream."\n"; - -Here is the output for the `Hello {{ name }}` template: - - [txt] - TEXT_TYPE(Hello ) - VAR_START_TYPE() - NAME_TYPE(name) - VAR_END_TYPE() - EOF_TYPE() - -You can change the default lexer use by Twig (`Twig_Lexer`) by calling the -`setLexer()` method: - - [php] - $twig->setLexer($lexer); - -Lexer classes must implement the `Twig_LexerInterface`: - - [php] - interface Twig_LexerInterface - { - /** - * Tokenizes a source code. - * - * @param string $code The source code - * @param string $filename A unique identifier for the source code - * - * @return Twig_TokenStream A token stream instance - */ - public function tokenize($code, $filename = 'n/a'); - } - -The Parser ----------- - -The parser converts the token stream into an AST (Abstract Syntax Tree), or a -node tree (of class `Twig_Node_Module`). The core extension defines the basic -nodes like: `for`, `if`, ... and the expression nodes. - -You can manually convert a token stream into a node tree by calling the -`parse()` method of an environment: - - [php] - $nodes = $twig->parse($stream); - -Echoing the node object gives you a nice representation of the tree: - - [php] - echo $nodes."\n"; - -Here is the output for the `Hello {{ name }}` template: - - [txt] - Twig_Node_Module( - Twig_Node_Text(Hello ) - Twig_Node_Print( - Twig_Node_Expression_Name(name) - ) - ) - -The default parser (`Twig_TokenParser`) can be also changed by calling the -`setParser()` method: - - [php] - $twig->setParser($parser); - -All Twig parsers must implement the `Twig_ParserInterface`: - - [php] - interface Twig_ParserInterface - { - /** - * Converts a token stream to a node tree. - * - * @param Twig_TokenStream $stream A token stream instance - * - * @return Twig_Node_Module A node tree - */ - public function parser(Twig_TokenStream $code); - } - -The Compiler ------------- - -The last step is done by the compiler. It takes a node tree as an input and -generates PHP code usable for runtime execution of the templates. The default -compiler generates PHP classes to ease the implementation of the template -inheritance feature. - -You can call the compiler by hand with the `compile()` method of an -environment: - - [php] - $php = $twig->compile($nodes); - -The `compile()` method returns the PHP source code representing the node. - -The generated template for a `Hello {{ name }}` template reads as follows: - - [php] - /* Hello {{ name }} */ - class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template - { - public function display($context) - { - $this->env->initRuntime(); - - // line 1 - echo "Hello "; - echo (isset($context['name']) ? $context['name'] : null); - } - } - -As for the lexer and the parser, the default compiler (`Twig_Compiler`) can be -changed by calling the `setCompiler()` method: - - [php] - $twig->setCompiler($compiler); - -All Twig compilers must implement the `Twig_CompilerInterface`: - - [php] - interface Twig_CompilerInterface - { - /** - * Compiles a node. - * - * @param Twig_Node $node The node to compile - * - * @return Twig_Compiler The current compiler instance - */ - public function compile(Twig_Node $node); - - /** - * Gets the current PHP code after compilation. - * - * @return string The PHP code - */ - public function getSource(); - } diff --git a/doc/06-Recipes.markdown b/doc/06-Recipes.markdown deleted file mode 100644 index 05f7d0e..0000000 --- a/doc/06-Recipes.markdown +++ /dev/null @@ -1,208 +0,0 @@ -Recipes -======= - -Making a Layout conditional ---------------------------- - -Working with Ajax means that the same content is sometimes displayed as is, -and sometimes decorated with a layout. As Twig layout template names can be -any valid expression, you can pass a variable that evaluates to `true` when -the request is made via Ajax and choose the layout accordingly: - - [twig] - {% extends request.ajax ? "base_ajax.html" : "base.html" %} - - {% block content %} - This is the content to be displayed. - {% endblock %} - -Making an Include dynamic -------------------------- - -When including a template, its name does not need to be a string. For -instance, the name can depend on the value of a variable: - - [twig] - {% include var ~ '_foo.html' %} - -If `var` evaluates to `index`, the `index_foo.html` template will be -rendered. - -As a matter of fact, the template name can be any valid expression, such as -the following: - - [twig] - {% include var|default('index') ~ '_foo.html' %} - -Customizing the Syntax ----------------------- - -Twig allows some syntax customization for the block delimiters. It's not -recommended to use this feature as templates will be tied with your custom -syntax. But for specific projects, it can make sense to change the defaults. - -To change the block delimiters, you need to create your own lexer object: - - [php] - $twig = new Twig_Environment(); - - $lexer = new Twig_Lexer($twig, array( - 'tag_comment' => array('{#', '#}'), - 'tag_block' => array('{%', '%}'), - 'tag_variable' => array('{{', '}}'), - )); - $twig->setLexer($lexer); - -Here are some configuration example that simulates some other template engines -syntax: - - [php] - // Ruby erb syntax - $lexer = new Twig_Lexer($twig, array( - 'tag_comment' => array('<%#', '%>'), - 'tag_block' => array('<%', '%>'), - 'tag_variable' => array('<%=', '%>'), - )); - - // SGML Comment Syntax - $lexer = new Twig_Lexer($twig, array( - 'tag_comment' => array(''), - 'tag_block' => array(''), - 'tag_variable' => array('${', '}'), - )); - - // Smarty like - $lexer = new Twig_Lexer($twig, array( - 'tag_comment' => array('{*', '*}'), - 'tag_block' => array('{', '}'), - 'tag_variable' => array('{$', '}'), - )); - -Using dynamic Object Properties -------------------------------- - -When Twig encounters a variable like `article.title`, it tries to find a -`title` public property in the `article` object. - -It also works if the property does not exist but is rather defined dynamically -thanks to the magic `__get()` method; you just need to also implement the -`__isset()` magic method like shown in the following snippet of code: - - [php] - class Article - { - public function __get($name) - { - if ('title' == $name) - { - return 'The title'; - } - - // throw some kind of error - } - - public function __isset($name) - { - if ('title' == $name) - { - return true; - } - - return false; - } - } - -Making the Templates aware of the Context ------------------------------------------ - -Sometimes, you want to make the templates aware of some "context" of your -application. But by default, the compiled templates are only passed an array -of parameters. - -When rendering a template, you can pass your context objects along, but it's -not very practical. There is a better solution. - -By default, all compiled templates extends a base class, the built-in -`Twig_Template`. This base class is configurable with the -`base_template_class` option: - - [php] - $twig = new Twig_Environment($loader, array('base_template_class' => 'ProjectTemplate')); - -Now, all templates will automatically extend the custom `ProjectTemplate` -class. Create the `ProjectTemplate` and add some getter/setter methods to -allow communication between your templates and your application: - - [php] - class ProjectTemplate extends Twig_Template - { - protected $context = null; - - public function setContext($context) - { - $this->context = $context; - } - - public function getContext() - { - return $this->context; - } - } - -Now, you can use the setter to inject the context whenever you create a -template, and use the getter from within your custom nodes. - -Accessing the parent Context in Nested Loops --------------------------------------------- - -Sometimes, when using nested loops, you need to access the parent context. The -parent context is always accessible via the `loop.parent` variable. For -instance, if you have the following template data: - - $data = array( - 'topics' => array( - 'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'), - 'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'), - ), - ); - -And the following template to display all messages in all topics: - - [twig] - {% for topic, messages in topics %} - * {{ loop.index }}: {{ topic }} - {% for message in messages %} - - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }} - {% endfor %} - {% endfor %} - -The output will be similar to: - - * 1: topic1 - - 1.1: The message 1 of topic 1 - - 1.2: The message 2 of topic 1 - * 2: topic2 - - 2.1: The message 1 of topic 2 - - 2.2: The message 2 of topic 2 - -In the inner loop, the `loop.parent` variable is used to access the outer -context. So, the index of the current `topic` defined in the outer for loop is -accessible via the `loop.parent.loop.index` variable. - -Passing a Macro as an Argument ------------------------------- - -(new in Twig 0.9.6) - -By default, a macro directly outputs its content to the screen. If you want to -pass the content of a macro as an argument to a method or to another macro, -you can use the `set` tag: - - [twig] - {% import "form_elements.html" as form %} - - {% set theinput %} - {{ form.input('test', 'text', 'Value') }} - {% endset %} - - {{ form.row('Label', theinput) }} diff --git a/doc/advanced.rst b/doc/advanced.rst new file mode 100644 index 0000000..696d407 --- /dev/null +++ b/doc/advanced.rst @@ -0,0 +1,558 @@ +Extending Twig +============== + +Twig supports extensions that can add extra tags, filters, or even extend the +parser itself with node visitor classes. The main motivation for writing +an extension is to move often used code into a reusable class like adding +support for internationalization. + +Most of the time, it is useful to create a single extension for your project, +to host all the specific tags and filters you want to add to Twig. + +.. note:: + + Before writing your own extensions, have a look at the Twig official + extension repository: http://github.com/fabpot/Twig-extensions. + +Extending without an Extension (new in Twig 0.9.7) +-------------------------------------------------- + +If you just need to register a small amount of tags and/or filters, you can +register them without creating an extension:: + + $twig = new Twig_Environment($loader); + $twig->addTokenParser(new CustomTokenParser()); + $twig->addFilter('upper', new Twig_Filter_Function('strtoupper')); + +Anatomy of an Extension +----------------------- + +An extension is a class that implements the following interface:: + + interface Twig_ExtensionInterface + { + /** + * Initializes the runtime environment. + * + * This is where you can load some file that contains filter functions for instance. + */ + function initRuntime(); + + /** + * Returns the token parser instances to add to the existing list. + * + * @return array An array of Twig_TokenParser instances + */ + function getTokenParsers(); + + /** + * Returns the node visitor instances to add to the existing list. + * + * @return array An array of Twig_NodeVisitorInterface instances + */ + function getNodeVisitors(); + + /** + * Returns a list of filters to add to the existing list. + * + * @return array An array of filters + */ + function getFilters(); + + /** + * Returns a list of tests to add to the existing list. + * + * @return array An array of tests + */ + function getTests(); + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + function getName(); + } + +To keep your extension class clean and lean, it can inherit from the built-in +``Twig_Extension`` class instead of implementing the whole interface. That +way, you just need to implement the ``getName()`` method as the +``Twig_Extension`` provides empty implementations for all other methods. + +The ``getName()`` method must return a unique identifier for your extension. + +Now, with this information in mind, let's create the most basic extension +possible:: + + class Project_Twig_Extension extends Twig_Extension + { + public function getName() + { + return 'project'; + } + } + +.. note:: + + Of course, this extension does nothing for now. We will add tags and + filters in the coming sections. + +Twig does not care where you save your extension on the filesystem, as all +extensions must be registered explicitly to be available in your templates. + +You can register an extension by using the ``addExtension()`` method on your +main ``Environment`` object:: + + $twig = new Twig_Environment($loader); + $twig->addExtension(new Project_Twig_Extension()); + +Of course, you need to first load the extension file by either using +``require_once()`` or by using an autoloader (see `spl_autoload_register()`_). + +.. tip:: + + The bundled extensions are great examples of how extensions work. + +Defining new Filters +-------------------- + +.. caution:: + + This section describes the creation of new filters for Twig 0.9.5 and + above. + +The most common element you will want to add to Twig is filters. A filter is +just a regular PHP function or a method that takes the left side of the filter +(before the pipe ``|``) as first argument and the extra arguments passed to +the filter (within parentheses ``()``) as extra arguments. + +For instance, let's say you have the following code in a template: + +.. code-block:: jinja + + {{ 'TWIG'|lower }} + +When compiling this template to PHP, Twig will first look for the PHP function +associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig +filter, and it is simply mapped to the PHP ``strtolower()`` function. After +compilation, the generated PHP code is roughly equivalent to:: + + + +As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP +function. + +A filter can also take extra arguments like in the following example: + +.. code-block:: jinja + + {{ now|date('d/m/Y') }} + +In this case, the extra arguments are passed to the function after the main +argument, and the compiled code is equivalent to:: + + + +Function Filters +~~~~~~~~~~~~~~~~ + +Let's see how to create a new filter. + +In this section, we will create a ``rot13`` filter, which should return the +`rot13`_ transformation of a string. Here is an example of its usage and the +expected output: + +.. code-block:: jinja + + {{ "Twig"|rot13 }} + + {# should displays Gjvt #} + +Adding a filter is as simple as calling the ``addFilter`` method of the +``Twig_Environment`` instance (new in Twig 0.9.7):: + + $twig = new Twig_Environment($loader); + $twig->addFilter('upper', new Twig_Filter_Function('strtoupper')); + +To add a filter to an extension, you need to override the ``getFilters()`` +method. This method must return an array of filters to add to the Twig +environment:: + + class Project_Twig_Extension extends Twig_Extension + { + public function getFilters() + { + return array( + 'rot13' => new Twig_Filter_Function('str_rot13'), + ); + } + + public function getName() + { + return 'project'; + } + } + +As you can see in the above code, the ``getFilters()`` method returns an array +where keys are the name of the filters (``rot13``) and the values the +definition of the filter (``new Twig_Filter_Function('str_rot13')``). + +The definition of a filter is always an object. For this first example, we +have defined a filter as an object of the ``Twig_Filter_Function`` class. + +The ``Twig_Filter_Function`` class is to be used when you need to define a +filter implemented as a function. The first argument passed to the +``Twig_Filter_Function`` constructor is the name of the function to call, here +``str_rot13``, a native PHP function. + +Let's say I now want to be able to add a prefix before the converted string: + +.. code-block:: jinja + + {{ "Twig"|rot13('prefix_') }} + + {# should displays prefix_Gjvt #} + +As the PHP ``str_rot13()`` function does not support this requirement, let's +create a new PHP function:: + + function project_compute_rot13($string, $prefix = '') + { + return $prefix.str_rot13($string); + } + +As you can see, the ``prefix`` argument of the filter is passed as an extra +argument to the ``project_compute_rot13()`` function. + +.. note:: + + This function can declared anywhere by it is a good idea to define it in + the same file as the extension class. + +The new extension code looks very similar to the previous one:: + + class Project_Twig_Extension extends Twig_Extension + { + public function getFilters() + { + return array( + 'rot13' => new Twig_Filter_Function('project_compute_rot13'), + ); + } + + public function getName() + { + return 'project'; + } + } + +Class Method Filters +~~~~~~~~~~~~~~~~~~~~ + +Instead of creating a function to define a filter as we have done before, you +can also create a static method in a class for better encapsulation. + +The ``Twig_Filter_Function`` class can also be used to register such static +methods as filters:: + + class Project_Twig_Extension extends Twig_Extension + { + public function getFilters() + { + return array( + 'rot13' => new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'), + ); + } + + static public function rot13Filter($string) + { + return str_rot13($string); + } + + public function getName() + { + return 'project'; + } + } + +Object Method Filters +~~~~~~~~~~~~~~~~~~~~~ + +Defining static methods is one step towards better encapsulation, but defining +filters as methods of your extension class is probably the best solution. + +This is possible by using ``Twig_Filter_Method`` instead of +``Twig_Filter_Function`` when defining a filter:: + + class Project_Twig_Extension extends Twig_Extension + { + public function getFilters() + { + return array( + 'rot13' => new Twig_Filter_Method($this, 'rot13Filter'), + ); + } + + public function rot13Filter($string) + { + return str_rot13($string); + } + + public function getName() + { + return 'project'; + } + } + +The first argument of the ``Twig_Filter_Method`` constructor is always +``$this``, the current extension object. The second one is the name of the +method to call. + +Using methods for filters is a great way to package your filter without +polluting the global namespace. This also gives the developer more flexibility +at the cost of a small overhead. + +Environment aware Filters +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Twig_Filter`` classes take options as their last argument. For instance, +if you want access to the current environment instance in your filter, set the +``needs_environment`` option to ``true``:: + + $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true)); + +Twig will then pass the current environment as the first argument to the +filter call:: + + function twig_compute_rot13(Twig_Environment $env, $string) + { + // get the current charset for instance + $charset = $env->getCharset(); + + return str_rot13($string); + } + +Automatic Escaping +~~~~~~~~~~~~~~~~~~ + +If automatic escaping is enabled, the output of the filter may be escaped +before printing. If your filter acts as an escaper (or explicitly outputs html +or javascript code), you will want the raw output to be printed. In such a +case, set the ``is_safe`` option:: + + $filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html'))); + +Some advanced filters may have to work on already escaped or safe values. In +such a case, set the ``pre_escape`` option:: + + $filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html'))); + +Overriding default Filters +-------------------------- + +.. caution:: + + This section describes how to override default filters for Twig 0.9.5 and + above. + +If some default core filters do not suit your needs, you can easily override +them by creating your own core extension. Of course, you don't need to copy +and paste the whole core extension code of Twig. Instead, you can just extends +it and override the filter(s) you want by overriding the ``getFilters()`` +method:: + + class MyCoreExtension extends Twig_Extension_Core + { + public function getFilters() + { + return array_merge( + parent::getFilters(), + array( + 'date' => Twig_Filter_Method($this, 'dateFilter') + ) + ); + } + + public function dateFilter($timestamp, $format = 'F j, Y H:i') + { + return '...'.twig_date_format_filter($timestamp, $format); + } + } + +Here, we override the ``date`` filter with a custom one. Using this new core +extension is as simple as registering the ``MyCoreExtension`` extension by +calling the ``addExtension()`` method on the environment instance:: + + $twig = new Twig_Environment($loader); + $twig->addExtension(new MyCoreExtension()); + +But I can already hear some people wondering how it can work as the Core +extension is loaded by default. That's true, but the trick is that both +extensions share the same unique identifier (``core`` - defined in the +``getName()`` method). By registering an extension with the same name as an +existing one, you have actually overridden the default one, even if it is +already registered:: + + $twig->addExtension(new Twig_Extension_Core()); + $twig->addExtension(new MyCoreExtension()); + +Defining new Tags +----------------- + +One of the most exciting feature of a template engine like Twig is the +possibility to define new language constructs. + +Let's create a simple ``set`` tag that allows the definition of simple +variables from within a template. The tag can be used like follows: + +.. code-block:: jinja + + {% set name = "value" %} + + {{ name }} + + {# should output value #} + +.. note:: + + The ``set`` tag is part of the Core extension and as such is always + available. The built-in version is slightly more powerful and supports + multiple assignments by default (cf. the template designers chapter for + more information). + +Three steps are needed to define a new tag: + +* Defining a Token Parser class (responsible for parsing the template code); + +* Defining a Node class (responsible for converting the parsed code to PHP); + +* Registering the tag in an extension. + +Registering a new tag +~~~~~~~~~~~~~~~~~~~~~ + +Adding a tag in an extension can be done by overriding the +``getTokenParsers()`` method. This method must return an array of tags to add +to the Twig environment:: + + class Project_Twig_Extension extends Twig_Extension + { + public function getTokenParsers() + { + return array(new Project_Set_TokenParser()); + } + + // ... + } + +In the above code, we have added a single new tag, defined by the +``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is +responsible for parsing the tag and compiling it to PHP. + +Defining a Token Parser +~~~~~~~~~~~~~~~~~~~~~~~ + +Now, let's see the actual code of this class:: + + class Project_Set_TokenParser extends Twig_TokenParser + { + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(); + $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '='); + $value = $this->parser->getExpressionParser()->parseExpression(); + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Project_Set_Node($name, $value, $lineno, $this->getTag()); + } + + public function getTag() + { + return 'set'; + } + } + +The ``getTag()`` method must return the tag we want to parse, here ``set``. + +The ``parse()`` method is invoked whenever the parser encounters a ``set`` +tag. It should return a ``Twig_Node`` instance that represents the node (the +``Project_Set_Node`` calls creating is explained in the next section). + +The parsing process is simplified thanks to a bunch of methods you can call +from the token stream (``$this->parser->getStream()``): + +* ``test()``: Tests the type and optionally the value of the next token and + returns it. + +* ``expect()``: Expects a token and returns it (like ``test()``) or throw a + syntax error if not found (the second argument is the expected value of the + token). + +* ``look()``: Looks a the next token. This is how you can have a look at the + next token without consuming it (after you are done with ``look()``, you + must use ``rewind()``). + +Parsing expressions is done by calling the ``parseExpression()`` like we did for +the ``set`` tag. + +.. tip:: + + Reading the existing ``TokenParser`` classes is the best way to learn all + the nitty-gritty details of the parsing process. + +Defining a Node +~~~~~~~~~~~~~~~ + +The ``Project_Set_Node`` class itself is rather simple:: + + class Project_Set_Node extends Twig_Node + { + public function __construct($name, Twig_Node_Expression $value, $lineno) + { + parent::__construct(array('value' => $value), array('name' => $name), $lineno); + } + + public function compile($compiler) + { + $compiler + ->addDebugInfo($this) + ->write('$context[\''.$this->getAttribute('name').'\'] = ') + ->subcompile($this->getNode('value')) + ->raw(";\n") + ; + } + } + +The compiler implements a fluid interface and provides methods that helps the +developer generate beautiful and readable PHP code: + +* ``subcompile()``: Compiles a node. + +* ``raw()``: Writes the given string as is. + +* ``write()``: Writes the given string by adding indentation at the beginning + of each line. + +* ``string()``: Writes a quoted string. + +* ``repr()``: Writes a PHP representation of a given value (see + ``Twig_Node_For`` for a usage example). + +* ``addDebugInfo()``: Adds the line of the original template file related to + the current node as a comment. + +* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a + usage example). + +* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a + usage example). + +Creating a Node Visitor +----------------------- + +To be written... + +.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register +.. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php diff --git a/doc/api.rst b/doc/api.rst new file mode 100644 index 0000000..ae59870 --- /dev/null +++ b/doc/api.rst @@ -0,0 +1,467 @@ +Twig for Developers +=================== + +This chapter describes the API to Twig and not the template language. It will +be most useful as reference to those implementing the template interface to +the application and not those who are creating Twig templates. + +Basics +------ + +Twig uses a central object called the **environment** (of class +``Twig_Environment``). Instances of this class are used to store the +configuration and extensions, and are used to load templates from the file +system or other locations. + +Most applications will create one ``Twig_Environment`` object on application +initialization and use that to load templates. In some cases it's however +useful to have multiple environments side by side, if different configurations +are in use. + +The simplest way to configure Twig to load templates for your application +looks roughly like this:: + + require_once '/path/to/lib/Twig/Autoloader.php'; + Twig_Autoloader::register(); + + $loader = new Twig_Loader_Filesystem('/path/to/templates'); + $twig = new Twig_Environment($loader, array( + 'cache' => '/path/to/compilation_cache', + )); + +This will create a template environment with the default settings and a loader +that looks up the templates in the ``/path/to/templates/`` folder. Different +loaders are available and you can also write your own if you want to load +templates from a database or other resources. + +.. caution:: + + Before Twig 0.9.3, the ``cache`` option did not exist, and the cache + directory was passed as a second argument of the loader. + +.. note:: + + Notice that the second argument of the environment is an array of options. + The ``cache`` option is a compilation cache directory, where Twig caches + the compiled templates to avoid the parsing phase for sub-sequent + requests. It is very different from the cache you might want to add for + the evaluated templates. For such a need, you can use any available PHP + cache library. + +To load a template from this environment you just have to call the +``loadTemplate()`` method which then returns a ``Twig_Template`` instance:: + + $template = $twig->loadTemplate('index.html'); + +To render the template with some variables, call the ``render()`` method:: + + echo $template->render(array('the' => 'variables', 'go' => 'here')); + +.. note:: + + The ``display()`` method is a shortcut to output the template directly. + +Environment Options +------------------- + +When creating a new ``Twig_Environment`` instance, you can pass an array of +options as the constructor second argument:: + + $twig = new Twig_Environment($loader, array('debug' => true)); + +The following options are available: + +* ``debug``: When set to ``true``, the generated templates have a + ``__toString()`` method that you can use to display the generated nodes + (default to ``false``). + +* ``charset``: The charset used by the templates (default to ``utf-8``). + +* ``base_template_class``: The base template class to use for generated + templates (default to ``Twig_Template``). + +* ``cache``: An absolute path where to store the compiled templates, or + ``false`` to disable caching (which is the default). + +* ``auto_reload``: When developing with Twig, it's useful to recompile the + template whenever the source code changes. If you don't provide a value for + the ``auto_reload`` option, it will be determined automatically based on the + ``debug`` value. + +* ``strict_variables`` (new in Twig 0.9.7): If set to ``false``, Twig will + silently ignore invalid variables (variables and or attributes/methods that + do not exist) and replace them with a ``null`` value. When set to ``true``, + Twig throws an exception instead (default to ``false``). + +* ``autoescape`` (new in Twig 0.9.10): If set to ``true``, auto-escaping will + be enabled by default for all templates (default to ``true``). + +* ``optimizations`` (new in Twig 0.9.10): A flag that indicates which + optimizations to apply (default to ``-1`` -- all optimizations are enabled; + set it to ``0`` to disable). + +.. caution:: + + Before Twig 0.9.3, the ``cache`` and ``auto_reload`` options did not + exist. They were passed as a second and third arguments of the filesystem + loader respectively. + +Loaders +------- + +.. caution:: + + This section describes the loaders as implemented in Twig version 0.9.4 + and above. + +Loaders are responsible for loading templates from a resource such as the file +system. + +Compilation Cache +~~~~~~~~~~~~~~~~~ + +All template loaders can cache the compiled templates on the filesystem for +future reuse. It speeds up Twig a lot as templates are only compiled once; and +the performance boost is even larger if you use a PHP accelerator such as APC. +See the ``cache`` and ``auto_reload`` options of ``Twig_Environment`` above +for more information. + +Built-in Loaders +~~~~~~~~~~~~~~~~ + +Here is a list of the built-in loaders Twig provides: + +* ``Twig_Loader_Filesystem``: Loads templates from the file system. This + loader can find templates in folders on the file system and is the preferred + way to load them:: + + $loader = new Twig_Loader_Filesystem($templateDir); + + It can also look for templates in an array of directories:: + + $loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2)); + + With such a configuration, Twig will first look for templates in + ``$templateDir1`` and if they do not exist, it will fallback to look for + them in the ``$templateDir2``. + +* ``Twig_Loader_String``: Loads templates from a string. It's a dummy loader + as you pass it the source code directly:: + + $loader = new Twig_Loader_String(); + +* ``Twig_Loader_Array``: Loads a template from a PHP array. It's passed an + array of strings bound to template names. This loader is useful for unit + testing:: + + $loader = new Twig_Loader_Array($templates); + +.. tip:: + + When using the ``Array`` or ``String`` loaders with a cache mechanism, you + should know that a new cache key is generated each time a template content + "changes" (the cache key being the source code of the template). If you + don't want to see your cache grows out of control, you need to take care + of clearing the old cache file by yourself. + +Create your own Loader +~~~~~~~~~~~~~~~~~~~~~~ + +All loaders implement the ``Twig_LoaderInterface``:: + + interface Twig_LoaderInterface + { + /** + * Gets the source code of a template, given its name. + * + * @param string $name string The name of the template to load + * + * @return string The template source code + */ + function getSource($name); + + /** + * Gets the cache key to use for the cache for a given template name. + * + * @param string $name string The name of the template to load + * + * @return string The cache key + */ + function getCacheKey($name); + + /** + * Returns true if the template is still fresh. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + */ + function isFresh($name, $time); + } + +As an example, here is how the built-in ``Twig_Loader_String`` reads:: + + class Twig_Loader_String implements Twig_LoaderInterface + { + public function getSource($name) + { + return $name; + } + + public function getCacheKey($name) + { + return $name; + } + + public function isFresh($name, $time) + { + return false; + } + } + +The ``isFresh()`` method must return ``true`` if the current cached template +is still fresh, given the last modification time, or ``false`` otherwise. + +Using Extensions +---------------- + +Twig extensions are packages that adds new features to Twig. Using an +extension is as simple as using the ``addExtension()`` method:: + + $twig->addExtension(new Twig_Extension_Sandbox()); + +Twig comes bundled with the following extensions: + +* *Twig_Extension_Core*: Defines all the core features of Twig and is + automatically registered when you create a new environment. + +* *Twig_Extension_Escaper*: Adds automatic output-escaping and the possibility + to escape/unescape blocks of code. + +* *Twig_Extension_Sandbox*: Adds a sandbox mode to the default Twig + environment, making it safe to evaluated untrusted code. + +* *Twig_Extension_Optimizer*: Optimizers the node tree before compilation (as + of Twig 0.9.10). + +The core, escaper, and optimizer extensions do not need to be added to the +Twig environment, as they are registered by default. You can disable an +already registered extension:: + + $twig->removeExtension('escaper'); + +Built-in Extensions +------------------- + +This section describes the features added by the built-in extensions. + +.. tip:: + + Read the chapter about extending Twig to learn how to create your own + extensions. + +Core Extension +~~~~~~~~~~~~~~ + +The ``core`` extension defines all the core features of Twig: + +* Tags: + + * ``for`` + * ``if`` + * ``extends`` + * ``include`` + * ``block`` + * ``parent`` + * ``display`` + * ``filter`` + * ``macro`` + * ``import`` + * ``set`` + +* Filters: + + * ``date`` + * ``format`` + * ``even`` + * ``odd`` + * ``urlencode`` + * ``title`` + * ``capitalize`` + * ``upper`` + * ``lower`` + * ``striptags`` + * ``join`` + * ``reverse`` + * ``length`` + * ``sort`` + * ``in`` + * ``range`` + * ``cycle`` + * ``default`` + * ``keys`` + * ``items`` + * ``escape`` + * ``e`` + +Escaper Extension +~~~~~~~~~~~~~~~~~ + +The ``escaper`` extension adds automatic output escaping to Twig. It defines a +new tag, ``autoescape``, and a new filter, ``raw``. + +When creating the escaper extension, you can switch on or off the global +output escaping strategy:: + + $escaper = new Twig_Extension_Escaper(true); + $twig->addExtension($escaper); + +If set to ``true``, all variables in templates are escaped, except those using +the ``raw`` filter: + +.. code-block:: jinja + + {{ article.to_html|raw }} + +You can also change the escaping mode locally by using the ``autoescape`` tag: + +.. code-block:: jinja + + {% autoescape on %} + {% var %} + {% var|raw %} {# var won't be escaped #} + {% var|escape %} {# var won't be doubled-escaped #} + {% endautoescape %} + +.. warning:: + + The ``autoescape`` tag has no effect on included files. + +The escaping rules are implemented as follows (it describes the behavior of +Twig 0.9.9 and above): + +* Literals (integers, booleans, arrays, ...) used in the template directly as + variables or filter arguments are never automatically escaped: + + .. code-block:: jinja + + {{ "Twig
    " }} {# won't be escaped #} + + {% set text = "Twig
    " %} + {{ text }} {# will be escaped #} + +* Expressions which the result is always a literal or a variable marked safe + are never automatically escaped: + + .. code-block:: jinja + + {{ foo ? "Twig
    " : "
    Twig" }} {# won't be escaped #} + + {% set text = "Twig
    " %} + {{ foo ? text : "
    Twig" }} {# will be escaped #} + + {% set text = "Twig
    " %} + {{ foo ? text|raw : "
    Twig" }} {# won't be escaped #} + + {% set text = "Twig
    " %} + {{ foo ? text|escape : "
    Twig" }} {# the result of the expression won't be escaped #} + +* Escaping is applied before printing, after any other filter is applied: + + .. code-block:: jinja + + {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #} + +* The `raw` filter should only be used at the end of the filter chain: + + .. code-block:: jinja + + {{ var|raw|upper }} {# will be escaped #} + + {{ var|upper|raw }} {# won't be escaped #} + +* Automatic escaping is not applied if the last filter in the chain is marked + safe for the current context (e.g. ``html`` or ``js``). ``escaper`` and + ``escaper('html')`` are marked safe for html, ``escaper('js')`` is marked + safe for javascript, ``raw`` is marked safe for everything. + + .. code-block:: jinja + + {% autoescape js on %} + {{ var|escape('html') }} {# will be escaped for html and javascript #} + {{ var }} {# will be escaped for javascript #} + {{ var|escape('js') }} {# won't be double-escaped #} + {% endautoescape %} + +Sandbox Extension +~~~~~~~~~~~~~~~~~ + +The ``sandbox`` extension can be used to evaluate untrusted code. Access to +unsafe attributes and methods is prohibited. The sandbox security is managed +by a policy instance. By default, Twig comes with one policy class: +``Twig_Sandbox_SecurityPolicy``. This class allows you to white-list some +tags, filters, properties, and methods:: + + $tags = array('if'); + $filters = array('upper'); + $methods = array( + 'Article' => array('getTitle', 'getBody'), + ); + $properties = array( + 'Article' => array('title', 'body'), + ); + $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties); + +With the previous configuration, the security policy will only allow usage of +the ``if`` tag, and the ``upper`` filter. Moreover, the templates will only be +able to call the ``getTitle()`` and ``getBody()`` methods on ``Article`` +objects, and the ``title`` and ``body`` public properties. Everything else +won't be allowed and will generate a ``Twig_Sandbox_SecurityError`` exception. + +The policy object is the first argument of the sandbox constructor:: + + $sandbox = new Twig_Extension_Sandbox($policy); + $twig->addExtension($sandbox); + +By default, the sandbox mode is disabled and should be enabled when including +untrusted template code by using the ``sandbox`` tag: + +.. code-block:: jinja + + {% sandbox %} + {% include 'user.html' %} + {% endsandbox %} + +You can sandbox all templates by passing ``true`` as the second argument of +the extension constructor:: + + $sandbox = new Twig_Extension_Sandbox($policy, true); + +Optimizer Extension (as of Twig 0.9.10) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``optimizer`` extension optimizes the node tree before compilation:: + + $twig->addExtension(new Twig_Extension_Optimizer()); + +By default, all optimizations are turned on. You can select the ones you want +to enable by passing them to the constructor:: + + $optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR); + + $twig->addExtension($optimizer); + +Exceptions +---------- + +Twig can throw exceptions: + +* ``Twig_Error``: The base exception for all errors. + +* ``Twig_SyntaxError``: Thrown to tell the user that there is a problem with + the template syntax. + +* ``Twig_RuntimeError``: Thrown when an error occurs at runtime (when a filter + does not exist for instance). + +* `wTwig_Sandbox_SecurityError``: Thrown when an unallowed tag, filter, or + method is called in a sandboxed template. diff --git a/doc/hacking.rst b/doc/hacking.rst new file mode 100644 index 0000000..0a82b6c --- /dev/null +++ b/doc/hacking.rst @@ -0,0 +1,184 @@ +Hacking Twig +============ + +Twig is very extensible and you can easily hack it. Keep in mind that you +should probably try to create an extension before hacking the core, as most +features and enhancements can be done with extensions. This chapter is also +useful for people who want to understand how Twig works under the hood. + +How Twig works? +--------------- + +The rendering of a Twig template can be summarized into four key steps: + +* **Load** the template: If the template is already compiled, load it and go + to the *evaluation* step, otherwise: + + * First, the **lexer** tokenizes the template source code into small pieces + for easier processing; + * Then, the **parser** converts the token stream into a meaningful tree + of nodes (the Abstract Syntax Tree); + * Eventually, the *compiler* transforms the AST into PHP code; + +* **Evaluate** the template: It basically means calling the ``display()`` + method of the compiled template and passing it the context. + +The Lexer +--------- + +The Twig lexer goal is to tokenize a source code into a token stream (each +token is of class ``Token``, and the stream is an instance of +``Twig_TokenStream``). The default lexer recognizes nine different token types: + +* ``Twig_Token::TEXT_TYPE`` +* ``Twig_Token::BLOCK_START_TYPE`` +* ``Twig_Token::VAR_START_TYPE`` +* ``Twig_Token::BLOCK_END_TYPE`` +* ``Twig_Token::VAR_END_TYPE`` +* ``Twig_Token::NAME_TYPE`` +* ``Twig_Token::NUMBER_TYPE`` +* ``Twig_Token::STRING_TYPE`` +* ``Twig_Token::OPERATOR_TYPE`` +* ``Twig_Token::EOF_TYPE`` + +You can manually convert a source code into a token stream by calling the +``tokenize()`` of an environment:: + + $stream = $twig->tokenize($source, $identifier); + +As the stream has a ``__toString()`` method, you can have a textual +representation of it by echoing the object:: + + echo $stream."\n"; + +Here is the output for the ``Hello {{ name }}`` template: + +.. code-block:: text + + TEXT_TYPE(Hello ) + VAR_START_TYPE() + NAME_TYPE(name) + VAR_END_TYPE() + EOF_TYPE() + +You can change the default lexer use by Twig (``Twig_Lexer``) by calling the +``setLexer()`` method:: + + $twig->setLexer($lexer); + +Lexer classes must implement the ``Twig_LexerInterface``:: + + interface Twig_LexerInterface + { + /** + * Tokenizes a source code. + * + * @param string $code The source code + * @param string $filename A unique identifier for the source code + * + * @return Twig_TokenStream A token stream instance + */ + function tokenize($code, $filename = 'n/a'); + } + +The Parser +---------- + +The parser converts the token stream into an AST (Abstract Syntax Tree), or a +node tree (of class ``Twig_Node_Module``). The core extension defines the +basic nodes like: ``for``, ``if``, ... and the expression nodes. + +You can manually convert a token stream into a node tree by calling the +``parse()`` method of an environment:: + + $nodes = $twig->parse($stream); + +Echoing the node object gives you a nice representation of the tree:: + + echo $nodes."\n"; + +Here is the output for the ``Hello {{ name }}`` template: + +.. code-block:: text + + Twig_Node_Module( + Twig_Node_Text(Hello ) + Twig_Node_Print( + Twig_Node_Expression_Name(name) + ) + ) + +The default parser (``Twig_TokenParser``) can be also changed by calling the +``setParser()`` method:: + + $twig->setParser($parser); + +All Twig parsers must implement the ``Twig_ParserInterface``:: + + interface Twig_ParserInterface + { + /** + * Converts a token stream to a node tree. + * + * @param Twig_TokenStream $stream A token stream instance + * + * @return Twig_Node_Module A node tree + */ + function parser(Twig_TokenStream $code); + } + +The Compiler +------------ + +The last step is done by the compiler. It takes a node tree as an input and +generates PHP code usable for runtime execution of the templates. The default +compiler generates PHP classes to ease the implementation of the template +inheritance feature. + +You can call the compiler by hand with the ``compile()`` method of an +environment:: + + $php = $twig->compile($nodes); + +The ``compile()`` method returns the PHP source code representing the node. + +The generated template for a ``Hello {{ name }}`` template reads as follows:: + + /* Hello {{ name }} */ + class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template + { + public function display($context) + { + $this->env->initRuntime(); + + // line 1 + echo "Hello "; + echo (isset($context['name']) ? $context['name'] : null); + } + } + +As for the lexer and the parser, the default compiler (``Twig_Compiler``) can +be changed by calling the ``setCompiler()`` method:: + + $twig->setCompiler($compiler); + +All Twig compilers must implement the ``Twig_CompilerInterface``:: + + interface Twig_CompilerInterface + { + /** + * Compiles a node. + * + * @param Twig_Node $node The node to compile + * + * @return Twig_Compiler The current compiler instance + */ + function compile(Twig_Node $node); + + /** + * Gets the current PHP code after compilation. + * + * @return string The PHP code + */ + function getSource(); + } diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..5f7ff12 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,12 @@ +Twig +==== + +.. toctree:: + :maxdepth: 2 + + intro + templates + api + advanced + hacking + recipes diff --git a/doc/intro.rst b/doc/intro.rst new file mode 100644 index 0000000..56116dc --- /dev/null +++ b/doc/intro.rst @@ -0,0 +1,99 @@ +Introduction +============ + +This is the documentation for Twig, the flexible, fast, and secure template +engine for PHP. + +If you have any exposure to other text-based template languages, such as +Smarty, Django, or Jinja, you should feel right at home with Twig. It's both +designer and developer friendly by sticking to PHP's principles and adding +functionality useful for templating environments. + +The key-features are... + +* *Fast*: Twig compiles templates down to plain optimized PHP code. The + overhead compared to regular PHP code was reduced to the very minimum. + +* *Secure*: Twig has a sandbox mode to evaluate untrusted template code. This + allows Twig to be used as a template language for applications where users + may modify the template design. + +* *Flexible*: Twig is powered by a flexible lexer and parser. This allows the + developer to define its own custom tags and filters, and create its own DSL. + +Prerequisites +------------- + +Twig needs at least **PHP 5.2.4** to run. + +Installation +------------ + +You have multiple ways to install Twig. If you are unsure what to do, go with +the tarball. + +From the tarball release +~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Download the most recent tarball from the `download page`_ +2. Unpack the tarball +3. Move the files somewhere in your project + +Installing the development version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Install Subversion or Git +2. For Subversion: ``svn co http://svn.twig-project.org/trunk/ twig``, for Git: + ``git clone git://github.com/fabpot/Twig.git`` + +Installing the PEAR package +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Install PEAR +2. ``pear channel-discover pear.twig-project.org`` +3. ``pear install twig/Twig`` (or ``pear install twig/Twig-beta``) + +Basic API Usage +--------------- + +This section gives you a brief introduction to the PHP API for Twig. + +The first step to use Twig is to register its autoloader:: + + require_once '/path/to/lib/Twig/Autoloader.php'; + Twig_Autoloader::register(); + +Replace the ``/path/to/lib/`` path with the path you used for Twig +installation. + +.. note:: + + Twig follows the PEAR convention names for its classes, which means you + can easily integrate Twig classes loading in your own autoloader. + +.. code-block:: php + + $loader = new Twig_Loader_String(); + $twig = new Twig_Environment($loader); + + $template = $twig->loadTemplate('Hello {{ name }}!'); + + $template->display(array('name' => 'Fabien')); + +Twig uses a loader (``Twig_Loader_String``) to locate templates, and an +environment (``Twig_Environment``) to store the configuration. + +The ``loadTemplate()`` method uses the loader to locate and load the template +and returns a template object (``Twig_Template``) which is suitable for +rendering with the ``display()`` method. + +Twig also comes with a filesystem loader:: + + $loader = new Twig_Loader_Filesystem('/path/to/templates'); + $twig = new Twig_Environment($loader, array( + 'cache' => '/path/to/compilation_cache', + )); + + $template = $twig->loadTemplate('index.html'); + +.. _`download page`: http://www.twig-project.org/installation diff --git a/doc/recipes.rst b/doc/recipes.rst new file mode 100644 index 0000000..12a0b38 --- /dev/null +++ b/doc/recipes.rst @@ -0,0 +1,172 @@ +Recipes +======= + +Making a Layout conditional +--------------------------- + +Working with Ajax means that the same content is sometimes displayed as is, +and sometimes decorated with a layout. As Twig layout template names can be +any valid expression, you can pass a variable that evaluates to ``true`` when +the request is made via Ajax and choose the layout accordingly: + +.. code-block:: jinja + + {% extends request.ajax ? "base_ajax.html" : "base.html" %} + + {% block content %} + This is the content to be displayed. + {% endblock %} + +Making an Include dynamic +------------------------- + +When including a template, its name does not need to be a string. For +instance, the name can depend on the value of a variable: + +.. code-block:: jinja + + {% include var ~ '_foo.html' %} + +If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be +rendered. + +As a matter of fact, the template name can be any valid expression, such as +the following: + +.. code-block:: jinja + + {% include var|default('index') ~ '_foo.html' %} + +Customizing the Syntax +---------------------- + +Twig allows some syntax customization for the block delimiters. It's not +recommended to use this feature as templates will be tied with your custom +syntax. But for specific projects, it can make sense to change the defaults. + +To change the block delimiters, you need to create your own lexer object:: + + $twig = new Twig_Environment(); + + $lexer = new Twig_Lexer($twig, array( + 'tag_comment' => array('{#', '#}'), + 'tag_block' => array('{%', '%}'), + 'tag_variable' => array('{{', '}}'), + )); + $twig->setLexer($lexer); + +Here are some configuration example that simulates some other template engines +syntax:: + + // Ruby erb syntax + $lexer = new Twig_Lexer($twig, array( + 'tag_comment' => array('<%#', '%>'), + 'tag_block' => array('<%', '%>'), + 'tag_variable' => array('<%=', '%>'), + )); + + // SGML Comment Syntax + $lexer = new Twig_Lexer($twig, array( + 'tag_comment' => array(''), + 'tag_block' => array(''), + 'tag_variable' => array('${', '}'), + )); + + // Smarty like + $lexer = new Twig_Lexer($twig, array( + 'tag_comment' => array('{*', '*}'), + 'tag_block' => array('{', '}'), + 'tag_variable' => array('{$', '}'), + )); + +Using dynamic Object Properties +------------------------------- + +When Twig encounters a variable like ``article.title``, it tries to find a +``title`` public property in the ``article`` object. + +It also works if the property does not exist but is rather defined dynamically +thanks to the magic ``__get()`` method; you just need to also implement the +``__isset()`` magic method like shown in the following snippet of code:: + + class Article + { + public function __get($name) + { + if ('title' == $name) + { + return 'The title'; + } + + // throw some kind of error + } + + public function __isset($name) + { + if ('title' == $name) + { + return true; + } + + return false; + } + } + +Accessing the parent Context in Nested Loops +-------------------------------------------- + +Sometimes, when using nested loops, you need to access the parent context. The +parent context is always accessible via the ``loop.parent`` variable. For +instance, if you have the following template data:: + + $data = array( + 'topics' => array( + 'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'), + 'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'), + ), + ); + +And the following template to display all messages in all topics: + +.. code-block:: jinja + + {% for topic, messages in topics %} + * {{ loop.index }}: {{ topic }} + {% for message in messages %} + - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }} + {% endfor %} + {% endfor %} + +The output will be similar to: + +.. code-block:: text + + * 1: topic1 + - 1.1: The message 1 of topic 1 + - 1.2: The message 2 of topic 1 + * 2: topic2 + - 2.1: The message 1 of topic 2 + - 2.2: The message 2 of topic 2 + +In the inner loop, the ``loop.parent`` variable is used to access the outer +context. So, the index of the current ``topic`` defined in the outer for loop +is accessible via the ``loop.parent.loop.index`` variable. + +Passing a Macro as an Argument +------------------------------ + +(new in Twig 0.9.6) + +By default, a macro directly outputs its content to the screen. If you want to +pass the content of a macro as an argument to a method or to another macro, +you can use the ``set`` tag: + +.. code-block:: jinja + + {% import "form_elements.html" as form %} + + {% set theinput %} + {{ form.input('test', 'text', 'Value') }} + {% endset %} + + {{ form.row('Label', theinput) }} diff --git a/doc/templates.rst b/doc/templates.rst new file mode 100644 index 0000000..58e31f9 --- /dev/null +++ b/doc/templates.rst @@ -0,0 +1,1408 @@ +Twig for Template Designers +=========================== + +This document describes the syntax and semantics of the template engine and +will be most useful as reference to those creating Twig templates. + +Synopsis +-------- + +A template is simply a text file. It can generate any text-based format (HTML, +XML, CSV, LaTeX, etc.). It doesn't have a specific extension, ``.html`` or +``.xml`` are just fine. + +A template contains **variables** or **expressions**, which get replaced with +values when the template is evaluated, and tags, which control the logic of +the template. + +Below is a minimal template that illustrates a few basics. We will cover the +details later in that document: + +.. code-block:: jinja + + + + + My Webpage + + + + +

    My Webpage

    + {{ a_variable }} + + + +There are two kinds of delimiters: ``{% ... %}`` and ``{{ ... }}``. The first +one is used to execute statements such as for-loops, the latter prints the +result of an expression to the template. + +IDEs Integration +---------------- + +Modern IDEs support syntax highlighting and auto-completion for a large range +of languages. As Twig syntax is quite similar to Jinja and Django templates, +IDEs that support these two Python templating systems should also support +Twig. + +If you use Textmate, you can use the `Jinja`_ bundle or the `Django`_ one. + +If you use Vim, you can use the `Jinja syntax plugin`_. + +Variables +--------- + +The application passes variables to the templates you can mess around in the +template. Variables may have attributes or elements on them you can access +too. How a variable looks like, heavily depends on the application providing +those. + +You can use a dot (``.``) to access attributes of a variable, alternative the +so-called "subscript" syntax (``[]``) can be used. The following lines do the +same: + +.. code-block:: jinja + + {{ foo.bar }} + {{ foo['bar'] }} + +.. note:: + + It's important to know that the curly braces are *not* part of the + variable but the print statement. If you access variables inside tags + don't put the braces around. + +If a variable or attribute does not exist you will get back a ``null`` value +(which can be tested with the ``none`` expression). + +.. sidebar:: Implementation + + For convenience sake ``foo.bar`` does the following things on the PHP + layer: + + * check if ``foo`` is an array and ``bar`` a valid element; + * if not, and if ``foo`` is an object, check that ``bar`` is a valid property; + * if not, and if ``foo`` is an object, check that ``bar`` is a valid method + (even if ``bar`` is the constructor - use ``__construct()`` instead); + * if not, and if ``foo`` is an object, check that ``getBar`` is a valid method; + * if not, and if ``foo`` is an object, check that ``isBar`` is a valid method (as of Twig 0.9.9); + * if not, return a ``null`` value. + + ``foo['bar']`` on the other hand works mostly the same with the a small + difference in the order: + + * check if ``foo`` is an array and ``bar`` a valid element; + * if not, return a ``null`` value. + + Using the alternative syntax is also useful to dynamically get attributes + from arrays: + + .. code-block:: jinja + + foo[bar] + +Twig always references the following variables: + +* ``_self``: references the current template (was ``self`` before 0.9.9); +* ``_context``: references the current context; +* ``_charset``: references the current charset (as of 0.9.9). + +Filters +------- + +Variables can by modified by **filters**. Filters are separated from the +variable by a pipe symbol (``|``) and may have optional arguments in +parentheses. Multiple filters can be chained. The output of one filter is +applied to the next. + +``{{ name|striptags|title }}`` for example will remove all HTML tags from the +``name`` and title-cases it. Filters that accept arguments have parentheses +around the arguments, like a function call. This example will join a list by +commas: ``{{ list|join(', ') }}``. + +The built-in filters section below describes all the built-in filters. + +Comments +-------- + +To comment-out part of a line in a template, use the comment syntax ``{# ... +#}``. This is useful to comment out parts of the template for debugging or to +add information for other template designers or yourself: + +.. code-block:: jinja + + {# note: disabled template because we no longer use this + {% for user in users %} + ... + {% endfor %} + #} + +Whitespace Control +------------------ + +The first newline after a template tag is removed automatically (like in PHP.) +Whitespace is not further modified by the template engine, so each whitespace +(spaces, tabs, newlines etc.) is returned unchanged. + +Use the ``spaceless`` tag to remove whitespace between HTML tags: + +.. code-block:: jinja + + {% spaceless %} +
    + foo +
    + {% endspaceless %} + + {# output will be
    foo
    #} + +Escaping +-------- + +It is sometimes desirable or even necessary to have Twig ignore parts it would +otherwise handle as variables or blocks. For example if the default syntax is +used and you want to use ``{{`` as raw string in the template and not start a +variable you have to use a trick. + +The easiest way is to output the variable delimiter (``{{``) by using a variable +expression: + +.. code-block:: jinja + + {{ '{{' }} + +For bigger sections it makes sense to mark a block ``raw``. For example to put +Twig syntax as example into a template you can use this snippet: + +.. code-block:: jinja + + {% raw %} + + {% endraw %} + +Template Inheritance +-------------------- + +The most powerful part of Twig is template 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. + +Sounds complicated but is very basic. It's easiest to understand it by +starting with an example. + +Base Template +~~~~~~~~~~~~~ + +This template, which we'll call ``base.html``, defines a simple HTML skeleton +document that you might use for a simple two-column page. It's the job of +"child" templates to fill the empty blocks with content: + +.. code-block:: jinja + + + + + {% block head %} + + {% block title %}{% endblock %} - My Webpage + {% endblock %} + + +
    {% block content %}{% endblock %}
    + + + + +In this example, the ``{% block %}`` tags define four blocks that child +templates can fill in. All the ``block`` tag does is to tell the template +engine that a child template may override those portions of the template. + +Child Template +~~~~~~~~~~~~~~ + +A child template might look like this: + +.. code-block:: jinja + + {% extends "base.html" %} + + {% block title %}Index{% endblock %} + {% block head %} + {% parent %} + + {% endblock %} + {% block content %} +

    Index

    +

    + Welcome on my awesome homepage. +

    + {% endblock %} + +The ``{% extends %}`` tag is the key here. It tells the template engine that +this template "extends" another template. When the template system evaluates +this template, first it locates the parent. The extends tag should be the +first tag in the template. + +The filename of the template depends on the template loader. For example the +``Twig_Loader_Filesystem`` allows you to access other templates by giving the +filename. You can access templates in subdirectories with a slash: + +.. code-block:: jinja + + {% extends "layout/default.html" %} + +But this behavior can depend on the application embedding Twig. Note that +since the child template doesn't define the ``footer`` block, the value from +the parent template is used instead. + +You can't define multiple ``{% block %}`` tags with the same name in the same +template. This limitation exists because a block tag works in "both" +directions. That is, a block tag doesn't just provide a hole to fill - it also +defines the content that fills the hole in the *parent*. If there were two +similarly-named ``{% block %}`` tags in a template, that template's parent +wouldn't know which one of the blocks' content to use. + +If you want to print a block multiple times you can however use the +``display`` tag: + +.. code-block:: jinja + + {% block title %}{% endblock %} +

    {% display title %}

    + {% block body %}{% endblock %} + +Like PHP, Twig does not support multiple inheritance. So you can only have one +extends tag called per rendering. + +Parent Blocks +~~~~~~~~~~~~~ + +It's possible to render the contents of the parent block by using the ``parent`` +tag. This gives back the results of the parent block: + +.. code-block:: jinja + + {% block sidebar %} +

    Table Of Contents

    + ... + {% parent %} + {% endblock %} + +Named Block End-Tags +~~~~~~~~~~~~~~~~~~~~ + +Twig allows you to put the name of the block after the end tag for better +readability: + +.. code-block:: jinja + + {% block sidebar %} + {% block inner_sidebar %} + ... + {% endblock inner_sidebar %} + {% endblock sidebar %} + +However the name after the ``endblock`` word must match the block name. + +Block Nesting and Scope +~~~~~~~~~~~~~~~~~~~~~~~ + +Blocks can be nested for more complex layouts. Per default, blocks have access +to variables from outer scopes: + +.. code-block:: jinja + + {% for item in seq %} +
  • {% block loop_item %}{{ item }}{% endblock %}
  • + {% endfor %} + +Block Shortcuts +~~~~~~~~~~~~~~~ + +For blocks with few content, it's possible to have a shortcut syntax. The +following constructs do the same: + +.. code-block:: jinja + + {% block title %} + {{ page_title|title }} + {% endblock %} + +.. code-block:: jinja + + {% block title page_title|title %} + +Dynamic Inheritance (as of Twig 0.9.7) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Twig supports dynamic inheritance by using a variable as the base template: + +.. code-block:: jinja + + {% extends some_var %} + +If the variable evaluates to a ``Twig_Template`` object, Twig will use it as +the parent template:: + + // {% extends layout %} + + $layout = $twig->loadTemplate('some_layout_template.twig'); + + $twig->display('template.twig', array('layout' => $layout)); + +Conditional Inheritance (as of Twig 0.9.7) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As a matter of fact, the template name can be any valid expression. So, it's +also possible to make the inheritance mechanism conditional: + +.. code-block:: jinja + + {% extends standalone ? "minimum.html" : "base.html" %} + +In this example, the template will extend the "minimum.html" layout template +if the ``standalone`` variable evaluates to ``true``, and "base.html" +otherwise. + +Import Context Behavior +----------------------- + +Per default included templates are passed the current context. + +The context that is passed to the included template includes variables defined +in the template: + +.. code-block:: jinja + + {% for box in boxes %} + {% include "render_box.html" %} + {% endfor %} + +The included template ``render_box.html`` is able to access ``box``. + +HTML Escaping +------------- + +When generating HTML from templates, there's always a risk that a variable +will include characters that affect the resulting HTML. There are two +approaches: manually escaping each variable or automatically escaping +everything by default. + +Twig supports both, automatic escaping is enabled by default. + +.. note:: + + Automatic escaping is only supported if the *escaper* extension has been + enabled (which is the default). + +Working with Manual Escaping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If manual escaping is enabled it's **your** responsibility to escape variables +if needed. What to escape? If you have a variable that *may* include any of +the following chars (``>``, ``<``, ``&``, or ``"``) you **have to** escape it unless +the variable contains well-formed and trusted HTML. Escaping works by piping +the variable through the ``|e`` filter: ``{{ user.username|e }}``. + +Working with Automatic Escaping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whether automatic escaping is enabled or not, you can mark a section of a +template to be escaped or not by using the ``autoescape`` tag: + +.. code-block:: jinja + + {% autoescape on %} + Everything will be automatically escaped in this block + {% endautoescape %} + + {% autoescape off %} + Everything will be outputed as is in this block + {% endautoescape %} + + {% autoescape on js %} + Everything will be automatically escaped in this block + using the js escaping strategy + {% endautoescape %} + +When automatic escaping is enabled everything is escaped by default except for +values explicitly marked as safe. Those can be marked in the template by using +the ``|raw`` filter. + +Functions returning template data (like macros and ``parent``) always return +safe markup. + +.. note:: + + Twig is smart enough to not escape an already escaped value by the + ``escape`` filter. + +.. note:: + + The chapter for developers give more information about when and how + automatic escaping is applied. + +List of Control Structures +-------------------------- + +A control structure refers to all those things that control the flow of a +program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as well as +things like blocks. Control structures appear inside ``{% ... %}`` blocks. + +For +~~~ + +Loop over each item in a sequence. For example, to display a list of users +provided in a variable called ``users``: + +.. code-block:: jinja + +

    Members

    + + +.. note:: + + A sequence can be either an array or an object implementing the + ``Iterator`` interface. + +If you do need to iterate over a sequence of numbers, you can use the ``..`` +operator (as of Twig 0.9.5): + +.. code-block:: jinja + + {% for i in 0..10 %} + * {{ i }} + {% endfor %} + +The above snippet of code would print all numbers from 0 to 10. + +It can be also useful with letters: + +.. code-block:: jinja + + {% for letter in 'a'..'z' %} + * {{ letter }} + {% endfor %} + +The ``..`` operator can take any expression at both sides: + +.. code-block:: jinja + + {% for letter in 'a'|upper..'z'|upper %} + * {{ letter }} + {% endfor %} + +If you need a step different from 1, you can use the ``range`` filter instead: + +.. code-block:: jinja + + {% for i in 0|range(10, 2) %} + * {{ i }} + {% endfor %} + +Inside of a ``for`` loop block you can access some special variables: + +===================== ============================================================= +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 first iteration +``loop.last`` True if last iteration +``loop.length`` The number of items in the sequence +``loop.parent`` The parent context +===================== ============================================================= + +.. note:: + + The ``loop.length``, ``loop.revindex``, ``loop.revindex0``, and + ``loop.last`` variables are only available for PHP arrays, or objects that + implement the ``Countable`` interface (as of Twig 0.9.7). + +.. note:: + + Unlike in PHP it's not possible to ``break`` or ``continue`` in a loop. + +If no iteration took place because the sequence was empty, you can render a +replacement block by using ``else``: + +.. code-block:: jinja + + + +By default, a loop iterates over the values of the sequence. You can iterate +on keys by using the ``keys`` filter: + +.. code-block:: jinja + +

    Members

    + + +You can also access both keys and values: + +.. code-block:: jinja + +

    Members

    + + +.. note:: + + On Twig before 0.9.3, you need to use the ``items`` filter to access both + the keys and values (``{% for key, value in users|items %}``). + +To conveniently display comma-separated lists or things alike, you can use +``joined by ", "`` at the end of the loop statement (as of Twig 0.9.10). Of +course this is not limited to commas: + +.. code-block:: jinja + +

    Members

    +

    {% for user in users joined by ", " %}{{ user }}{% endfor %}

    + + {# generates a string like

    Fabien, Jordi, Thomas, Lucas

    #} + +.. note:: + + This way you don't have to check if the item is the last one, the comma + will only be added in between two items. + +If +~~ + +The ``if`` statement in Twig is comparable with the if statements of PHP. In the +simplest form you can use it to test if a variable is defined, not empty or +not false: + +.. code-block:: jinja + + {% if users %} + + {% endif %} + +For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can use +more complex ``expressions`` there too: + +.. code-block:: jinja + + {% if kenny.sick %} + Kenny is sick. + {% elseif kenny.dead %} + You killed Kenny! You bastard!!! + {% else %} + Kenny looks okay --- so far + {% endif %} + +Macros +~~~~~~ + +Macros are comparable with functions in regular programming languages. They +are useful to put often used HTML idioms into reusable elements to not repeat +yourself. + +Here is a small example of a macro that renders a form element: + +.. code-block:: jinja + + {% macro input(name, value, type, size) %} + + {% endmacro %} + +Macros differs from native PHP functions in a few ways: + + * Default argument values are defined by using the ``default`` filter in the + macro body; + + * Arguments of a macro are always optional. + +But as PHP functions, macros don't have access to the current template +variables. + +.. tip:: + + You can pass the whole context as an argument by using the special + ``_context`` variable. + +Macros can be defined in any template, and need to be "imported" before being +used (see the Import section for more information): + +.. code-block:: jinja + + {% import "forms.html" as forms %} + +The above ``import`` call imports the "forms.html" file (which can contain only +macros, or a template and some macros), and import the functions as items of +the ``forms`` variable. + +The macro can then be called at will: + +.. code-block:: jinja + +

    {{ forms.input('username') }}

    +

    {{ forms.input('password', none, 'password') }}

    + +If macros are defined and used in the same template, you can use the +special ``_self`` variable, without importing them: + +.. code-block:: jinja + +

    {{ _self.input('username') }}

    + +When you want to use a macro in another one from the same file, use the ``_self`` +variable: + +.. code-block:: jinja + + {% macro input(name, value, type, size) %} + + {% endmacro %} + + {% macro wrapped_input(name, value, type, size) %} +
    + {{ _self.input(name, value, type, size) }} +
    + {% endmacro %} + +When the macro is defined in another file, you need to import it: + +.. code-block:: jinja + + {# forms.html #} + + {% macro input(name, value, type, size) %} + + {% endmacro %} + + {# shortcuts.html #} + + {% macro wrapped_input(name, value, type, size) %} + {% import "forms.html" as forms %} +
    + {{ forms.input(name, value, type, size) }} +
    + {% endmacro %} + +Filters +~~~~~~~ + +Filter sections allow you to apply regular Twig filters on a block of template +data. Just wrap the code in the special ``filter`` section: + +.. code-block:: jinja + + {% filter upper %} + This text becomes uppercase + {% endfilter %} + +You can also chain filters: + +.. code-block:: jinja + + {% filter lower|escape %} + SOME TEXT + {% endfilter %} + +It should returns ``<strong>some text</strong>``. + +Assignments +~~~~~~~~~~~ + +Inside code blocks you can also assign values to variables. Assignments use +the ``set`` tag and can have multiple targets: + +.. code-block:: jinja + + {% set foo = 'foo' %} + + {% set foo = [1, 2] %} + + {% set foo = {'foo': 'bar'} %} + + {% set foo = 'foo' ~ 'bar' %} + + {% set foo, bar = 'foo', 'bar' %} + +The ``set`` tag can also be used to 'capture' chunks of HTML (new in Twig +0.9.6): + +.. code-block:: jinja + + {% set foo %} + + {% endset %} + +Extends +~~~~~~~ + +The ``extends`` tag can be used to extend a template from another one. You can +have multiple of them in a file but only one of them may be executed at the +time. There is no support for multiple inheritance. See the section about +Template inheritance above for more information. + +Block +~~~~~ + +Blocks are used for inheritance and act as placeholders and replacements at +the same time. They are documented in detail as part of the section about +Template inheritance above. + +Include +~~~~~~~ + +The ``include`` statement is useful to include a template and return the +rendered content of that file into the current namespace: + +.. code-block:: jinja + + {% include 'header.html' %} + Body + {% include 'footer.html' %} + +Included templates have access to the variables of the active context. + +You can add additional variables by passing them after the ``with`` keyword: + +.. code-block:: jinja + + {# the foo template will have access to the variables from the current context and the foo one #} + {% include 'foo' with ['foo': 'bar'] %} + + {% set vars = {'foo': 'bar'} %} + {% include 'foo' with vars %} + +You can disable access to the context by appending the ``only`` keyword: + +.. code-block:: jinja + + {# only the foo variable will be accessible #} + {% include 'foo' with ['foo': 'bar'] only %} + +.. code-block:: jinja + + {# no variable will be accessible #} + {% include 'foo' only %} + +.. note:: + + The ``with`` keyword is supported as of Twig 0.9.5. The ``only`` keyword + is supported as of Twig 0.9.9. + +.. tip:: + + When including a template created by an end user, you should consider + sandboxing it. More information in the "Twig for Developers" chapter. + +The template name can be any valid Twig expression: + +.. code-block:: jinja + + {% include some_var %} + {% include ajax ? 'ajax.html' : 'not_ajax.html' %} + +And if the variable evaluates to a ``Twig_Template`` object, Twig will use it +directly:: + + // {% include template %} + + $template = $twig->loadTemplate('some_template.twig'); + + $twig->display('template.twig', array('template' => $template)); + +Import +~~~~~~ + +Twig supports putting often used code into macros. These macros can go into +different templates and get imported from there. + +Imagine we have a helper module that renders forms (called ``forms.html``): + +.. code-block:: jinja + + {% macro input(name, value, type, size) %} + + {% endmacro %} + + {% macro textarea(name, value, rows) %} + + {% endmacro %} + +Importing these macros in a template is as easy as using the ``import`` tag: + +.. code-block:: jinja + + {% import 'forms.html' as forms %} + +
    +
    Username
    +
    {{ forms.input('username') }}
    +
    Password
    +
    {{ forms.input('password', none, 'password') }}
    +
    +

    {{ forms.textarea('comment') }}

    + +Importing is not needed if the macros and the template are defined in the file; +use the special ``_self`` variable instead: + +.. code-block:: jinja + + {# index.html template #} + + {% macro textarea(name, value, rows) %} + + {% endmacro %} + +

    {{ _self.textarea('comment') }}

    + +But you can still create an alias by importing from the ``_self`` variable: + +.. code-block:: jinja + + {# index.html template #} + + {% macro textarea(name, value, rows) %} + + {% endmacro %} + + {% import _self as forms %} + +

    {{ forms.textarea('comment') }}

    + +Expressions +----------- + +Twig allows basic expressions everywhere. These work very similar to regular +PHP and even if you're not working with PHP you should feel comfortable with +it. + +The operator precedence is as follows, with the lowest-precedence operators +listed first: ``or``, ``and``, ``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``+``, ``-``, +``~``, ``*``, ``/``, ``%``, ``//``, ``is``, ``..``, and ``**``. + +.. caution:: + + When compiling deep-nested arrays or math expressions with Xdebug enabled, + Twig can easily reach the default maximum nesting level set by Xdebug via + the ``xdebug.max_nesting_level`` setting; changing the default (100) to a + bigger value solves the issue. + +Literals +~~~~~~~~ + +The simplest form of expressions are literals. Literals are representations +for PHP types such as strings, numbers, and arrays. The following literals +exist: + +* ``"Hello World"``: Everything between two double or single quotes is a + string. They are useful whenever you need a string in the template (for + example as arguments to function calls, filters or just to extend or + include a template). + +* ``42`` / ``42.23``: Integers and floating point numbers are created by just + writing the number down. If a dot is present the number is a float, + otherwise an integer. + +* ``["foo", "bar"]`` (new in Twig 0.9.5): Arrays are defined by a sequence of + expressions separated by a comma (``,``) and wrapped with squared brackets + (``[]``). + +* ``{"foo": "bar"}`` (new in Twig 0.9.10): Hashes are defined by a list of keys + and values separated by a comma (``,``) and wrapped with curly braces (``{}``). + A value can be any valid expression. + +* ``true`` / ``false``: ``true`` represents the true value, ``false`` + represents the false value. + +* ``none``: ``none`` represents no specific value (the equivalent of ``null`` in + PHP). This is the value returned when a variable does not exist. + +Arrays and hashes can be nested: + +.. code-block:: jinja + + {% set foo = [1, {"foo": "bar"}] %} + +Math +~~~~ + +Twig allows you to calculate with values. This is rarely useful in templates +but exists for completeness' sake. The following operators are supported: + +* ``+``: Adds two objects together (the operands are casted to numbers). ``{{ + 1 + 1 }}`` is ``2``. + +* ``-``: Substracts the second number from the first one. ``{{ 3 - 2 }}`` is + ``1``. + +* ``/``: Divides two numbers. The return value will be a floating point + number. ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``. + +* ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is + ``4``. + +* ``//``: Divides two numbers and returns the truncated integer result. ``{{ + 20 // 7 }}`` is ``2``. + +* ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would + return ``4``. + +* ``**``: Raises the left operand to the power of the right operand. ``{{ 2**3 + }}`` would return ``8``. + +Logic +~~~~~ + +For ``if`` statements, ``for`` filtering or ``if`` expressions it can be useful to +combine multiple expressions: + +* ``and``: Returns true if the left and the right operands are both true. + +* ``or``: Returns true if the left or the right operand is true. + +* ``not``: Negates a statement. + +* ``(expr)``: Groups an expression. + +Comparisons +~~~~~~~~~~~ + +The following comparison operators are supported in any expression: ``==``, +``!=``, ``<``, ``>``, ``>=``, and ``<=``. + +Containment Operator (new in Twig 0.9.5) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``in`` operator performs containment test. + +It returns ``true`` if the left operand is contained in the right: + +.. code-block:: jinja + + {# returns true #} + + {{ 1 in [1, 2, 3] }} + + {{ 'cd' in 'abcde' }} + +.. tip:: + + You can use this filter to perform a containment test on strings, arrays, + or objects implementing the ``Traversable`` interface. + +To perform a negative test, use the ``not in`` operator: + +.. code-block:: jinja + + {% if 1 not in [1, 2, 3] %} + + {# is equivalent to #} + {% if not (1 in [1, 2, 3]) %} + +Tests (new in Twig 0.9.9) +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``is`` operator performs tests. Tests can be used to test a variable against +a common expression. The right operand is name of the test: + +.. code-block:: jinja + + {# find out if a variable is odd #} + + {{ name is odd }} + +Tests can accept arguments too: + +.. code-block:: jinja + + {% if loop.index is divisibleby(3) %} + +Tests can be negated by using the ``not in`` operator: + +.. code-block:: jinja + + {% if loop.index is not divisibleby(3) %} + + {# is equivalent to #} + {% if not (loop.index is divisibleby(3)) %} + +The built-in tests section below describes all the built-in tests. + +Other Operators +~~~~~~~~~~~~~~~ + +The following operators are very useful but don't fit into any of the other +two categories: + +* ``..`` (new in Twig 0.9.5): Creates a sequence based on the operand before + and after the operator (see the ``for`` tag for some usage examples). + +* ``|``: Applies a filter. + +* ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello + " ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello + John!``. + +* ``.``, ``[]``: Gets an attribute of an object. + +* ``?:``: Twig supports the PHP ternary operator: + + .. code-block:: jinja + + {{ foo ? 'yes' : 'no' }} + +List of built-in Filters +------------------------ + +``date`` +~~~~~~~~ + +The ``date`` filter is able to format a date to a given format: + +.. code-block:: jinja + + {{ post.published_at|date("m/d/Y") }} + +The ``date`` filter accepts any date format supported by `DateTime`_ and +``DateTime`` instances. For instance, to display the current date, filter the +word "now": + +.. code-block:: jinja + + {{ "now"|date("m/d/Y") }} + +``format`` +~~~~~~~~~~ + +The ``format`` filter formats a given string by replacing the placeholders +(placeholders follows the ``printf`` notation): + +.. code-block:: jinja + + {{ "I like %s and %s."|format(foo, "bar") }} + + {# returns I like foo and bar. (if the foo parameter equals to the foo string) #} + +``replace`` (new in Twig 0.9.9) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``replace`` filter formats a given string by replacing the placeholders +(placeholders are free-form): + +.. code-block:: jinja + + {{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }} + + {# returns I like foo and bar. (if the foo parameter equals to the foo string) #} + +``cycle`` +~~~~~~~~~ + +The ``cycle`` filter can be used to cycle between an array of values: + +.. code-block:: jinja + + {% for i in 0..10 %} + {{ ['odd', 'even']|cycle(i) }} + {% endfor %} + +The array can contain any number of values: + +.. code-block:: jinja + + {% set fruits = ['apple', 'orange', 'citrus'] %} + + {% for i in 0..10 %} + {{ fruits|cycle(i) }} + {% endfor %} + +``url_encode`` +~~~~~~~~~~~~~~ + +The ``url_encode`` filter URL encodes a given string. + +``json_encode`` +~~~~~~~~~~~~~~~ + +The ``json_encode`` filter returns the JSON representation of a string. + +``title`` +~~~~~~~~~ + +The ``title`` filter returns a titlecased version of the value. I.e. words will +start with uppercase letters, all remaining characters are lowercase. + +``capitalize`` +~~~~~~~~~~~~~~ + +The ``capitalize`` filter capitalizes a value. The first character will be +uppercase, all others lowercase. + +``upper`` +~~~~~~~~~ + +The ``upper`` filter converts a value to uppercase. + +``lower`` +~~~~~~~~~ + +The ``lower`` filter converts a value to lowercase. + +``striptags`` +~~~~~~~~~~~~~ + +The ``striptags`` filter strips SGML/XML tags and replace adjacent whitespace by +one space. + +``join`` +~~~~~~~~ + +The ``join`` filter returns a string which is the concatenation of the strings +in the sequence. The separator between elements is an empty string per +default, you can define it with the optional parameter: + +.. code-block:: jinja + + {{ [1, 2, 3]|join('|') }} + {# returns 1|2|3 #} + + {{ [1, 2, 3]|join }} + {# returns 123 #} + +``reverse`` +~~~~~~~~~~~ + +The ``reverse`` filter reverses an array or an object if it implements the +``Iterator`` interface. + +``length`` +~~~~~~~~~~ + +The ``length`` filters returns the number of items of a sequence or mapping, or +the length of a string. + +``sort`` +~~~~~~~~ + +The ``sort`` filter sorts an array. + +``range`` (new in Twig 0.9.5) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Returns a list containing a sequence of numbers. The left side of the filter +represents the low value. The first argument of the filter is mandatory and +represents the high value. The second argument is optional and represents the +step (which defaults to ``1``). + +If you do need to iterate over a sequence of numbers: + +.. code-block:: jinja + + {% for i in 0|range(10) %} + * {{ i }} + {% endfor %} + +.. tip:: + + The ``range`` filter works as the native PHP ``range`` function. + +The ``..`` operator (see above) is a syntactic sugar for the ``range`` filter +(with a step of 1): + +.. code-block:: jinja + + {% for i in 0|range(10) %} + * {{ i }} + {% endfor %} + + {# is equivalent to #} + + {% for i in 0..10 %} + * {{ i }} + {% endfor %} + +``default`` +~~~~~~~~~~~ + +The ``default`` filter returns the passed default value if the value is +undefined, otherwise the value of the variable: + +.. code-block:: jinja + + {{ my_variable|default('my_variable is not defined') }} + +``keys`` +~~~~~~~~ + +The ``keys`` filter returns the keys of an array. It is useful when you want to +iterate over the keys of an array: + +.. code-block:: jinja + + {% for key in array|keys %} + ... + {% endfor %} + +``escape``, ``e`` +~~~~~~~~~~~~~~~~~ + +The ``escape`` filter converts the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in +strings to HTML-safe sequences. Use this if you need to display text that +might contain such characters in HTML. + +.. note:: + + Internally, ``escape`` uses the PHP ``htmlspecialchars`` function. + +``raw`` +~~~~~~~ + +The ``raw`` filter marks the value as safe which means that in an environment +with automatic escaping enabled this variable will not be escaped if ``raw`` is +the last filter applied to it. + +.. code-block:: jinja + + {% autoescape on } + {{ var|raw }} {# var won't be escaped #} + {% autoescape off %} + +``constant`` (new in Twig 0.9.9) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``constant`` returns the constant value for a given string: + +.. code-block:: jinja + + {{ some_date|date('DATE_W3C'|constant) }} + +List of built-in Tests (new in Twig 0.9.9) +------------------------------------------ + +``divisibleby`` +~~~~~~~~~~~~~~~ + +``divisibleby`` checks if a variable is divisible by a number: + +.. code-block:: jinja + + {% if loop.index is divisibleby(3) %} + +``none`` +~~~~~~~~ + +``none`` returns ``true`` if the variable is ``none``: + +.. code-block:: jinja + + {{ var is none }} + +``even`` +~~~~~~~~ + +``even`` returns ``true`` if the given number is even: + +.. code-block:: jinja + + {{ var is even }} + +``odd`` +~~~~~~~ + +``odd`` returns ``true`` if the given number is odd: + +.. code-block:: jinja + + {{ var is odd }} + +``sameas`` +~~~~~~~~~~ + +``sameas`` checks if a variable points to the same memory address than another +variable: + +.. code-block:: jinja + + {% if foo.attribute is sameas(false) %} + the foo attribute really is the ``false`` PHP value + {% endif %} + +``constant`` +~~~~~~~~~~~~ + +``constant`` checks if a variable has the exact same value as a constant. You +can use either global constants or class constants: + +.. code-block:: jinja + + {% if post.status is constant('Post::PUBLISHED') %} + the status attribute is exactly the same as Post::PUBLISHED + {% endif %} + +``defined`` +~~~~~~~~~~~ + +``defined`` checks if a variable is defined in the current context. This is very +useful if you use the ``strict_variables`` option: + +.. code-block:: jinja + + {# defined works with variable names #} + {% if foo is defined %} + ... + {% endif %} + + {# and attributes on variables names #} + {% if foo.bar is defined %} + ... + {% endif %} + +Extensions +---------- + +Twig can be easily extended. If you are looking for new tags or filters, have +a look at the Twig official extension repository: +http://github.com/fabpot/Twig-extensions. + +.. _`Jinja`: http://jinja.pocoo.org/2/documentation/integration +.. _`Django`: http://code.djangoproject.com/wiki/TextMate +.. _`Jinja syntax plugin`: http://jinja.pocoo.org/2/documentation/integration +.. _`DateTime`: http://www.php.net/manual/en/datetime.construct.php -- 1.7.2.5