扩展 Twig
原文:Extending Twig 翻译:小虾米(QQ:509129)
扩展 Twig
Twig可以以多种方式扩展;您可以添加额外的标记、过滤器、测试、操作符、全局变量和函数。甚至可以将解析器本身扩展到节点访问者。
本章的第一部分描述了如何轻松地扩展Twig。如果您想要在不同的项目中重用您的变更,或者您想要与其他项目共享它们,那么您应该在下面的小节中创建一个扩展。
在不创建扩展的情况下扩展Twig时,当PHP代码更新时,Twig将无法重新编译您的模板。要实时查看更改,要么禁用模板缓存,要么将代码打包为扩展(参见本章的下一节)。
在扩展Twig之前,您必须了解所有可能的扩展点和何时使用它们之间的差异。
首先,记住Twig有两种主要的语言结构:
{{ }}: 用于打印表达式的结果;
{% %}: 用于执行语句。
为了理解为什么Twig暴露了这么多的扩展点,让我们看看如何实现一个Lorem ipsum生成器(它需要知道生成的单词的数量)。
您可以使用lipsum标签:
{% lipsum 40 %}这是可行的,但是用一个标签来表示lipsum并不是一个好主意,至少有三个主要原因:
lipsum不是一种语言结构;
标签输出的东西;
标签不灵活,因为您不能在表达式中使用它:
实际上,您很少需要创建标记;这是好消息,因为标签是Twig最复杂的扩展点。
现在,让我们用一个lipsum过滤器:
同样的,它是可行的,但它看起来很奇怪。过滤器将传递的值转换为其他东西,但是这里我们使用值来表示生成的单词的数量(因此,40是过滤器的参数,而不是我们想要转换的值)。
接下来,让我们使用一个lipsum函数:
我们开始吧。对于这个特定的示例,创建函数是使用的扩展点。你可以在任何地方使用这个表达方式:
最后,您还可以使用一个全局对象,使用一个能够生成lorem ipsum文本的方法:
作为经验法则,使用函数来频繁地使用特性和全局对象。
当你想扩展Twig时,请记住以下几点:
什么?
实施困难吗?
多久一次
什么时候?
宏(macro)
不
频繁
内容生成
全局变量(global)
不
频繁
Helper对象
函数(function)
不
频繁
内容生成
过滤器(filter)
不
频繁
值转换
标签器(tag)
复杂
稀有
DSL语言结构
测试器(test)
不
稀有
布尔判定
操作符(operator)
不
稀有
值转换
全局变量(Globals)
然后可以在模板中的任何地方使用文本变量:
过滤器(Filters)
创建过滤器器就像将名称与PHP可调用的名称关联一样简单:
传递给Twig_Filter构造函数的第一个参数是您将在模板中使用的过滤器的名称,第二个参数是与它关联的PHP。 然后,将过滤器添加到您的Twig环境中:
下面是如何在模板中使用它:
当被Twig调用时,PHP callable将过滤器的左侧(在管道|之前)作为第一个参数,并且将额外的参数传递给过滤器(在括号()中)作为额外的参数。 例如,以下代码:
被编译成如下内容:
Twig_Filter使用一个数组选项作为最后的参数:
Environment-aware过滤器(Environment-aware Filters)
如果您想访问过滤器中的当前环境实例,请将needs_environment选项设置为true;Twig将把当前环境作为第一个参数传递给筛选器:
Context-aware Filters(Context-aware过滤器)
如果您想要访问过滤器中的当前上下文,请将needs_context选项设置为true;Twig将把当前上下文作为第一个参数传递到过滤器调用(或者第二个If needs_environment也设置为true):
自动转义(Automatic Escaping)
如果可以自动转义,那么过滤器的输出可能在打印之前就可以转义。如果您的过滤器充当了一个转义者(或者显式输出HTML或JavaScript代码),那么您将希望打印原始输出。在这种情况下,设置is_safe选项:
一些过滤器可能需要处理已经安全的输入,例如在添加(安全)HTML标签到最初不安全的输出时。在这种情况下,设置pre_escape选项,在它运行通过过滤器之前,要转义输入数据:
可变的过滤器(Variadic Filters)
当一个过滤器应该接受任意数量的参数时,将is_variadic选项设置为true;Twig将把额外的参数作为数组的最后一个参数传递给过滤器。
请注意,传递给变量过滤器的命名参数不能检查有效性,它们会自动终止在选项数组中。
动态过滤器(Dynamic Filters)
包含特殊*字符的过滤器名称是一个动态过滤器,它可以是任意字符串:
下面的过滤器将与上面定义的动态过滤器匹配:
product_path
category_path
动态过滤器可以定义多个动态部件:
过滤器将在正常的过滤器参数之前接收所有动态部件值,但是在环境和上下文之后。例如,对“foo”| a_path_b()的调用将导致将下列参数传递给过滤器:(“a”、“b”、“foo”)。
弃用的过滤器(Deprecated Filters)
您可以通过将弃用选项设置为true来标记过滤器。您还可以提供一个替代的过滤器,它在有意义的情况下替代已弃用的过滤器:
当一个过滤器被弃用时,Twig会在编译一个使用它的模板时发出一个弃用通知。有关更多信息,请参见显示弃用通知。
函数(Functions)
函数的定义与过滤器完全相同,但您需要创建Twig_Function的一个实例:
函数支持与过滤器相同的特性,除了pre_escape和preserves_safety选项之外。
测试器(Tests)
测试的定义与过滤器和函数的定义完全相同,但是您需要创建Twig_Test的一个实例:
测试允许您创建自定义应用程序特定逻辑来评估布尔条件。作为一个简单的例子,让我们创建一个Twig测试来检查对象是否为“红色”:
测试函数应该总是返回true / false。
在创建测试时,您可以使用node_class选项来提供自定义的测试编译。如果您的测试可以被编译成PHP原语,那么这是很有用的。这是许多在Twig中构建的测试所使用的:
上面的示例演示如何创建使用节点类的测试。节点类可以访问一个叫“节点”的子节点。此子节点包含正在测试的值。当奇数过滤器用于代码,如:
节点子节点将包含my_value的表达式。基于节点的测试还可以访问参数节点。这个节点将包含提供给您测试的各种其他参数。
如果您想要将变量的位置或命名参数传递给测试,那么将is_variadic选项设置为true。测试还支持动态名称特性作为过滤器和函数。
标签器(Tags)
像Twig这样的模板引擎最令人兴奋的特性之一是定义新语言结构的可能性。这也是最复杂的特性,因为您需要了解Twig的内部工作是如何工作的。
让我们创建一个简单的set标签,允许在模板内定义简单的变量。标签可以使用如下:
set标记是核心扩展的一部分,因此总是可用的。内置版本的功能稍微强大一些,默认情况下支持多个赋值(cf.模板设计器章节以获取更多信息)。
定义一个新标签需要三个步骤:
定义令牌解析器类(负责解析模板代码);
定义一个节点类(负责将解析的代码转换为PHP);
注册标记。
注册一个新的标签(Registering a new tag)
添加一个标记就像在Twig_Environment实例上调用addTokenParser方法一样简单:
定义一个令牌解析器(Defining a Token Parser)
现在,让我们看看这个类的实际代码:
getTag()方法必须返回我们要解析的标记,这里设置。
每当解析器遇到set标签时,都会调用parse()方法。它应该返回表示该节点的Twig_Node实例(Project_Set_Node调用创建在下一节中解释)。
由于可以从令牌流中调用的一系列方法($ this - >解析器- > getStream()),解析过程简化了。
getCurrent():获取流中的当前标记。
next():移动到流中的下一个标记,但返回旧的。
test($ type)、test($value)或test($type,$value):确定当前标记是否为特定类型或值(或两者)。值可能是几个可能值的数组。
expect($type,$value[$message]):如果当前的令牌不是给定的类型/值,则会抛出语法错误。否则,如果类型和值是正确的,则返回令牌并将流移动到下一个令牌。
look():查看下一个令牌而不使用它。
解析表达式是通过调用parseExpression()来完成的,就像我们为set标签所做的那样。
阅读现有的TokenParser类是了解解析过程所有细节的最好方法。
定义一个节点(Defining a Node)
Project_Set_Node类本身相当简单:
编译器实现了一个流体接口,并提供一些方法,帮助开发人员生成漂亮且可读的PHP代码:
subcompile():编译一个节点。
raw():按原样写给定的字符串。
write():在每一行的开头添加缩进来写给定的字符串。
string():编写引用的字符串。
repr():写一个给定值的PHP表示(参见Twig_Node_For的使用示例)。
addDebugInfo():添加与当前节点相关的原始模板文件的行作为注释。
indent():对生成的代码进行缩进(参见Twig_Node_Block作为一个使用例)。
outdent():输出生成的代码(参见Twig_Node_Block以获得使用示例)。
穿件一个扩展(Creating an Extension)
编写扩展的主要动机是将经常使用的代码转移到可重用的类中,例如为国际化添加支持。扩展可以定义标记、筛选器、测试、操作符、全局变量、函数和节点访问者。
大多数时候,为您的项目创建一个扩展是很有用的,它可以添加到Twig您想要所有特定标记和过滤器。
当将代码打包为扩展时,Twig足够智能,可以在每次更改时重新编译您的模板(当启用auto_reload时)。
在编写自己的扩展之前,请查看Twig官方扩展存储库:http://github.com/twigphp/twig扩展。
扩展是实现以下接口的类:
为了保持您的扩展类干净和精简,从内置的Twig_Extension类继承而不是实现接口,因为它为所有方法提供了空实现:
当然,这个扩展现在什么也不做。我们将在下一节中对其进行定制。
Twig不关心在文件系统中保存扩展的位置,因为所有扩展都必须在模板中显式注册。
您可以在主环境对象上使用addExtension()方法注册一个扩展:
Twig核心扩展是扩展工作的很好的例子。
全局变量(Globals)
全局变量可以通过getGlobals()方法在扩展中注册:
函数(Functions)
函数可以通过getFunctions()方法在扩展中注册:
过滤器(Filters)
要向扩展添加过滤器,您需要重写getfilter()方法。此方法必须返回一个过滤器数组,以添加到Twig环境:
标签器(Tags)
在扩展中添加标记可以通过覆盖getTokenParsers()方法来完成。此方法必须返回一个标记数组,以添加到Twig环境:
在上面的代码中,我们添加了一个新标签,由Project_Set_TokenParser类定义。Project_Set_TokenParser类负责解析标记并将其编译为PHP。
操作符(Operators)
getOperators()方法允许添加新操作符。下面是如何添加!| |,和& &运算符:
测试器(Tests)
getTests()方法让您添加新的测试函数:
Definition vs Runtime
Twig过滤器、函数和测试运行时实现可以定义为任何有效的PHP调用:
函数/静态方法(functions/static methods):简单实现和快速(由所有Twig核心扩展使用);但是运行时很难依赖于外部对象;
闭包(closures):简单的实现;
对象方法(object methods):如果运行时代码依赖于外部对象,则更加灵活和必需。
使用方法的最简单的方法是在扩展本身上定义它们:
这是非常方便的,但不推荐,因为它使模板编译依赖于运行时依赖关系,即使它们不需要(例如作为连接到数据库引擎的依赖关系)。
您可以通过在环境中注册一个Twig_RuntimeLoaderInterface实例来轻松地将扩展定义与运行时实现分离,该实例知道如何实例化这样的运行时类(运行时类必须是可自动的):
Twig附带一个PSR-11兼容运行时加载程序(Twig_ContainerRuntimeLoader)。
现在可以将运行时逻辑转移到一个新的Project_Twig_RuntimeExtension类,并直接在扩展中使用它:
负载(Overloading)
为了超载已经定义的过滤器、测试、操作符、全局变量或函数,将其重新定义为扩展,并尽可能晚地注册(订单事项):
在这里,我们已经使用自定义的日期过滤器重载了内置的日期过滤器。
如果您在Twig_Environment上执行相同的操作,请注意它比任何其他注册扩展都优先:
注意,不建议重写内置的Twig元素,因为它可能会令人困惑。
测试一个扩展(Testing an Extension)
函数测试(Functional Tests)
通过在测试目录中创建以下文件结构,您可以为扩展创建函数测试:
IntegrationTest.php文件应该是这样的:
可以在Twig仓库tests/Twig/Fixtures目录中找到fixture示例。
节点测试(Node Tests)
测试节点访问者可能是复杂的,因此从Twig_Test_NodeTestCase扩展您的测试用例。示例可以在Twig仓库tests/Twig/Node目录中找到。
Last updated
Was this helpful?