# 面向模板设计师
---
本文档介绍模板引擎的语法和语义,这是创建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