面向开发者的Twig

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

基础

Twig使用一个名为 环境 (等级) \Twig\Environment ). 此类的实例用于存储配置和扩展,并用于加载模板。

大多数应用程序都会创建一个 \Twig\Environment 对象,并使用该对象加载模板。在某些情况下,使用不同的配置同时使用多个环境可能会很有用。

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

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 实例:

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

若要使用某些变量呈现模板,请调用 render() 方法:

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

注解

这个 display() 方法是输出呈现模板的快捷方式。

也可以一次加载和渲染模板:

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

如果模板定义块,则可以通过 renderBlock() 呼叫:

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

环境选项

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

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

以下选项可用:

  • debug boolean

    当设置为 true ,生成的模板具有 __toString() 方法,可用于显示生成的节点(默认为 false

  • charset string (defaults to utf-8)

    模板使用的字符集。

  • cache string or false

    存储已编译模板的绝对路径,或 false 禁用缓存(这是默认设置)。

  • auto_reload boolean

    使用Twig进行开发时,每当源代码发生变化时重新编译模板是很有用的。如果不为 auto_reload 选项,它将根据 debug 价值。

  • strict_variables boolean

    如果设置为 false ,Twig将自动忽略无效变量(不存在的变量和/或属性/方法),并将它们替换为 null 价值观。设置为时 true 而是抛出一个默认的Twig false

  • autoescape string

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

  • optimizations integer

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

装载机

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

编译缓存

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

内置装载机

以下是内置装载机的列表:

\Twig\Loader\FilesystemLoader

\Twig\Loader\FilesystemLoader 从文件系统加载模板。此加载程序可以在文件系统的文件夹中找到模板,并且是加载模板的首选方法:

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

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

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

有了这样的配置,Twig将首先在 $templateDir1 如果它们不存在,它将回退到 $templateDir2 .

您可以通过 addPath()prependPath() 方法::

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

文件系统加载器还支持名称空间模板。这样可以将模板分组到具有自己模板路径的不同名称空间下。

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

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

命名空间模板可以通过 @namespace_name/template_path 符号:

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

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

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

注解

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

\Twig\Loader\ArrayLoader

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

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

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

这个装载器对于单元测试非常有用。它还可以用于将所有模板存储在一个PHP文件中的小项目。

小技巧

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

\Twig\Loader\ChainLoader

\Twig\Loader\ChainLoader 将模板的加载委托给其他加载程序::

$loader1 = new \Twig\Loader\ArrayLoader([
    'base.html' => '{% block content %}{% endblock %}',
]);
$loader2 = new \Twig\Loader\ArrayLoader([
    'index.html' => '{% extends "base.html" %}{% block content %}Hello {{ name }}{% endblock %}',
    'base.html'  => 'Will never be loaded',
]);

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

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

在寻找模板时,Twig依次尝试每个加载程序,并在找到模板后立即返回。当渲染 index.html 模板来自上面的示例,Twig将用 $loader2 但是 base.html 模板将从 $loader1 .

注解

您也可以通过 addLoader() 方法。

创建自己的加载程序

所有装载机都执行 \Twig\Loader\LoaderInterface ::

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() 方法:

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

Twig附带以下扩展:

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

默认情况下,将注册核心、转义器和优化器扩展。

内置扩展

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

小技巧

阅读关于 extending Twig 学习如何创建自己的扩展。

堆芯延伸

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

逃逸器扩展

这个 escaper 扩展将自动输出转义添加到Twig。它定义了一个标记, autoescape ,和一个过滤器, raw .

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

$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
    {{ foo ? "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/>" %}
    {{ foo ? text|raw : "<br/>Twig" }} {# won't be escaped #}
    
  • 对象与A __toString 方法转换为字符串并转义。您可以通过将一些类和/或接口标记为对某些策略是安全的 EscaperExtension::addSafeClass()

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    // mark object of class Foo as safe for the HTML strategy
    $escaper->addSafeClass('Foo', ['html']);
    
    // mark object of interface Foo as safe for the HTML strategy
    $escaper->addSafeClass('FooInterface', ['html']);
    
    // mark object of class Foo as safe for the HTML and JS strategies
    $escaper->addSafeClass('Foo', ['html', 'js']);
    
    // mark object of class Foo as safe for all strategies
    $escaper->addSafeClass('Foo', ['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 #}
    
  • 如果链中的最后一个筛选器对于当前上下文标记为安全,则不应用自动转义(例如。 htmljsescapeescape('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 %}
    

注解

请注意,自动转义有一些限制,因为转义是在求值后应用于表达式的。例如,在处理连接时, {{{{ foo|raw ~ bar }}}} 不会给出预期的结果,因为转义应用于串联的结果,而不是应用于单个变量(因此 raw 过滤器在这里没有任何效果)。

沙盒扩展

这个 sandbox 扩展可用于计算不受信任的代码。禁止访问不安全的属性和方法。沙盒安全性由策略实例管理。默认情况下,Twig附带一个策略类: \Twig\Sandbox\SecurityPolicy . 此类允许您列出一些标记、筛选器、属性和方法的白名单:

$tags = ['if'];
$filters = ['upper'];
$methods = [
    'Article' => ['getTitle', 'getBody'],
];
$properties = [
    'Article' => ['title', 'body'],
];
$functions = ['range'];
$policy = new \Twig\Sandbox\SecurityPolicy($tags, $filters, $methods, $properties, $functions);

对于以前的配置,安全策略将只允许使用 if 标签,和 upper 过滤器。而且,模板只能调用 getTitle()getBody() 方法对 Article 对象,以及 titlebody 公共财产。其他的一切都是不允许的,并且会产生 \Twig\Sandbox\SecurityError 例外。

策略对象是沙盒构造函数的第一个参数:

$sandbox = new \Twig\Extension\SandboxExtension($policy);
$twig->addExtension($sandbox);

默认情况下,沙盒模式是禁用的,当使用 sandbox 标签:

1
2
3
{% sandbox %}
    {% include 'user.html' %}
{% endsandbox %}

您可以通过 true 作为扩展构造函数的第二个参数:

$sandbox = new \Twig\Extension\SandboxExtension($policy, true);

探查器扩展

这个 profiler extension为Twig模板启用了一个profiler;它应该只在开发计算机上使用,因为它会增加一些开销:

$profile = new \Twig\Profiler\Profile();
$twig->addExtension(new \Twig\Extension\ProfilerExtension($profile));

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

配置文件包含有关模板、块和宏执行的时间和内存消耗的信息。

也可以将数据转储到 Blackfire.io 兼容格式:

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

上传配置文件使其可视化(创建一个 free account 第一个):

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

优化器扩展

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

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

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

$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 ,优化 for 通过移除 loop 尽可能创建变量。
  • \Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_RAW_FILTER ,删除 raw 尽可能过滤。
  • \Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_VAR_ACCESS ,尽可能简化已编译模板中变量的创建和访问。

例外情况

Twig可以引发异常:

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