Twig

灵活、快速且安全的
PHP 模板引擎

a Symfony Product
文档 开发者 Twig
您正在阅读 Twig 3.x 的文档。切换到 Twig 1.x, 2.x 的文档。

问题 & 反馈

许可

Twig 文档 基于新的 BSD 许可 授权。

开发者 Twig

本章介绍 Twig 的 API,而不是模板语言。对于那些为应用程序实现模板接口的人,而不是那些创建 Twig 模板的人来说,它将是最有用的参考。

基础

Twig 使用一个名为 environment (类 \Twig\Environment)的中心对象。此类的实例用于存储配置和扩展,并用于加载模板。

大多数应用程序在应用程序初始化时创建一个 \Twig\Environment 对象,并使用它来加载模板。在某些情况下,并排拥有多个具有不同配置的环境可能很有用。

配置 Twig 为应用程序加载模板的典型方法大致如下

1
2
3
4
5
6
require_once '/path/to/vendor/autoload.php';

$loader = new \Twig\Loader\FilesystemLoader('/path/to/templates');
$twig = new \Twig\Environment($loader, [
    'cache' => '/path/to/compilation_cache',
]);

这将创建一个具有默认配置的模板环境和一个在 /path/to/templates/ 目录中查找模板的加载器。可以使用不同的加载器,如果您想从数据库或其他资源加载模板,也可以编写自己的加载器。

注意

请注意,环境的第二个参数是选项数组。cache 选项是编译缓存目录,Twig 在其中缓存编译后的模板,以避免后续请求的解析阶段。这与您可能想要为评估后的模板添加的缓存非常不同。对于这种需求,您可以使用任何可用的 PHP 缓存库。

加载模板

要加载模板,请在 Twig 环境中调用 load() 方法,该方法返回一个 \Twig\TemplateWrapper 实例

1
$template = $twig->load('index.html.twig');

渲染模板

要使用一些变量渲染模板,请调用 render() 方法

1
echo $template->render(['the' => 'variables', 'go' => 'here']);

注意

display() 方法是输出渲染模板的快捷方式。

您也可以直接通过 Environment 加载和渲染模板

1
echo $twig->render('index.html.twig', ['the' => 'variables', 'go' => 'here']);

如果模板定义了块,则可以通过 renderBlock() 调用单独渲染它们

1
echo $template->renderBlock('block_name', ['the' => 'variables', 'go' => 'here']);

流式模板

3.18

要流式传输模板,请调用 stream() 方法

1
$template->stream(['the' => 'variables', 'go' => 'here']);

要流式传输特定的模板块,请调用 streamBlock() 方法

1
$template->streamBlock('block_name', ['the' => 'variables', 'go' => 'here']);

注意

stream()streamBlock() 方法返回一个可迭代对象。

环境选项

在创建新的 \Twig\Environment 实例时,您可以将选项数组作为构造函数的第二个参数传递

1
$twig = new \Twig\Environment($loader, ['debug' => true]);

以下选项可用

  • debug 布尔值

    设置为 true 时,生成的模板具有一个 __toString() 方法,您可以使用该方法显示生成的节点(默认为 false)。

  • charset 字符串 (默认为 utf-8)

    模板使用的字符集。

  • cache 字符串false

    存储编译后的模板的绝对路径,或 false 以禁用缓存(默认值)。

  • auto_reload 布尔值

    当使用 Twig 开发时,每当源代码更改时重新编译模板都很有用。如果您没有为 auto_reload 选项提供值,它将根据 debug 值自动确定。

  • strict_variables 布尔值

    如果设置为 false,Twig 将静默忽略无效变量(不存在的变量和/或属性/方法)并将它们替换为 null 值。当设置为 true 时,Twig 会抛出异常(默认为 false)。

  • autoescape 字符串

    设置默认的自动转义策略(name, html, js, css, url, html_attr,或一个 PHP 回调,它接受模板“文件名”并返回要使用的转义策略——回调不能是函数名,以避免与内置转义策略冲突);将其设置为 false 以禁用自动转义。name 转义策略根据模板文件名扩展名确定模板要使用的转义策略(此策略在运行时不会产生任何开销,因为自动转义是在编译时完成的。)

  • optimizations 整数

    一个标志,指示要应用哪些优化(默认为 -1 —— 启用所有优化;将其设置为 0 以禁用)。

