模板设计器的Twig

本文描述了模板引擎的语法和语义,对于那些创建Twig模板的人来说非常有用。

简介

模板是一个常规的文本文件。它可以生成任何基于文本的格式(HTML、XML、CSV、LaTeX等)。它没有特定的扩展名, .html.xml 很好。

模板包含 变量表达 ,在评估模板时将其替换为值,并且 tags ,控制模板的逻辑。

下面是一个最小的模板,它说明了一些基础知识。我们稍后将介绍更多细节:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!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循环之类的语句,后者输出表达式的结果。

IDEs集成

许多IDE支持Twig的语法高亮显示和自动完成:

也, TwigFiddle 是一个在线服务,允许您从浏览器执行Twig模板;它支持所有版本的Twig。

变量

应用程序将变量传递给模板,以便在模板中进行操作。变量也可以有你可以访问的属性或元素。变量的可视化表示在很大程度上依赖于提供它的应用程序。

使用点 (. )要访问变量的属性(PHP对象的方法或属性,或PHP数组的项):

1
{{ foo.bar }}

注解

重要的是要知道大括号 not 变量的一部分,但print语句。当访问标记中的变量时,不要将括号放在标记周围。

如果变量或属性不存在,则会收到 nullstrict_variables 选项设置为 false 或者,如果 strict_variables 设置后,Twig将抛出一个错误(请参见 environment options

注解

如果要访问变量的动态属性,请使用 attribute 而是函数。

这个 attribute 当属性包含特殊字符(如 - 这将被解释为减号运算符):

1
2
{# equivalent to the non-working foo.data-foo #}
{{ attribute(foo, 'data-foo') }}

全局变量

模板中始终提供以下变量:

  • _self :引用当前模板名称;
  • _context :引用当前上下文;
  • _charset :引用当前字符集。

设置变量

可以为代码块内的变量赋值。作业使用 set 标签:

1
2
3
{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}

过滤器

变量可以修改为 过滤器 . 过滤器与变量之间用管道符号隔开 (| ). 可以链接多个过滤器。一个过滤器的输出应用于下一个过滤器。

下面的示例从 name 标题是:

1
{{ name|striptags|title }}

接受参数的筛选器在参数周围有括号。此示例通过逗号连接列表中的元素:

1
{{ list|join(', ') }}

若要对代码部分应用筛选器,请使用 apply 标签:

1
2
3
{% apply upper %}
    This text becomes uppercase
{% endapply %}

filters 页面以了解有关内置过滤器的更多信息。

功能

可以调用函数来生成内容。函数的名称后跟括号来调用 (() )可能会有争论。

例如, range 函数返回一个包含整数算术级数的列表:

1
2
3
{% for i in range(0, 3) %}
    {{ i }},
{% endfor %}

functions 页面以了解有关内置函数的更多信息。

命名参数

1
2
3
{% for i in range(low=1, high=10, step=2) %}
    {{ i }},
{% endfor %}

使用命名参数可以使模板更明确地显示作为参数传递的值的含义:

1
2
3
4
5
{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}

{# versus #}

{{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}

命名参数还允许您跳过某些不想更改其默认值的参数:

1
2
3
4
5
{# the first argument is the date format, which defaults to the global date format if null is passed #}
{{ "now"|date(null, "Europe/Paris") }}

{# or skip the format value by using a named argument for the time zone #}
{{ "now"|date(timezone="Europe/Paris") }}

也可以在一个调用中同时使用位置参数和命名参数,在这种情况下,位置参数必须始终位于命名参数之前:

1
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}

小技巧

每个函数和筛选器文档页都有一个部分,其中列出了所有参数的名称(如果支持)。

控制结构

控制结构是指所有控制程序流的东西-条件句(即。 if/elseif /否则), for -循环,以及块之类的东西。控制结构出现在里面 {{% ... %}} 阻碍。

例如,要显示在名为 users 使用 for 标签:

1
2
3
4
5
6
<h1>Members</h1>
<ul>
    {% for user in users %}
        <li>{{ user.username|e }}</li>
    {% endfor %}
</ul>

这个 if 标记可用于测试表达式:

1
2
3
4
5
6
7
{% if users|length > 0 %}
    <ul>
        {% for user in users %}
            <li>{{ user.username|e }}</li>
        {% endfor %}
    </ul>
{% endif %}

tags 页面以了解有关内置标记的更多信息。

评论

要注释掉模板中某行的一部分,请使用注释语法 {{# ... #}} . 这对于调试或为其他模板设计器或您自己添加信息非常有用:

1
2
3
4
5
{# note: disabled template because we no longer use this
    {% for user in users %}
        ...
    {% endfor %}
#}

包括其他模板

这个 include 函数可用于包含模板并将该模板的呈现内容返回到当前模板中:

1
{{ include('sidebar.html') }}

默认情况下,包含的模板可以访问与包含它们的模板相同的上下文。这意味着主模板中定义的任何变量也将在包含的模板中可用:

1
2
3
{% for box in boxes %}
    {{ include('render_box.html') }}
{% endfor %}

包含的模板 render_box.html 能够访问 box 变量。

模板的名称取决于模板加载器。例如 \Twig\Loader\FilesystemLoader 允许您通过指定文件名来访问其他模板。可以使用斜杠访问子目录中的模板:

1
{{ include('sections/articles/sidebar.html') }}

此行为取决于应用程序嵌入Twig。

模板继承

Twig最强大的部分是模板继承。模板继承允许您构建一个基本的“骨架”模板,该模板包含站点的所有公共元素并定义 阻碍 子模板可以重写。

从一个例子开始更容易理解这个概念。

让我们定义一个基本模板, base.html ,它定义了可用于两列页面的HTML框架文档:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!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 %}
                &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
            {% endblock %}
        </div>
    </body>
</html>

在这个例子中, block 标记定义了子模板可以填充的四个块。所有的 block 标记的作用是告诉模板引擎子模板可能会覆盖模板的这些部分。

子模板可能如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{% 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 块,使用父模板中的值。

可以使用 parent 功能。这将返回父块的结果:

1
2
3
4
5
{% block sidebar %}
    <h3>Table Of Contents</h3>
    ...
    {{ parent() }}
{% endblock %}

小技巧

的文档页 extends 标记描述了更高级的特性,如块嵌套、作用域、动态继承和条件继承。

注解

Twig还通过“水平重用”在 use 标签。

HTML逃逸

当从模板生成HTML时,总是存在这样一种风险:变量将包含影响结果HTML的字符。有两种方法:手动转义每个变量或在默认情况下自动转义所有变量。

Twig同时支持这两种功能,默认情况下启用了自动转义。

自动转义策略可以通过 autoescape 选项,默认为 html .

使用手动逃逸

如果启用了手动转义,则为 your 如果需要,有责任逃避变量。逃什么?来自不可信源的任何变量。

通过使用 escapee 过滤器:

1
{{ user.username|e }}

默认情况下, escape 过滤器使用 html 策略,但根据转义上下文,您可能希望显式使用其他策略:

1
2
3
4
{{ user.username|e('js') }}
{{ user.username|e('css') }}
{{ user.username|e('url') }}
{{ user.username|e('html_attr') }}

使用自动转义

无论是否启用自动转义,都可以使用 autoescape 标签:

1
2
3
{% autoescape %}
    Everything will be automatically escaped in this block (using the HTML strategy)
{% endautoescape %}

默认情况下,自动转义使用 html 逃避策略。如果在其他上下文中输出变量,则需要使用适当的转义策略显式转义它们:

1
2
3
{% autoescape 'js' %}
    Everything will be automatically escaped in this block (using the JS strategy)
{% endautoescape %}

逃逸

有时需要甚至有必要让Twig忽略它将作为变量或块处理的部分。例如,如果使用了默认语法,并且您希望使用 {{{{ 作为模板中的原始字符串而不是一个变量,你必须使用一个技巧。

最简单的方法是输出变量分隔符 ({{{{ )通过使用变量表达式:

1
{{ '{{' }}

对于较大的部分,标记一个块是有意义的 verbatim .

宏指令

宏与常规编程语言中的函数相当。它们有助于重用HTML片段以避免重复。它们在 macro 标签文档。

表达

Twig允许表达式无处不在。

注解

运算符优先级如下,优先级最低的运算符列在最前面: ?: (ternary operator), b-and, b-xor, b-or, or, and, ==, !=, <=>, <, >, >=, <=, in, matches, starts with, ends with, .., +, -, ~, *, /, `` /; ``%`is (测试), **??| (过滤器), [].

1
2
3
4
5
6
7
{% set greeting = 'Hello ' %}
{% set name = 'Fabien' %}

{{ greeting ~ name|lower }}   {# Hello fabien #}

{# use parenthesis to change precedence #}
{{ (greeting ~ name)|lower }} {# hello fabien #}

直接常量

最简单的表达式形式是文字。文字是PHP类型(如字符串、数字和数组)的表示。存在以下文字:

  • "Hello World" :两个双引号或单引号之间的所有内容都是字符串。当您需要模板中的字符串时(例如,作为函数调用、过滤器的参数,或者只是为了扩展或包含模板),它们都很有用。如果字符串前面有反斜杠,则可以包含分隔符 (\ )--就像在 'It\'s good' . 如果字符串包含反斜杠(例如。 'c:\Program Files' )通过加倍来逃避(例如。 'c:\\Program Files'

  • 42 / 42.23 :整数和浮点数是通过写下数字来创建的。如果有一个点,这个数字是一个浮点数,否则是一个整数。

  • ["foo", "bar"] :数组由逗号分隔的表达式序列定义 (, )用方括号包起来 ([]

  • {{"foo": "bar"}} :散列由逗号分隔的键和值列表定义 (, )用大括号包着 ({{}} ):

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    {# keys as string #}
    { 'foo': 'foo', 'bar': 'bar' }
    
    {# keys as names (equivalent to the previous hash) #}
    { foo: 'foo', bar: 'bar' }
    
    {# keys as integer #}
    { 2: 'foo', 4: 'bar' }
    
    {# keys can be omitted if it is the same as the variable name #}
    { foo }
    {# is equivalent to the following #}
    { 'foo': foo }
    
    {# keys as expressions (the expression must be enclosed into parentheses) #}
    {% set foo = 'foo' %}
    { (foo): 'foo', (1 + 1): 'bar', (foo ~ 'b'): 'baz' }
    
  • true / falsetrue 代表真值, false 表示假值。

  • nullnull 表示没有特定值。这是变量不存在时返回的值。 none 是一个别名 null .

数组和哈希可以嵌套:

1
{% set foo = [1, {"foo": "bar"}] %}

小技巧

使用双引号或单引号字符串对性能没有影响,但是 string interpolation 仅在双引号字符串中支持。

数学

Twig允许您在模板中进行数学运算;支持以下运算符:

  • + :将两个数字相加(操作数转换为数字)。 {{{{ 1 + 1 }}}}2 .
  • - :从第一个数字中减去第二个数字。 {{{{ 3 - 2 }}}}1 .
  • / :除以两个数字。返回值将是一个浮点数。 {{{{ 1 / 2 }}}}{{{{ 0.5 }}}} .
  • % :计算整数除法的余数。 {{{{ 11 % 7 }}}}4 .
  • // :将两个数字相除并返回整数结果。 {{{{ 20 // 7 }}}}2{{{{ -20  // 7 }}}}-3 (这只是 round 过滤器)。
  • * :将左操作数与右操作数相乘。 {{{{ 2 * 2 }}}} 会回来 4 .
  • ** :将左操作数提升到右操作数的幂。 {{{{ 2 ** 3 }}}} 会回来 8 .

逻辑

可以使用以下运算符组合多个表达式:

  • and :如果左操作数和右操作数都为true,则返回true。
  • or :如果左操作数或右操作数为真,则返回true。
  • not :否定语句。
  • (expr) :对表达式分组。

注解

Twig还支持按位运算符 (b-andb-xorb-or

注解

运算符区分大小写。

比较

任何表达式都支持以下比较运算符: ==!=<>>=<= .

您还可以检查字符串 starts withends with 另一个字符串:

1
2
3
4
5
{% if 'Fabien' starts with 'F' %}
{% endif %}

{% if 'Fabien' ends with 'n' %}
{% endif %}

注解

对于复杂字符串比较 matches 操作员允许您使用 regular expressions

1
2
{% if phone matches '/^[\\d\\.]+$/' %}
{% endif %}

安全壳操作员

这个 in 操作员进行密封试验。它回来了 true 如果左操作数包含在右操作数中:

1
2
3
4
5
{# returns true #}

{{ 1 in [1, 2, 3] }}

{{ 'cd' in 'abcde' }}

小技巧

可以使用此筛选器对实现 Traversable 接口。

要执行阴性测试,请使用 not in 操作员:

1
2
3
4
{% if 1 not in [1, 2, 3] %}

{# is equivalent to #}
{% if not (1 in [1, 2, 3]) %}

测试操作员

这个 is 操作员执行测试。测试可用于根据公共表达式测试变量。右操作数是测试的名称:

1
2
3
{# find out if a variable is odd #}

{{ name is odd }}

测试也可以接受参数:

1
{% if post.status is constant('Post::PUBLISHED') %}

可以使用 is not 操作员:

1
2
3
4
{% if post.status is not constant('Post::PUBLISHED') %}

{# is equivalent to #}
{% if not (post.status is constant('Post::PUBLISHED')) %}

tests 页面以了解有关内置测试的更多信息。

其他经营者

以下运算符不适用于任何其他类别:

  • | :应用筛选器。

  • .. :基于运算符前后的操作数创建序列(这是 range 功能:

    1
    2
    3
    4
    {{ 1..5 }}
    
    {# equivalent to #}
    {{ range(1, 5) }}
    

    注意,由于 operator precedence rules

    1
    (1..5)|join(', ')
    
  • ~ :将所有操作数转换为字符串并连接它们。 {{{{ "Hello " ~ name ~ "!" }}}} 将返回(假设 name'John'Hello John! .

  • .[] :获取变量的属性。

  • ?: :三元运算符:

    1
    2
    3
    {{ foo ? 'yes' : 'no' }}
    {{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }}
    {{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }}
    
  • ?? :空合并运算符:

    1
    2
    {# returns the value of foo if it is defined and not null, 'no' otherwise #}
    {{ foo ?? 'no' }}
    

串插值

串插值 (#{{expression}} )允许任何有效表达式出现在 double-quoted string . 计算该表达式的结果将插入到字符串中:

1
2
{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}

空白控件

模板标记后的第一个换行被自动删除(就像在PHP中)。模板引擎不会进一步修改空白,因此每个空白(空格、制表符、换行符等)都会原封不动地返回。

您还可以在每个标记级别上控制空白。通过在标记上使用空白控件修饰符,可以修剪前导空格和/或尾随空格。

Twig支持两个修改器:

  • 空白修剪 通过 - 修饰符:删除所有空白(包括换行符);
  • 线条空白修剪 通过 ~ 修饰符:删除所有空白(不包括换行符)。在右边使用这个修饰符将禁用从PHP继承的第一个换行符的默认删除。

修饰符可以在标记的任一侧使用,如 {{%--%}} 它们会消耗标签那一边的所有空白。可以在标记的一侧或两侧使用修饰符:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{% set value = 'no spaces' %}
{#- No leading/trailing whitespace -#}
{%- if true -%}
    {{- value -}}
{%- endif -%}
{# output 'no spaces' #}

<li>
    {{ value }}    </li>
{# outputs '<li>\n    no spaces    </li>' #}

<li>
    {{- value }}    </li>
{# outputs '<li>no spaces    </li>' #}

<li>
    {{~ value }}    </li>
{# outputs '<li>\nno spaces    </li>' #}

小技巧

除了空格修饰符之外,Twig还有一个 spaceless 删除空白的筛选器 在HTML标记之间

1
2
3
4
5
6
7
{% apply spaceless %}
    <div>
        <strong>foo bar</strong>
    </div>
{% endapply %}

{# output will be <div><strong>foo bar</strong></div> #}

扩展

树枝可以伸展。如果您想创建自己的扩展,请阅读 Creating an Extension 章。