# 面向模板設計師
---
本文檔介紹模板引擎的語法和語義,這是創建Twig模板時最有用的參考。
##概要
模板實際就是一個簡單的文本文件。它可以生成任意基于文本的格式(HTML,
XML, CSV, LaTeX, 等等)。它沒有特定的擴展,``.html``或``.xml``都行。
模板包含變量**variables**或表達式**expressions**,在評估模板時,這些帶值的變量或表達式會被替換;另外,還有控制模板邏輯的標簽**tags**。
下面是一個非常簡單的模板,它闡述了一些基礎知識。稍后我們將進一步討論。
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
<h1>My Webpage</h1>
{{ a_variable }}
</body>
</html>
有兩種形式的分隔符:``{% ... %}`` 和 ``{{ ... }}``。前者用于執行語句,例如for循環;后者將表達式的結果輸出到模板中。
##IDE 集成
很多IDE都支持Twig的語法高亮和自動完成。
* *Textmate* via the `Twig bundle`_
* *Vim* via the `Jinja syntax plugin`_ or the `vim-twig plugin`_
* *Netbeans* via the `Twig syntax plugin`_ (until 7.1, native as of 7.2)
* *PhpStorm* (native as of 2.1)
* *Eclipse* via the `Twig plugin`_
* *Sublime Text* via the `Twig bundle`_
* *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects)
* *Coda* and *SubEthaEdit* via the `Twig syntax mode`_
* *Coda 2* via the `other Twig syntax mode`_
* *Komodo* and *Komodo Edit* via the Twig highlight/syntax check mode
* *Notepad++* via the `Notepad++ Twig Highlighter`_
* *Emacs* via `web-mode.el`_
* *Atom* via the `PHP-twig for atom`_
另外,`TwigFiddle`是一款在線服務,它允許你在瀏覽器中執行Twig模板;它支持Twig的所有版本。
##變量
---------
應用程序將變量傳入模板中進行處理。變量可以包含你能訪問的屬性或元素。變量的可視化表現形式很大程度上取決于提供變量的應用程序。
你可以使用``.``來訪問變量的屬性(方法或PHP對象的屬性,或PHP數組單元);也可以使用所謂的"subscript"語法``[]``:
{{ foo.bar }}
{{ foo['bar'] }}
當屬性包含特殊字符時(比如``-``,將被解析為減號操作符),使用``attribute``函數訪問變量屬性:
{# equivalent to the non-working foo.data-foo #}
{{ attribute(foo, 'data-foo') }}
注意:
>你務必知道花括號**并不**是變量的一部分,它只是一個打印聲明。在訪問標簽內部的變量時,不要將其放在花括號中。
如果變量或屬性不存在,當``strict_variables``被設置為``false``時,你將接收一個``null``值。另外,如果``strict_variables``被設置了,Twig將拋出一個錯誤(查看 環境選項)。
### 實現
為方便起見 ``foo.bar``在PHP層做了下面這些工作:
> * 檢查``foo``是不是一個數組,``bar``是不是一個有效元素;
> * 如果不是,如果 ``foo`` 是一個對象,檢查``bar``是不是有效的屬性(property)。
> * 如果不是,如果 ``foo`` 是一個對象,檢查``bar``是不是有效的方法。(即使``bar``是構造函數 - 使用 ``__construct()`` 替代它)
> * 如果不是,如果 ``foo`` 是一個對象,檢查``getBar``是不是有效的方法。
> * 如果不是,如果 ``foo`` 是一個對象,檢查``isBar``是不是有效的方法。
> * 如果不是,即返回一個 ``null`` 值。
> ``foo['bar']``在另一方面只適用于PHP數組: on the other hand only works with PHP arrays:
> * 檢查 ``foo`` 是不是一個數字,并檢查``bar``是不是一個有效元素;
> * 如果不是,即返回一個 ``null`` 值。
注意:
> 如果你想訪問變量的動態屬性,使用attribute函數替代。
###全局變量
以下變量在模板中始終可用:
* ``_self``: 引用當前模板;
* ``_context``: 引用當前上下文;
* ``_charset``: 引用當前字符集;
###設置變量
你可以在代碼塊內為變量分配值。這里用到了 set 標簽:
{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}
##過濾器
可以通過過濾器**filters**來修改變量。過濾器中,用``|``分隔多個變量,還可以在括號中加入可選參數。可以鏈接多個過濾器。一個過濾器的輸出結果將用于下一個過濾器中。
下面的例子會刪除所有帶有``name``和title-cases的HTML標簽:
{{ name|striptags|title }}
過濾器接收由圓括號包裹的參數。這個例子中,加入了一個由逗號分隔的參數列表:
{{ list|join(', ') }}
要在一段代碼中應用過濾器,需要將它包裹在filter標簽中:
{% filter upper %}
This text becomes uppercase
{% endfilter %}
訪問filters頁面,了解更多內置過濾器。
##函數
函數可被調用,用于生產內容。函數通過函數名被調用,其后緊跟圓括號(``()``),它還可以設置參數。
舉個例子,``range``返回一個包含整數等差數列的列表:
{% for i in range(0, 3) %}
{{ i }},
{% endfor %}
訪問functions頁面,了解更多的內置函數。
##具名實參
> Twig 1.12 新加入的具名實參支持。
{% for i in range(low=1, high=10, step=2) %}
{{ i }},
{% endfor %}
使用具名實參,使模板中作為參數被傳遞的值更加清晰。
{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
{# versus #}
{{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}
具名實參同樣允許你跳過某些不需要改變默認值的參數:
{# 第一個參數是日期格式,如果傳遞的是空值,它將是默認的全局日期格式。 #}
{{ "now"|date(null, "Europe/Paris") }}
{# 或者,通過為時區使用一個具名實參來跳過格式值。#}
{{ "now"|date(timezone="Europe/Paris") }}
你還可以在一次調用中,同時使用位置參數和具名實參,此時,位置參數必須放在具名實參前面:
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
提示:
> 每個函數和過濾器的文檔頁面都有一節,列出支持的所有參數。
##控制結構
控制結構是指控制程序流程的所有東西——條件語句(例如 ``if``/``elseif``/``else``),``for``循環,以及程序塊等等。控制結構出現在 ``{% ... %}``塊中。
例如,要顯示一個被調用的``user``變量中提供的用戶(users)列表,使用for標簽:
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
if 標簽可以用來測試表達式:
{% if users|length > 0 %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}
前往tags頁面,了解更多內置的標簽。
##注釋
要在模板中注釋某一行,使用注釋語法``{# ...#}``。這常用于調試或者為自己或其他模板設計師添加信息。
{# note: disabled template because we no longer use this
{% for user in users %}
...
{% endfor %}
#}
##引入其他模板
Twig提供的 include 函數使你更方便地在模板中引入模板,并將該模板已渲染后的內容返回到當前模板:
{{ include('sidebar.html') }}
默認地,被引入的模板可以使用當前模板的語境(context)。這意味著,在主模板中定義的任意變量,在被引入的模板中同樣可用。
{% for box in boxes %}
{{ include('render_box.html') }}
{% endfor %}
被引入的模板``render_box.html``可以使用``box``變量。
模板的文件名,卻決于模板加載器。舉個例子:``Twig_Loader_Filesystem``允許你通過給定文件名稱訪問其他模板。你可以使用斜線來訪問子目錄內的模板:
{{ include('sections/articles/sidebar.html') }}
這個行為取決于內嵌Twig的應用程序。
##模板繼承
模板集成是Twig最強大之處。模板繼承允許你構建一個包含你網站中所有通用元素的基礎的"skeleton"模板,并定義子模版可以覆寫的塊**blocks**。
聽起來很復雜,但實際上很簡單。以一個例子來說,會更容易明白點。
現在,我們來定義一個基礎的模板,``base.html``,它定義了一個簡單的HTML skeleton文檔,你可以在一個簡單兩欄頁面中使用:
<!DOCTYPE html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2011 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
</html>
在這個例子中,block標簽定義了4個塊(block),可以由子模版進行填充。對于模板引擎來說,所有的``block``標簽都可以由子模版來覆寫該部分。
子模版大概是這個樣子的:
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ parent() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}
其中的`extends`標簽是關鍵所在。它告訴模板引擎當前模板擴展自另一個模板。當模板系統評估這個模板時,首先會定位到父模板。注意:extends標簽必須是模板的第一個標簽。
注意,由于子模版未定義``footer``塊,就用來自父模板的值替代使用了。
可以通過使用parents函數來渲染父級塊。它將返回父級塊的結果:
{% block sidebar %}
<h3>Table Of Contents</h3>
...
{{ parent() }}
{% endblock %}
提示:
> 在extends標簽的文檔頁面,還有更多的高級特性介紹,例如塊嵌套、范圍、動態繼承和條件繼承。
注意:
> Twig 在use標簽的幫助下,還能支持多重繼承和所謂的橫向重用(horizontal reuse)。這是一個幾乎不會在常規模板中用到的高級特性。
##HTML 轉義
當由模板生成HTML時,總會存在一個風險,包含字符的變量會影響最終生成的HTML。有兩種辦法來處理:手動地轉義每個變量,或者默認自動轉義所有。
Twig兩者都支持,自動轉義是默認啟用的。
記住:
> 只有在*escaper*擴展被啟用的情況下,自動轉義才被支持(實際上這個擴展默認就是啟用的)。
###使用手動轉義
如果選擇了手動轉義,那么轉義所有需要的變量就是**你的**職責了。哪些變量需要轉義呢?反正任何變量都不要相信。
轉義變量,通過使用`escape`或``e``過濾器進行:
{{ user.username|e }}
默認地,``escape``過濾器使用``html``策略,但取決于轉義的上下文(context),你可能需要顯式地使用其他的可用策略:
{{ user.username|e('js') }}
{{ user.username|e('css') }}
{{ user.username|e('url') }}
{{ user.username|e('html_attr') }}
###使用自動轉義
不論是否啟用了自動轉義,你都可以在模板中,使用autoescape標簽來標記某一部分是否已被轉義:
{% autoescape %}
本塊內所有東西都會被以HTML策略自動轉義掉
{% endautoescape %}
默認地,自動轉義使用``html``轉義策略。如果你在其他語境中輸出變量,你必須使用合適的轉義策略顯示地進行轉義:
{% autoescape 'js' %}
Everything will be automatically escaped in this block (using the JS strategy)
{% endautoescape %}
##轉義
忽略Twig模板的某一部分,有時是可取的,甚至必要的,被忽略的部分或作為變量或者代碼塊處理。比如,使用默認的語法時,你想要在模板中以原生字符串的形式使用``{{``,而不是以變量的開頭定界符來使用,此時便存在一個風險。
最簡單的辦法就是使用變量表達式來輸出變量定界符(``{{``) :
{{ '{{' }}
對于較大的段落,它也能一字不差的處理。參考`verbatim`標簽。
##宏指令
> Twig 1.12新增的。支持默認的參數值。
宏指令堪比常規程序語言中函數。使用宏指令可以重用常用的HTML片段,而不用再自己手動復制了。
宏指令通過`macro`標簽進行定義。這里有一個渲染表單元素的宏指令小例子(在后文中,我們稱之為``forms.html``):
{% macro input(name, value, type, size) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
宏指令可在任意模版中定義,在使用前,必須通過import標簽引入(import):
{% import "forms.html" as forms %}
<p>{{ forms.input('username') }}</p>
或者,你可以通過使用from標簽從一個模版中引入個別宏指令名到當前命名空間中,還可以以別名的形式使用它們:
{% from 'forms.html' import input as input_field %}
<dl>
<dt>Username</dt>
<dd>{{ input_field('username') }}</dd>
<dt>Password</dt>
<dd>{{ input_field('password', '', 'password') }}</dd>
</dl>
當沒有在宏調用中為宏指令參數提供默認值時,可以為它定義一個。:
{% macro input(name, value = "", type = "text", size = 20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}
如果額外的位置參數被傳遞給了宏調用,這些參數最終會作為值的列表存放在指定的``varargs``變量中。
##表達式
Twig 允許在任意位置使用表達式。表達式非常類似常規的 PHP,甚至你不需要用到PHP,你會感到非常舒適。
注意:
> 運算符優先級如下所示,從最低優先級開始:``b-and``, ``b-xor``, ``b-or``, ``or``, ``and``,``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``matches``,``starts with``, ``ends with``, ``..``, ``+``, ``-``, ``~``, ``*``, ``/``,``//``, ``%``, ``is``, ``**``, ``|``, ``[]``, 以及 ``.``:
> ~~~
> {% set greeting = 'Hello ' %}
> {% set name = 'Fabien' %}
> {{ greeting ~ name|lower }} {# Hello fabien #}
> {# use parenthesis to change precedence #}
> {{ (greeting ~ name)|lower }} {# hello fabien #}
> ~~~
###字面值(Literals)
> 在 Twig 1.5中加入的,支持使用散列健作為名稱和表達式。
表達式的最簡單形式就是字面值。字面值是對PHP類型的陳述,比如字符串、數字、以及數組。以下字面值都是存在的
* ``"Hello World"``:在雙引號或單引號中的任何內容都是一個字符串。無論何時,如果你要在模板中用到字符串,它都能為你帶來幫助(比如作為函數調用的參數,過濾器,擴展或引入模版)。字符串可以包含由反斜線(``\``)開頭的定界符——例如``'It\'s good'``。如果字符串包含了反斜線(e.g. ``'c:\Program Files'``),像這樣轉義它``'c:\\Program Files'``,用兩個反斜線。
* ``42`` / ``42.23``: 整型數和浮點數只需寫下它們即可創建。如果一個點表示該數字是浮點數,那么沒有這個點即是一個整型數。
* ``["foo", "bar"]``: 字符串,由一對方括號``[]``包裹的由逗號``,``分隔的表達式序列組成。
* ``{"foo": "bar"}``: 散列,由一對花括號``{}``包裹的以逗號分隔的鍵值對列表構成。
~~~
{# keys as string #}
{ 'foo': 'foo', 'bar': 'bar' }
{# keys as names (equivalent to the previous hash) -- as of Twig 1.5 #}
{ foo: 'foo', bar: 'bar' }
{# keys as integer #}
{ 2: 'foo', 4: 'bar' }
{# keys as expressions (the expression must be enclosed into parentheses) -- as of Twig 1.5 #}
{ (1 + 1): 'foo', (a ~ 'b'): 'bar' }
~~~
* ``true`` / ``false``: ``true`` 表示正確的值,``false``表示錯誤的值。
* ``null``: ``null`` 表示沒有具體的值。這是在變量沒有值時返回的結果。``none``是``null``的別名。
數組和散列可以嵌套:
{% set foo = [1, {"foo": "bar"}] %}
提示:
> 使用單引號或雙引號字符串在性能上沒有區別,但字符串插值只被雙引號字符串支持。
###數學運算(Math)
Twig允許值計算。這很少用在模版中,但由于完整性的緣故而存在。Twig支持以下運算符:
* ``+``: 加。Adds two objects together (the operands are casted to numbers). ``{{1 + 1 }}`` is ``2``.
* ``-``: 減。Subtracts the second number from the first one. ``{{ 3 - 2 }}`` is
``1``.
* ``/``: 除。Divides two numbers. The returned 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 floored integer result. ``{{ 20
// 7 }}`` is ``2``, ``{{ -20 // 7 }}`` is ``-3`` (this is just syntactic
sugar for the :doc:`round<filters/round>` filter).
* ``*``: 乘。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``.
###邏輯
你可以使用以下操作符來組合多個表達式:
* ``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)``: 聚合表達式?
注意:
> Twig 還支持位操作符(``b-and``, ``b-xor``, 和 ``b-or``).
提示:
> 運算操作符是大小寫敏感的。
###比較運算符
以下比較運算符可以用于任意表達式中:``==``,
``!=``, ``<``, ``>``, ``>=``, 和 ``<=``。
你還可以檢查字符串是否由另一個字符串開頭``starts with``或結尾``ends with``:
{% if 'Fabien' starts with 'F' %}
{% endif %}
{% if 'Fabien' ends with 'n' %}
{% endif %}
注意:
> 對于復雜的字符串比較,``matches``匹配操作符允許使用正則表達式:
> ~~~
> {% if phone matches '/^[\\d\\.]+$/' %}
> {% endif %}
> ~~~
###包含操作符
包含操作符 ``in`` 用于測試是否存在包含關系。
如果左側運算對象包含在右側運算對象中,則返回 ``true``:
{# returns true #}
{{ 1 in [1, 2, 3] }}
{{ 'cd' in 'abcde' }}
提示:
> 你可以使用這個過濾器對字符串、數組、實現``Traversable``接口的對象進行包含關系測試。
使用``not in``操作符執行否測試:
{% if 1 not in [1, 2, 3] %}
{# 全等于 #}
{% if not (1 in [1, 2, 3]) %}
###測試操作符(Test Operator)
使用``is``操作符執行測試。測試可以用于針對變量和一般表達式之間的關系進行測試。右側操作數即是測試的名稱:
{# 檢查變量是否是奇數 #}
{{ name is odd }}
測試可以接受參數:
{% if post.status is constant('Post::PUBLISHED') %}
是否 ``is not`` 操作符進行否測試:
{% if post.status is not constant('Post::PUBLISHED') %}
{# is equivalent to #}
{% if not (post.status is constant('Post::PUBLISHED')) %}
查看tests頁面,了解更多內置測試。
###其他操作符
> 在Twig 1.12.0版加入了對擴展的三元操作符的支持。
以下操作符不適用于其他類型中:
* ``|``: 使用一個過濾器。
* ``..``: 創建一個基于操作符前后操作數的序列(這只是range函數的語法糖):
{{ 1..5 }}
{# equivalent to #}
{{ range(1, 5) }}
注意,由于操作符優先級規則的原因,你必須使用在將本操作符與過濾器組合時使用括號包裹:
`(1..5)|join(', ')`
* ``~``: 將所有操作數轉換為字符串并連接它們。``{{ "Hello " ~ name ~ "!" }}`` 將會返回(嘉定``name`` 是 ``'John'``) ``Hello
John!``。
* ``.``, ``[]``: 獲取對象的屬性。
* ``?:``: 三元操作符:
{{ foo ? 'yes' : 'no' }}
{# as of Twig 1.12.0 #}
{{ foo ?: 'no' }} 等同于 {{ foo ? foo : 'no' }}
{{ foo ? 'yes' }} 等同于 {{ foo ? 'yes' : '' }}
* ``??``: 非空操作符:
{# 如果foo已被定義非空值,則返回foo的值,否則返回'no' #}
{{ foo ?? 'no' }}
###字符串插值
> 在Twig1.5中加入的字符串插值
字符串插值(``#{expression}``)允許在**雙引號字符串**中出現任意有效的表達式。評估表達式的結果,就是將其插入到字符串中:
{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}
##空白控制(whitespace control)
> 在Twig 1.1版,計入了標簽級的空白控制
模板標簽后的第一個換行會被自動移除(如同PHP)。空白并不是由模板引擎進一步修改的,所以每個空白(空格、制表符、換行)都被未改變地返回。
使用 ``spaceless`` 標簽一處*HTML之間*的空白:
{% spaceless %}
<div>
<strong>foo bar</strong>
</div>
{% endspaceless %}
{# 將會輸出 <div><strong>foo bar</strong></div> #}
除了 spaceless 標簽,你還可以針對每個標簽級進行空白控制。通過對標簽使用空白控制修改器,可以移除首尾的空白:
{% set value = 'no spaces' %}
{#- No leading/trailing whitespace -#}
{%- if true -%}
{{- value -}}
{%- endif -%}
{# output 'no spaces' #}
上面的例子展示了默認的空白控制修改器,以及如何移除標簽左右的空白。移除空白會刪除標簽左側或右側的所有空白。你可以使用它來消除標簽某一側的空白:
{% set value = 'no spaces' %}
<li> {{- value }} </li>
{# outputs '<li>no spaces </li>' #}
##擴展
Twig可被輕松擴展。
如果你在尋找新的標簽、過濾器、或者函數,可以看一下[官方擴展倉庫](http://http://github.com/twigphp/Twig-extensions)。
如果想要創建自己的擴展,閱讀創建擴展章節。
- 首頁
- 目錄
- 介紹
- 安裝
- 面向模板設計師
- 面向開發者
- 擴展 Twig
- Twig的內部構建
- 棄用的特性
- 使用技巧
- 代碼規范
- 標簽 tags
- autoescape
- block
- do
- embed
- extends
- filter
- flush
- for
- from
- if
- import
- include
- macro
- sandbox
- set
- spaceless
- use
- verbatim
- 過濾器
- abs
- batch
- capitalize
- convert_encoding
- date
- date_modify
- default
- escape
- first
- format
- join
- json_encode
- keys
- last
- length
- lower
- merge
- nl2br
- number_format
- raw
- replace
- reverse
- round
- slice
- sort
- split
- striptags
- title
- trim
- upper
- url_encode
- 函數
- attribute
- block
- constant
- cycle
- date
- dump
- include
- max
- min
- parent
- random
- range
- source
- template_from_string
- 測試
- constant
- defined
- divisibleby
- empty
- even
- iterable
- null
- odd
- sameas