ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
#面向开发者 这一章介绍 Twig的API而非模版语言。这是针对实现模板接口的最有用的参考,而不是针对创建Twig模板。 ##基础 Twig 使用一个名为 **environment** 的中心对象(它是``Twig_Environment``类的对象)。这个类的实例用于存储配置和扩展,以及从文件系统或其他位置加载模版。 大多数应用程序会在初始化的时候创建一个 ``Twig_Environment`` 对象,并用它来加载模板。在某些情况下,如果在使用多个不同的配置,是可以让多个环境存在使用的。 为应用程序配置Twig加载模板的最简单方式大概是这样的: require_once '/path/to/lib/Twig/Autoloader.php'; Twig_Autoloader::register(); $loader = new Twig_Loader_Filesystem('/path/to/templates'); $twig = new Twig_Environment($loader, array( 'cache' => '/path/to/compilation_cache', )); 这将会创建一个带默认设置的模板环境()environment,以及一个在 ``/path/to/templates/`` 查找模版的加载器。不同的加载器都是可用的,如果你像从数据库或者其他来源加载模版,你也可以自己写一个加载器。 注意: > 值得注意的是,环境(environment)的第二个参数是一个环境选项数组。其中的``cache``选项表示编译缓存目录,Twig将编译过的模板缓存在这个目录中以避免解析阶段的重复请求。这与你可能想要为评估后的模版添加的缓存有很大的区别。对于这样的需求,你可以使用其他PHP缓存库。 要从环境中加载模版,只需要调用``loadTemplate()``方法即可,它会返回一个``Twig_Template``实例: $template = $twig->loadTemplate('index.html'); 要渲染带有变量的模板,调用``render()``方法: echo $template->render(array('the' => 'variables', 'go' => 'here')); 注意: > ``display()``方法是直接输出模版的快捷方式。 你还可以一步完成模板的加载和渲染: echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here')); ##环境选项 在创建一个新的 ``Twig_Environment`` 实例时,你可以传递一个选项数组作为构造函数的第二个参数: $twig = new Twig_Environment($loader, array('debug' => true)); 有以下这些可用选项: * ``debug`` *boolean* 设置为``true``时,生成的模版会有一个 ``__toString()`` 方法,可以用它来宣誓生成的节点。(默认设置是``false``)。 * ``charset`` *string (默认``utf-8``)* 用于模板的字符集。 * ``base_template_class`` *string (默认 ``Twig_Template``)* 用于生成的模板的基础模板类。 * ``cache`` *string|false* 存储编译后的模板的绝对路径,或者设为 ``false``来禁用缓存(默认``false``)。 * ``auto_reload`` *boolean* 在使用Twig进行开发时,这有助于在源代码修改时随时进行重新编译。如果不为``auto_reload``选项设置值,它会自动根据``debug``选项的值被设定。 * ``strict_variables`` *boolean* 如果设置为 ``false``,Twig会静默忽略无效的变量(包括变量、不存在的属性和方法),并以``null``值替换它们。如果将其设置为 ``true``,Twig则会抛出一个异常(默认是``false``)。 * ``autoescape`` *string|boolean* 如果设置为``true``,会默认为所有模板弃用HTML自动转义(默认是``true``)。 从Twig 1.8版开始,你可以将转义策略设置为(``html``, ``js``, 或者用``false``禁用)。 从Twig 1.9版开始,你可以将转义策略设置为(``css``, ``url``, ``html_attr``, or a PHP callback that takes the template "filename" and must return the escaping strategy to use -- the callback cannot be a function name to avoid collision with built-in escaping strategies)。 从Twig 1.17开始 , ``filename``转义策略决定了用于基于模板文件名扩展的模板的转义策略(由于自动转义在编译时就已经完成,所以本策略不会产生任何开销) * ``optimizations`` *integer* 此处的标记表示哪种优化方式将被应用(默认是``-1`` —— 所有的优化设置都被弃用;设置为``0``,则禁用)。 ##加载器(Loader) 加载器负责从文件系统之类的来源加载模板。 ###编译缓存 所有模板加载器都可以在文件系统中缓存编译后的模板以便将来重用。模板只需要编译一次就能为Twig提速不少,并且如果使用了APC之类的加速器,还能带来更大的性能提升。查看前文中``Twig_Environment``的``cache``和``auto_reload``两个选项了解更多信息。 ###内置的加载器 这是一个由Twig提供的内置加载器列表: ####``Twig_Loader_Filesystem`` > 在 Twig 1.10版中,加入了 ``prependPath()``,并支持命名空间。 ``Twig_Loader_Filesystem``从文件系统加载模板。这个加载器可以从文件系统目录中找到模板,这是加载模板的最佳方式: $loader = new Twig_Loader_Filesystem($templateDir); 它还可以从由多个目录组成的数组中寻找模板: $loader = new Twig_Loader_Filesystem(array($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', array()); ####``Twig_Loader_Array`` 使用``Twig_Loader_Array``从PHP数组加载模板。它被传递一个绑定模板名称的字符串数组: ~~~ $loader = new Twig_Loader_Array(array( 'index.html' => 'Hello {{ name }}!', )); $twig = new Twig_Environment($loader); echo $twig->render('index.html', array('name' => 'Fabien')); ~~~ 这个加载器对于单元测试非常有用。它还可以用于将所有模板存放在单个PHP文件内的小型项目,的确可以这样做。 提示: > 在使用带有缓存极致的 ``Array`` 或 ``String`` 加载器时,你应当明白新的缓存键时在每次模板内容改变时生成的(缓存键是指模板的源代码)。如果不希望缓存失控地增加,你需要注意自行清除旧的缓存。 ####``Twig_Loader_Chain`` ``Twig_Loader_Chain`` 将模板的加载工作委派给其他加载器: $loader1 = new Twig_Loader_Array(array( 'base.html' => '{% block content %}{% endblock %}', )); $loader2 = new Twig_Loader_Array(array( 'index.html' => '{% extends "base.html" %}{% block content %}Hello {{ name }}{% endblock %}', 'base.html' => 'Will never be loaded', )); $loader = new Twig_Loader_Chain(array($loader1, $loader2)); $twig = new Twig_Environment($loader); 在查找模板时,Twig会轮流尝试每个加载器,并在找到模板时立即返回。前面的例子中,在渲染 ``index.html``模板时,Twig 会使用``$loader2``来加载它,但``base.html``模板会从``$loader1``中被加载。 ``Twig_Loader_Chain`` 能接收任意实现了 ``Twig_LoaderInterface``接口的加载器。 注意: > 你还可以使用``addLoader()`` 方法来添加加载器。 ###创建你自己的加载器 所有的加载器都实现了 ``Twig_LoaderInterface``: interface Twig_LoaderInterface { /** * Gets the source code of a template, given its name. * * @param string $name string The name of the template to load * * @return string The template source code */ function getSource($name); /** * Gets the cache key to use for the cache for a given template name. * * @param string $name string The name of the template to load * * @return string The cache key */ 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 */ function isFresh($name, $time); } 考虑到最后修改的时间,如果当前被缓存的模板仍然是最新的,则 ``isFresh()`` 方法必须返回 ``true``,否则返回``false``。 提示: > 从Twig 1.11.0 开始,你还可以实现 ``Twig_ExistsLoaderInterface``,让你的加载器在使用链式加载器时更快速。 ##使用扩展 Twig的扩展程序其实是为Twig添加新特性的包(package)。使用扩展跟使用 ``addExtension()`` 方法一样简单: $twig->addExtension(new Twig_Extension_Sandbox()); Twig comes bundled with the following extensions: * *Twig_Extension_Core*: 定义Twig的所有核心特性。 * *Twig_Extension_Escaper*: 为代码块添加自动化输出转义以及是否转义的可能性。 * *Twig_Extension_Sandbox*: 为默认的Twig环境添加沙河模式,使其能安全地评估未受信任的代码。 * *Twig_Extension_Profiler*: 启用内置的Twig分析器(Twig 1.18版本以上可用)。 * *Twig_Extension_Optimizer*: 在编译前优化节点树。 上面的 core, escaper, 以及 optimizer 扩展不是必须要添加到 Twig环境中,因为它们都是默认已被注册的。 ##内置的扩展 这一节介绍由内置扩展添加的特性 提示: > 这一章是关于扩展Twig的,阅读这一章学习如何创建你自己的扩展。 ###核心扩展``core`` ``core``扩展定义Twig的所有核心特性: * Tags * Filters * Functions * Tests ###转义扩展``Escaper`` 使用 ``escaper`` 扩展,为Twig添加动态输出转义。它定义了``autoescape``标签和 ``raw``过滤器。 在创建转义扩展时,你可以打开或者关闭全局输出转义策略: $escaper = new Twig_Extension_Escaper('html'); $twig->addExtension($escaper); 如果将其设置为 ``html``,模板中的所有变量都会被转义(使用``html``转义策略), 除非是用了 ``raw`` 过滤器。: {{ article.to_html|raw }} 还可以使用 ``autoescape`` 标签来局部地改变转义模式(查看 autoescape 文档,了解Twig 1.8以上的语法): {% autoescape 'html' %} {{ var }} {{ var|raw }} {# var won't be escaped #} {{ var|escape }} {# var won't be double-escaped #} {% endautoescape %} 警告: > ``autoescape`` 对引入的文件没有影响。 像下面这样实现转义规则: * 在模板中直接用作变量或过滤器参数的字面值(包括整型数、布尔值、数组等)**从不**被自动转义: {{ "Twig<br />" }} {# won't be escaped #} {% set text = "Twig<br />" %} {{ text }} {# will be escaped #} * 被标注为安全的,其结果总是一个字面值或变量的表达式,**从不**被自动转义: {{ foo ? "Twig<br />" : "<br />Twig" }} {# won't be escaped #} {% set text = "Twig<br />" %} {{ foo ? text : "<br />Twig" }} {# will be escaped #} {% set text = "Twig<br />" %} {{ foo ? text|raw : "<br />Twig" }} {# won't be escaped #} {% set text = "Twig<br />" %} {{ foo ? text|escape : "<br />Twig" }} {# the result of the expression won't be escaped #} * 转义应用于打印之前,和其他过滤器应用之后: {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #} * `raw`过滤器只能用在过滤器链的结尾: {{ var|raw|upper }} {# will be escaped #} {{ var|upper|raw }} {# won't be escaped #} * 如果当前上下文(context,例如``html``或``js``)的过滤器链中最后一个过滤器被标注为安全,那么自动转义不会被应用。``escape`` 和 ``escape('html')`` 用于将 HTML标注为安全,``escape('js')`` 用于将JavaScript标注为安全,``raw``可以将任意内容标注为安全: {% 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`` ``sandbox``扩展用于评估未被信任的代码。禁止访问不安全的属性和方法。沙盒的安全性由一个policy实例进行管理。默认地,Twig带有一个policy类: ``Twig_Sandbox_SecurityPolicy``。这个类允许你为标签、属性、方法添加白名单: $tags = array('if'); $filters = array('upper'); $methods = array( 'Article' => array('getTitle', 'getBody'), ); $properties = array( 'Article' => array('title', 'body'), ); $functions = array('range'); $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions); 基于上述配置,安全策略仅允许使用``if``标签和``upper``过滤器。而且,模板只能用``Article``对象调用 ``getTitle()`` 和 ``getBody()`` 方法。其它的用法都被禁止,并会生成一个 ``Twig_Sandbox_SecurityError`` 异常。 策略对象 policy 是沙盒构造函数的第一个参数: $sandbox = new Twig_Extension_Sandbox($policy); $twig->addExtension($sandbox); 默认地,沙盒模式是被禁用了的。但在使用``sandbox``标签引入未被信任的模板代码时,应当启用沙盒模式: {% sandbox %} {% include 'user.html' %} {% endsandbox %} 可以通过将 extension 构造函数的第二个参数设置为``true``,将所有模板放入沙盒中: $sandbox = new Twig_Extension_Sandbox($policy, true); ###分析器扩展``profiler`` > 在 Twig 1.18 添加了分析器扩展 分析器扩展``profiler``为Twig模板启用了分析器;由于它增加了一些开销,所以只能在开发环境中使用它: $profile = new Twig_Profiler_Profile(); $twig->addExtension(new Twig_Extension_Profiler($profile)); $dumper = new Twig_Profiler_Dumper_Text(); echo $dumper->dump($profile); 一份分析结果包含了模板、代码块、以及指令执行的时间和内存消耗等信息 可以将分析数据转换成 [Blackfire.io](https://blackfire.io/) 兼容的格式: $dumper = new Twig_Profiler_Dumper_Blackfire(); file_put_contents('/path/to/profile.prof', $dumper->dump($profile)); 将分析结果上传,使其可视化(需要先[创建一个账号](https://blackfire.io/signup)): blackfire --slot=7 upload /path/to/profile.prof ###优化器扩展``potimizer`` 优化器扩展 ``optimizer`` 在编译前优化节点树: $twig->addExtension(new Twig_Extension_Optimizer()); 默认地,所有优化项都是开启了的。你可以通过将某些你想要启用的优化项传递给构造函数,以开启它们: $optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR); $twig->addExtension($optimizer); Twig 支持以下优化项: * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_ALL``, 启用所有优化项(这是默认值) * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_NONE``, 禁用所有优化项。这会减少编译时间,但会增加执行时间和内存消耗。 * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR``, 如果可以,则通过移除``loop``变量的创建来优化``for``标签。 * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_RAW_FILTER``, 如果可能,则移除 ``raw`` 过滤器。 * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_VAR_ACCESS``, 如果可能,则简化已编译模板中变量的创建和访问。 ##异常 Twig 可以抛出异常: * ``Twig_Error``: 所有错误的基础异常》 * ``Twig_Error_Syntax``: 抛出此异常,表示模板语法存在问题。 * ``Twig_Error_Runtime``: 在运行时刻(runtime)重现了某个错误,则抛出这个异常(比如某个过滤器并不存在) * ``Twig_Error_Loader``: 在模板加载过程中重现了某个错误,则抛出这个异常。 * ``Twig_Sandbox_SecurityError``: 在沙盒模式模板中调用了某个未被允许的标签、过滤器或方法时,抛出这个异常。