加载器

加载器负责从文件系统等资源加载模板。

编译缓存

所有模板加载器都可以将编译后的模板缓存在文件系统上以供将来重用。这大大加快了 Twig 的速度,因为模板只编译一次。

内置加载器

以下是内置加载器的列表

\Twig\Loader\FilesystemLoader

\Twig\Loader\FilesystemLoader 从文件系统加载模板。此加载器可以在文件系统上的文件夹中查找模板,并且是加载模板的首选方式

1
$loader = new \Twig\Loader\FilesystemLoader($templateDir);

它还可以在目录数组中查找模板

1
$loader = new \Twig\Loader\FilesystemLoader([$templateDir1, $templateDir2]);

使用这样的配置,Twig 将首先在 $templateDir1 中查找模板,如果它们不存在,它将回退到在 $templateDir2 中查找它们。

您可以通过 addPath()prependPath() 方法添加或前置路径

1
2
$loader->addPath($templateDir3);
$loader->prependPath($templateDir4);

文件系统加载器还支持命名空间模板。这允许您将模板分组在具有自己模板路径的不同命名空间下。

当使用 setPaths(), addPath()prependPath() 方法时,将命名空间指定为第二个参数(未指定时,这些方法作用于“main”命名空间)

1
$loader->addPath($templateDir, 'admin');

可以通过特殊的 @namespace_name/template_path 表示法访问命名空间模板

1
$twig->render('@admin/index.html.twig', []);

\Twig\Loader\FilesystemLoader 支持绝对路径和相对路径。首选使用相对路径,因为它使缓存键独立于项目根目录(例如,它允许从构建服务器预热缓存,其中目录可能与生产服务器上使用的目录不同)

1
$loader = new \Twig\Loader\FilesystemLoader('templates', getcwd().'/..');

注意

当不将根路径作为第二个参数传递时,Twig 对相对路径使用 getcwd()

\Twig\Loader\ArrayLoader

\Twig\Loader\ArrayLoader 从 PHP 数组加载模板。它被传递一个绑定到模板名称的字符串数组

1
2
3
4
5
6
$loader = new \Twig\Loader\ArrayLoader([
    'index.html.twig' => 'Hello {{ name }}!',
]);
$twig = new \Twig\Environment($loader);

echo $twig->render('index.html.twig', ['name' => 'Fabien']);

此加载器对于单元测试非常有用。它也可以用于小型项目,在小型项目中,将所有模板存储在单个 PHP 文件中可能是有意义的。

提示

当将 Array 加载器与缓存机制一起使用时,您应该知道每次模板内容“更改”时都会生成一个新的缓存键(缓存键是模板的源代码)。如果您不想看到您的缓存失控增长,您需要自己负责清除旧的缓存文件。

\Twig\Loader\ChainLoader

\Twig\Loader\ChainLoader 将模板的加载委托给其他加载器

1
2
3
4
5
6
7
8
9
10
11
$loader1 = new \Twig\Loader\ArrayLoader([
    'base.html.twig' => '{% block content %}{% endblock %}',
]);
$loader2 = new \Twig\Loader\ArrayLoader([
    'index.html.twig' => '{% extends "base.html.twig" %}{% block content %}Hello {{ name }}{% endblock %}',
    'base.html.twig'  => 'Will never be loaded',
]);

$loader = new \Twig\Loader\ChainLoader([$loader1, $loader2]);

$twig = new \Twig\Environment($loader);

当查找模板时,Twig 依次尝试每个加载器,并在找到模板后立即返回。当从上面的示例渲染 index.html.twig 模板时,Twig 将使用 $loader2 加载它,但 base.html.twig 模板将从 $loader1 加载。

注意

您也可以通过 addLoader() 方法添加加载器。

创建您自己的加载器

