食谱
显示弃用通知
已弃用的特性会生成弃用通知(通过调用trigger_error ()
PHP函数)。默认情况下,它们是静音的,永远不会显示或记录。
要从模板中删除所有不推荐的功能,请编写并运行如下所示的脚本:
1 2 3 4 5 6 7
require_once__DIR__.“/供应商/ autoload.php”;$嫩枝= create_your_twig_env ();$的用法=新\树枝\ Util \ DeprecationCollector ($嫩枝);print_r ($的用法->collectDir (__DIR__.“/模板”));
的collectDir ()
方法编译目录中找到的所有模板,捕获弃用通知并返回它们。
提示
如果模板没有存储在文件系统中,请使用收集()
方法相反。收集()
需要一个可否认的
它必须返回模板名称作为键和模板内容作为值(由\树枝\ Util \ TemplateDirIterator
).
然而,这段代码不会找到所有的弃用(就像使用一些已弃用的Twig类一样)。为了捕获所有的通知,注册一个自定义错误处理程序,如下所示:
1 2 3 4 5 6 7 8 9 10
$的用法= [];set_error_handler (函数($类型,$味精)使用(&$的用法){如果(E_USER_DEPRECATED = = =$类型) {$的用法[] =$味精;}});//运行应用程序print_r ($的用法);
注意,大多数弃用通知都是在此期间触发的编译,因此当模板已经缓存时不会生成它们。
提示
如果您想管理来自PHPUnit测试的弃用通知,请查看欧宝娱乐app下载地址symfony / phpunit-bridge软件包,这简化了过程。
使布局有条件
使用Ajax意味着相同的内容有时按原样显示,有时用布局进行装饰。由于Twig布局模板名称可以是任何有效的表达式,您可以传递一个变量,该变量的值为真正的
当通过Ajax发出请求时,选择相应的布局:
1 2 3 4 5
{%扩展请求。ajax吗?"base_ajax.html": "base.html" %}{%块内容%}这就是要显示的内容。{%endblock%}
使Include动态
当包含一个模板时,它的名称不需要是字符串。例如,名称可以依赖于变量的值:
1
{%包括Var ~ '_foo.html' %}
如果var
计算结果为指数
,index_foo.html
模板将被呈现。
事实上,模板名可以是任何有效的表达式,例如:
1
{%包括var |默认的('index') ~ '_foo.html' %}
重写一个也扩展自身的模板
模板有两种自定义方式:
- 继承:模板扩展父模板并覆盖一些块;
- 更换:如果你使用文件系统加载器,Twig加载它在配置目录列表中找到的第一个模板;在目录中找到的模板替换另一个来自列表中更远的目录。
但如何将两者结合起来呢?取代一个模板,也扩展自己(又名模板在目录中进一步的列表)?
假设你的模板都是从这两个地方加载的…/模板/ mysite
而且…/模板/违约
按这个顺序。的page.twig
模板,存储在…/模板/违约
全文如下:
1 2 3 4 5
{#页面。树枝#}{%扩展”布局。树枝“%}{%块内容%}{%endblock%}
您可以通过放入同名文件来替换此模板…/模板/ mysite
.如果您想扩展原始模板,您可能会编写以下代码:
1 2
{#页面。嫩枝in .../templates/mysite #}{%扩展”页面。嫩枝" %}{# from…/模板/违约#}
然而,这将不起作用,因为Twig将始终从加载模板…/模板/ mysite
.
事实证明,这是可以实现的,只需在模板目录的末尾添加一个目录,它是所有其他目录的父目录:…/模板
在我们这里。这使得系统中的每个模板文件都可以唯一寻址。大多数情况下,你会使用“普通”路径,但在特殊情况下,想要扩展一个覆盖模板本身的版本,我们可以在extends标签中引用它的父模板的完整的、明确的模板路径:
1 2
{#页面。嫩枝in .../templates/mysite #}{%扩展“默认/页面。树枝“%}{# from…/模板#}
请注意
这个食谱的灵感来自以下Django wiki页面:https://code.djangoproject.com/wiki/ExtendingTemplates
自定义语法
Twig允许对块分隔符进行一些语法定制。这是不建议使用此功能,因为模板将与您的自定义语法绑定。但是对于特定的项目,更改默认值是有意义的。
要更改块分隔符,您需要创建自己的lexer对象:
1 2 3 4 5 6 7 8 9
$嫩枝=新\树枝\环境(…);$词法分析程序=新、树枝、词法分析程序($嫩枝, (“tag_comment”= > [“{#”,“#}”),“tag_block”= > [{%的,“%}”),“tag_variable”= > [“{{”,‘}}),“插值”= > [“#{”,“}”)));$嫩枝->setLexer ($词法分析程序);
下面是一些模拟其他模板引擎语法的配置示例:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
// Ruby erb语法$词法分析程序=新、树枝、词法分析程序($嫩枝, (“tag_comment”= > [“< % #”,' % >),“tag_block”= > [“< %”,' % >),“tag_variable”= > [“< % =”,' % >)));// SGML注释语法$词法分析程序=新、树枝、词法分析程序($嫩枝, (“tag_comment”= > [“< !--#',“- >”),“tag_block”= > [“< !——”,“- >”),“tag_variable”= > [“${”,“}”)));//聪明的喜欢$词法分析程序=新、树枝、词法分析程序($嫩枝, (“tag_comment”= > [“{*”,“*}”),“tag_block”= > [“{”,“}”),“tag_variable”= > [‘{$’,“}”)));
使用动态对象属性
当Twig遇到一个变量article.title
,它试图找到一个标题
香港的公共财产文章
对象。
如果属性不存在,而是动态定义的,它也可以工作__get ()
方法;还需要实现__isset ()
Magic方法如下面的代码片段所示:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
类文章{公共函数__get($的名字){如果(“标题”= =$的名字) {返回“标题”;}//抛出某种错误}公共函数__isset($的名字){如果(“标题”= =$的名字) {返回真正的;}返回假;}}
在嵌套循环中访问父上下文
有时,在使用嵌套循环时,需要访问父上下文。类始终可以访问父上下文loop.parent
变量。例如,如果你有以下模板数据:
1 2 3 4 5 6
$数据= (“主题”= > [“人类”= > [“主题1的信息1”,“主题1的信息2”),“话题二”= > [“主题2的信息1”,“主题2的信息2”],],];
和下面的模板显示所有主题中的所有消息:
1 2 3 4 5 6
{%为Topic, Topic %}中的消息*{{循环。指数}}:{{topic}}{%为messages %}中的消息-{{循环。父.loop。指数}}.{{循环。指数}}:{{message}}{%endfor%}{%endfor%}
输出将类似于:
1 2 3 4 5 6
* 1: topic1 - 1.1:主题1的消息1 - 1.2:主题1的消息2 * 2:topic2 - 2.1:主题2的消息1 - 2.2:主题2的消息2
在内循环中,loop.parent
变量用于访问外部上下文。这是电流的指标主题
在外部for循环中定义的loop.parent.loop.index
变量。
动态定义未定义的函数、过滤器和标签
3.2
的registerUndefinedTokenParserCallback ()
方法在Twig 3.2中添加。
当一个函数/过滤器/标签没有定义时,Twig默认抛出一个\ \ SyntaxError树枝\错误
例外。但是,它也可以调用回调(任何有效的PHP可调用对象),返回函数/过滤器/标记。
对于标记,用寄存器回调registerUndefinedTokenParserCallback ()
.对于过滤器,用寄存器回调registerUndefinedFilterCallback ()
.对于函数,请使用registerUndefinedFunctionCallback ()
:
1 2 3 4 5 6 7 8 9
//自动注册所有原生PHP函数为Twig函数//永远不要在项目中这样做,因为它不安全$嫩枝->registerUndefinedFunctionCallback (函数($的名字){如果(function_exists ($的名字)) {返回新\树枝\ TwigFunction ($的名字,$的名字);}返回假;});
如果可调用对象不能返回有效的函数/过滤器/标记,则必须返回假
.
如果你注册了多个回调,Twig将依次调用它们,直到其中一个没有返回假
.
提示
由于函数/过滤器/标记的解析是在编译期间完成的,所以注册这些回调时没有开销。
验证模板语法
当模板代码由第三方提供时(例如通过web界面),在保存模板之前验证模板语法可能会很有趣。如果模板代码存储在美元的模板
变量,你可以这样做:
1 2 3 4 5 6 7
试一试{$嫩枝->解析($嫩枝->tokenize (新\树枝\源($模板)));// $template是有效的}抓(\ \ SyntaxError树枝\错误$e) {// $template包含一个或多个语法错误}
方法迭代一组文件时,可以将文件名传递给标记()
方法获取异常消息中的文件名:
1 2 3 4 5 6 7 8 9
foreach($文件作为$文件) {试一试{$嫩枝->解析($嫩枝->tokenize (新\树枝\源($模板,$文件->getFilename (),$文件)));// $template是有效的}抓(\ \ SyntaxError树枝\错误$e) {// $template包含一个或多个语法错误}}
请注意
这个方法不会捕获任何违反沙盒策略的行为,因为该策略是在模板呈现期间强制执行的(因为Twig需要上下文来进行一些检查,比如对象上允许的方法)。
启用OPcache时刷新修改的模板
当使用OPcache with时opcache.validate_timestamps
设置为0
, Twig缓存启用和自动重载禁用,清除模板缓存不会更新缓存。
为了解决这个问题,强制Twig使字节码缓存无效:
1 2 3 4
$嫩枝=新\树枝\环境($加载程序, (“缓存”= >新缓存\树枝\ \ FilesystemCache (“/一些/缓存/路径”, \树枝\ \ FilesystemCache缓存::FORCE_BYTECODE_INVALIDATION),/ /……]);
重用有状态节点访问者
将访问者附加到\树枝\环境
例如,Twig使用它来访问所有它编译模板。如果需要保留一些状态信息,则可能需要在访问新模板时重置它。
这可以通过以下代码实现:
12 3 4 5 6 7 8 9 10 11 12 13
受保护的$someTemplateState= [];公共函数enterNode(\ \树枝\节点$节点, \树枝\环境$env){如果($节点运算符\ \树枝\节点ModuleNode) {//当我们进入一个新模板时重置状态$这->someTemplateState = [];}/ /……返回$节点;}
使用数据库存储模板
如果您正在开发CMS,模板通常存储在数据库中。这个配方为您提供了一个简单的PDO模板加载器,您可以将其作为自己的模板加载器的起点。
首先,让我们在内存中创建一个临时的SQLite3数据库来使用:
1 2 3 4 5 6 7 8 9 10
$胸径=新PDO (的sqlite::内存:);$胸径->exec (创建表模板(名称字符串,源字符串,last_modified INTEGER));$基地='{% block内容%}{% endblock %}';$指数='{%扩展' base。树枝“%}{%块内容%}Hello {{ name }}{% endblock %} ';$现在= ();$胸径->准备('INSERT INTO templates (name, source, last_modified) VALUES (?, ?, ?)')->执行([“base.twig”,$基地,$现在]);$胸径->准备('INSERT INTO templates (name, source, last_modified) VALUES (?, ?, ?)')->执行([“index.twig”,$指数,$现在]);
我们创建了一个简单的模板
表中包含两个模板:base.twig
而且index.twig
.
现在,让我们定义一个能够使用这个数据库的加载器:
12 34 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
类DatabaseTwigLoader实现了\嫩枝\加载程序\LoaderInterface{受保护的$胸径;公共函数__construct(PDO$胸径){$这->胸径=$胸径;}公共函数getSourceContext(字符串$的名字):源{如果(假= = =$源=$这->getValue (“源”,$的名字)) {扔新\树枝\错误\ LoaderError (sprintf (“模板“%s”不存在。”,$的名字));}返回新\树枝\源($源,$的名字);}公共函数存在(字符串$的名字){返回$的名字= = =$这->getValue (“名字”,$的名字);}公共函数getCacheKey(字符串$的名字):字符串{返回$的名字;}公共函数isFresh(字符串$的名字, int$时间):保龄球{如果(假= = =$lastModified=$这->getValue (“last_modified”,$的名字)) {返回假;}返回$lastModified< =$时间;}受保护的函数getValue($列,$的名字){$某事=$这->胸径->准备(“选择”.$列.从模板WHERE name =:name);$某事->执行([”:“= >(字符串)$的名字]);返回$某事->fetchColumn ();}}
最后,这里有一个如何使用它的例子:
1 2 3 4
$加载程序=新DatabaseTwigLoader ($胸径);$嫩枝=新\树枝\环境($加载程序);回声$嫩枝->呈现(“index.twig”, (“名字”= >“法”]);
使用不同的模板源
这个食谱是上一个食谱的延续。即使您将提供的模板存储在数据库中,您也可能希望在文件系统中保留原始/基本模板。可以从不同来源加载模板时,需要使用\树枝\装载机\ ChainLoader
加载程序。
正如您在前面的菜谱中看到的,我们引用模板的方式与使用常规文件系统加载器时完全相同。这是能够混合和匹配来自数据库、文件系统或任何其他加载器的模板的关键:模板名应该是一个逻辑名称,而不是来自文件系统的路径:
1 2 3 4 5 6 7 8 9
$loader1=新DatabaseTwigLoader ($胸径);$loader2=新\树枝\装载机\ ArrayLoader ([“base.twig”= >'{% block内容%}{% endblock %}']);$加载程序=新\树枝\装载机\ ChainLoader ([$loader1,$loader2]);$嫩枝=新\树枝\环境($加载程序);回声$嫩枝->呈现(“index.twig”, (“名字”= >“法”]);
既然base.twig
Templates是在一个数组加载器中定义的,你可以从数据库中删除它,其他的一切仍然会像以前一样工作。
从字符串中加载模板
方法从模板加载存储在字符串中的模板template_from_string
函数(通过\树枝\ \ StringLoaderExtension延伸
扩展):
1
{{包括(template_from_string("Hello {{name}}"))}}
在PHP中,也可以通过via加载存储在字符串中的模板\树枝\环境::createTemplate ()
:
1 2
$模板=$嫩枝->createTemplate ('hello {{name}}');回声$模板->呈现([“名字”= >“法”]);
在相同的模板中使用Twig和AngularJS
在同一个文件中混合使用不同的模板语法并不推荐,因为AngularJS和Twig在语法中使用了相同的分隔符:{{
而且}}
.
不过,如果你想在同一个模板中使用AngularJS和Twig,有两种方法可以让它工作,这取决于你需要在模板中包含的AngularJS的数量:
- 元素来包装AngularJS的section,从而转义AngularJS的分隔符
{% verbatim %}
标记或通过转义每个分隔符{{' {{'}}
而且{{'}}'}}
; 更改其中一个模板引擎的分隔符(取决于您上次引入的引擎):
对于AngularJS,使用
interpolateProvider
服务,例如在模块初始化时:- 角。module('myApp', []).config(function($interpolateProvider) {
- 美元interpolateProvider.startSymbol (' {[') .endSymbol ()});
});
控件更改分隔符
tag_variable
词法分析程序的选项:1 2 3
$env->setLexer (新、树枝、词法分析程序($env, (“tag_variable”= > [”{[',']}’)));