所有加载器都实现了 \Twig\Loader\LoaderInterface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
interface \Twig\Loader\LoaderInterface
{
    /**
     * Returns the source context for a given template logical name.
     *
     * @param string $name The template logical name
     *
     * @return \Twig\Source
     *
     * @throws \Twig\Error\LoaderError When $name is not found
     */
    public function getSourceContext($name);

    /**
     * Gets the cache key to use for the cache for a given template name.
     *
     * @param string $name The name of the template to load
     *
     * @return string The cache key
     *
     * @throws \Twig\Error\LoaderError When $name is not found
     */
    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
     *
     * @return bool    true if the template is fresh, false otherwise
     *
     * @throws \Twig\Error\LoaderError When $name is not found
     */
    public function isFresh($name, $time);

    /**
     * Check if we have the source code of a template, given its name.
     *
     * @param string $name The name of the template to check if we can load
     *
     * @return bool    If the template source code is handled by this loader or not
     */
    public function exists($name);
}

如果当前的缓存模板仍然新鲜,则 isFresh() 方法必须返回 true,给定上次修改时间,否则返回 false

getSourceContext() 方法必须返回 \Twig\Source 的实例。

使用扩展

Twig 扩展是为 Twig 添加新功能的包。通过 addExtension() 方法注册扩展

1
$twig->addExtension(new \Twig\Extension\SandboxExtension());

Twig 捆绑了以下扩展

  • TwigExtensionCoreExtension:定义 Twig 的所有核心功能。
  • TwigExtensionDebugExtension:定义 dump 函数以帮助调试模板变量。
  • TwigExtensionEscaperExtension:添加自动输出转义以及转义/取消转义代码块的可能性。
  • TwigExtensionSandboxExtension:向默认 Twig 环境添加沙箱模式,使其可以安全地评估不受信任的代码。
  • TwigExtensionProfilerExtension:启用内置的 Twig profiler。
  • TwigExtensionOptimizerExtension:在编译之前优化节点树。
  • TwigExtensionStringLoaderExtension:定义 template_from_string
    函数,允许从模板中的字符串加载模板。

Core、Escaper 和 Optimizer 扩展默认注册。

内置扩展

本节介绍内置扩展添加的功能。

提示

阅读关于 扩展 Twig 的章节,了解如何创建自己的扩展。

核心扩展

core 扩展定义了 Twig 的所有核心功能

Escaper 扩展

escaper 扩展为 Twig 添加了自动输出转义。它定义了一个标签 autoescape 和一个过滤器 raw

创建 escaper 扩展时,您可以打开或关闭全局输出转义策略

1
2
$escaper = new \Twig\Extension\EscaperExtension('html');
$twig->addExtension($escaper);

如果设置为 html,则模板中的所有变量都将被转义(使用 html 转义策略),除了那些使用 raw 过滤器的变量

1
{{ article.to_html|raw }}

您也可以通过使用 autoescape 标签在本地更改转义模式

1
2
3
4
5
{% autoescape 'html' %}
    {{ var }}
    {{ var|raw }}      {# var won't be escaped #}
    {{ var|escape }}   {# var won't be double-escaped #}
{% endautoescape %}

警告

autoescape 标签对包含的文件没有影响。

转义规则的实现如下

  • 在模板中直接用作变量或过滤器参数的文字(整数、布尔值、数组等)永远不会自动转义

    1
    2
    3
    4
    {{ "Twig<br/>" }} {# won't be escaped #}
    
    {% set text = "Twig<br/>" %}
    {{ text }} {# will be escaped #}
  • 结果是文字或标记为安全的变量的表达式永远不会自动转义

    1
    2
    3
    4
    5
    6
    7
    8
    {{ any_value ? "Twig<br/>" : "<br/>Twig" }} {# won't be escaped #}
    
    {% set text = "Twig<br/>" %}
    {{ true ? text : "<br/>Twig" }} {# will be escaped #}
    {{ false ? text : "<br/>Twig" }} {# won't be escaped #}
    
    {% set text = "Twig<br/>" %}
    {{ any_value ? text|raw : "<br/>Twig" }} {# won't be escaped #}
  • 具有 __toString 方法的对象被转换为字符串并转义。您可以通过 EscaperExtension::addSafeClass() 将某些类和/或接口标记为对某些策略是安全的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // mark objects of class "HtmlGenerator" as safe for the HTML strategy
    $escaper->addSafeClass('HtmlGenerator', ['html']);
    
    // mark objects of interface "HtmlGeneratorInterface" as safe for the HTML strategy
    $escaper->addSafeClass('HtmlGeneratorInterface', ['html']);
    
    // mark objects of class "HtmlGenerator" as safe for the HTML and JS strategies
    $escaper->addSafeClass('HtmlGenerator', ['html', 'js']);
    
    // mark objects of class "HtmlGenerator" as safe for all strategies
    $escaper->addSafeClass('HtmlGenerator', ['all']);
  • 转义在打印之前应用,在应用任何其他过滤器之后应用

    1
    {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #}
  • raw 过滤器应仅在过滤器链的末尾使用

    1
    2
    3
    {{ var|raw|upper }} {# will be escaped #}
    
    {{ var|upper|raw }} {# won't be escaped #}
  • 如果链中的最后一个过滤器被标记为对当前上下文安全(例如,htmljs),则不应用自动转义。escapeescape('html') 被标记为对 HTML 安全,escape('js') 被标记为对 JavaScript 安全,raw 被标记为对所有内容安全。

    1
    2
    3
    4
    5
    {% autoescape 'js' %}
        {{ 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 %}

注意

请注意,自动转义有一些限制,因为转义是在评估后的表达式上应用的。例如,当使用连接时,{{ value|raw ~ other }} 不会给出预期的结果,因为转义应用于连接的结果,而不是单个变量(因此,raw 过滤器在这里不会有任何效果)。

Sandbox 扩展

sandbox 扩展可用于评估不受信任的代码。在 Twig Sandbox 章节中阅读更多相关信息。

Profiler 扩展

profiler 扩展为 Twig 模板启用了一个 profiler;它应该仅在您的开发机器上使用,因为它会增加一些开销

1
2
3
4
5
$profile = new \Twig\Profiler\Profile();
$twig->addExtension(new \Twig\Extension\ProfilerExtension($profile));

$dumper = new \Twig\Profiler\Dumper\TextDumper();
echo $dumper->dump($profile);

一个 profile 包含有关模板、块和宏执行的时间和内存消耗的信息。

您也可以以 Blackfire.io 兼容的格式转储数据

1
2
$dumper = new \Twig\Profiler\Dumper\BlackfireDumper();
file_put_contents('/path/to/profile.prof', $dumper->dump($profile));

上传 profile 以可视化它(首先创建一个 免费帐户

1
blackfire --slot=7 upload /path/to/profile.prof

Optimizer 扩展

optimizer 扩展在编译之前优化节点树

1
$twig->addExtension(new \Twig\Extension\OptimizerExtension());

默认情况下,所有优化都已启用。您可以通过将它们传递给构造函数来选择要启用的优化

1
2
3
$optimizer = new \Twig\Extension\OptimizerExtension(\Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_FOR);

$twig->addExtension($optimizer);

Twig 支持以下优化

  • \Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_ALL,启用所有优化(这是默认值)。
  • \Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_NONE,禁用所有优化。这减少了编译时间,但可能会增加执行时间和消耗的内存。
  • \Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_FOR,通过在可能的情况下删除 loop 变量创建来优化 for 标签。

异常

Twig 可以抛出异常

  • \Twig\Error\Error:所有错误的基本异常。
  • \Twig\Error\SyntaxError:抛出以告知用户模板语法存在问题。
  • \Twig\Error\RuntimeError:在运行时发生错误时抛出(例如,当过滤器不存在时)。
  • \Twig\Error\LoaderError:在模板加载期间发生错误时抛出。
  • \Twig\Sandbox\SecurityError:在沙箱模板中调用不允许的标签、过滤器或方法时抛出。