延长枝
树枝可以扩展在许多方面;您可以添加额外的标签,过滤器,测试,操作符,全局变量和函数。你甚至可以用节点扩展解析器本身游客。
请注意
本章的第一部分描述了如何扩展树枝。如果你想重复使用您的更改在不同项目或如果你想与他人分享,您应该创建一个扩展如以下部分所述。
谨慎
当延长枝没有创建一个扩展,树枝无法重新编译PHP代码时你的模板更新。看到你的实时变化,禁用模板缓存或代码打包成一个扩展(参见本章接下来的部分)。
延长枝之前,你必须了解所有不同的可能的扩展点之间的差异以及何时使用它们。
首先,记住,小枝有两个主要语言结构:
{{}}
:用于打印一个表达式的结果评价;{% %}
:用于执行语句。
要理解为什么树枝暴露太多的扩展点,让我们看看如何实现Lorem ipsum发电机(它需要知道的字数来生成)。
您可以使用一个lipsum
标签:
1
{%lipsum40%}
工作,但是使用一个标签lipsum
不是一个好主意至少三个主要的原因:
lipsum
不是一个语言结构;- 标签输出的东西;
标签不灵活,你不能使用它在一个表达式:
1
{{一些文本的~ {% lipsum 40%} ~一些文本的}}
事实上,你很少需要创建标签;这是个好消息,因为标签是最复杂的欧宝app在哪里找扩展点。
现在,让我们使用lipsum
过滤器:
1
{{40 | lipsum}}
再一次,它的工作原理。但一个过滤器应该将值传递给别的东西。在这里,我们使用的值来表示的字数来生成(所以,40
是过滤的论点,而不是我们想要的值变换)。
接下来,让我们用一个lipsum
函数:
1
{{lipsum (40)}}
我们开始吧。对于这个特定的例子中,创建一个函数要使用的扩展点。你可以在任何地方使用它接受一个表达式:
1 2 3
{{一些文本的~ lipsum(40) ~的一些文本}}{%集lipsum = lipsum (40) %}
最后,你也可以使用全球对象与方法能够生成lorem ipsum文本:
1
{{text.lipsum (40)}}
作为一个经验法则,使用功能为常用功能和全局对象的一切。
请记住当你想延长枝:
什么? | 实现困难吗? | 多长时间? | 什么时候? |
---|---|---|---|
宏 | 简单的 | 频繁的 | 内容生成 |
全球 | 简单的 | 频繁的 | Helper对象 |
函数 | 简单的 | 频繁的 | 内容生成 |
过滤器 | 简单的 | 频繁的 | 值转换 |
标签 | 复杂的 | 罕见的 | DSL语言结构 |
测试 | 简单的 | 罕见的 | 布尔决定 |
操作符 | 简单的 | 罕见的 | 值转换 |
全局变量
一个全局变量就像其他模板变量,除了它的可用在所有模板和宏:
1 2
美元嫩枝=新\树枝\环境(美元加载程序);美元嫩枝- >addGlobal (“文本”,新Text ());
然后,您可以使用文本
变量在一个模板:
1
{{text.lipsum (40)}}
过滤器
创建一个过滤器包括将一个名称与PHP调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ /一个匿名函数美元过滤器=新\树枝\ TwigFilter (“rot13”,函数(美元字符串){返回函数美元字符串);});/ /或一个简单的PHP函数美元过滤器=新\树枝\ TwigFilter (“rot13”,“函数”);/ /静态方法或类美元过滤器=新\树枝\ TwigFilter (“rot13”,(“SomeClass”,“rot13Filter”]);美元过滤器=新\树枝\ TwigFilter (“rot13”,“SomeClass:: rot13Filter”);/ /或者一个类方法美元过滤器=新\树枝\ TwigFilter (“rot13”,(美元这,“rot13Filter”]);/ /下一个需要运行时实现(更多信息见下文)美元过滤器=新\树枝\ TwigFilter (“rot13”,(“SomeClass”,“rot13Filter”]);
第一个参数传递到\树枝\ TwigFilter
构造函数的名字是你将使用的过滤模板和第二个是PHP调用关联。
然后,过滤器添加到树枝环境:
1 2
美元嫩枝=新\树枝\环境(美元加载程序);美元嫩枝- >addFilter (美元过滤器);
这里是如何使用它在一个模板:
1 2 3
{{“树枝”| rot13}}{#输出Gjvt #}
被树枝时,PHP调用接收滤波器的左侧(前管道|
)作为第一个参数和额外的参数传递给过滤器(在括号内()
)作为额外的参数。
例如,下面的代码:
1 2
{{|“树枝”较低的}}{{现在|日期(d / m / Y)}}
编译类似如下:
1 2
< ? php回声函数“树枝”)? >< ? php回声twig_date_format_filter (美元现在,' d / m / Y ')? >
的\树枝\ TwigFilter
类需要的选项数组的最后一个参数:
1
美元过滤器=新\树枝\ TwigFilter (“rot13”,“函数”,美元选项);
Environment-aware过滤器
如果你想访问当前环境下滤波器实例,设置needs_environment
选项真正的
;树枝会将当前环境作为第一个参数传递给过滤器调用:
1 2 3 4 5 6
美元过滤器=新\树枝\ TwigFilter (“rot13”,函数(\树枝\环境美元env,美元字符串){/ /获取当前的字符集美元字符集=美元env- >getCharset ();返回函数美元字符串);},“needs_environment”= >真正的]);
环境敏感的过滤器
如果你想访问当前上下文的过滤器,设置needs_context
选项真正的
;树枝会将当前上下文作为第一个参数传递给过滤器调用(或第二个如果needs_environment
也将真正的
):
1 2 3 4 5 6 7
美元过滤器=新\树枝\ TwigFilter (“rot13”,函数(美元上下文,美元字符串){/ /……},“needs_context”= >真正的]);美元过滤器=新\树枝\ TwigFilter (“rot13”,函数(\树枝\环境美元env,美元上下文,美元字符串){/ /……},“needs_context”= >真正的,“needs_environment”= >真正的]);
自动转义
如果启用了自动转义,滤波器的输出打印之前可以逃脱。如果你的过滤器作为逃脱者(或显式输出HTML或JavaScript代码),您将想要的原始输出打印。在这种情况下,设置is_safe
选择:
1
美元过滤器=新\树枝\ TwigFilter (“nl2br”,“nl2br”,(“is_safe”= > [“html”]]);
过滤器可能需要一些工作已经逃脱的输入或安全,例如当添加HTML标记(安全)最初不安全输出。在这种情况下,设置pre_escape
选择逃避输入数据前贯穿你的过滤器:
1
美元过滤器=新\树枝\ TwigFilter (“somefilter”,“somefilter”,(“pre_escape”= >“html”,“is_safe”= > [“html”]]);
可变的过滤器
当一个过滤器应该接受任意数量的参数,设置is_variadic
选项真正的
;树枝会通过额外的过滤器调用参数作为最后一个参数作为数组:
1 2 3
美元过滤器=新\树枝\ TwigFilter (“缩略图”,函数(美元文件数组,美元选项= []){/ /……},“is_variadic”= >真正的]);
被警告说,命名参数传递给一个可变滤波器不能检查有效性作为他们最终会自动的选项数组。
动态过滤器
包含特殊的过滤器的名字*
性格是一个动态的筛选和*
将匹配任何字符串部分:
1 2 3
美元过滤器=新\树枝\ TwigFilter (‘* _path‘,函数(美元的名字,美元参数){/ /……});
以下过滤器匹配上述定义动态过滤:
product_path
category_path
动态过滤器可以定义多个动态部分:
1 2 3
美元过滤器=新\树枝\ TwigFilter (“* _path_ *”,函数(美元的名字,美元后缀,美元参数){/ /……});
过滤器接收所有动态部分值在正常过滤参数,但在环境和上下文。例如,调用“foo”| a_path_b ()
会导致以下参数被传递给过滤器:(“a”、“b”,“foo”)
。
功能
函数是定义在相同的方式作为过滤器,但您需要创建的实例\树枝\ TwigFunction
:
1 2 3 4 5
美元嫩枝=新\树枝\环境(美元加载程序);美元函数=新\树枝\ TwigFunction (“function_name”,函数(){/ /……});美元嫩枝- >addFunction (美元函数);
功能支持相同的特性作为过滤器,除了pre_escape
和preserves_safety
选项。
测试
测试中定义的过滤器和功能一样,但是你需要创建的实例\树枝\ TwigTest
:
1 2 3 4 5
美元嫩枝=新\树枝\环境(美元加载程序);美元测试=新\树枝\ TwigTest (“test_name”,函数(){/ /……});美元嫩枝- >addTest (美元测试);
测试允许您创建自定义特定于应用程序的逻辑来评估布尔条件。作为一个简单的例子,让我们创建一个分支测试,检查对象是“红色”:
1 2 3 4 5 6 7 8 9 10 11
美元嫩枝=新\树枝\环境(美元加载程序);美元测试=新\树枝\ TwigTest (“红色”,函数(美元价值){如果(收取(美元价值- >颜色)& &美元价值- >颜色= =“红色”){返回真正的;}如果(收取(美元价值- >油漆)& &美元价值- >油漆= =“红色”){返回真正的;}返回假;});美元嫩枝- >addTest (美元测试);
测试函数必须返回真正的
/假
。
在创建测试可以使用node_class
提供自定义测试编译选项。这是非常有用的,如果您的测试可以被编译成PHP原语。这是许多树枝内置的测试:
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
名称空间应用程序;使用嫩枝\环境;使用嫩枝\节点\表达式\TestExpression;使用嫩枝\TwigTest;美元嫩枝=新环境(美元加载程序);美元测试=新TwigTest (“奇怪”,零,(“node_class”= > OddTestExpression::类);美元嫩枝- >addTest (美元测试);类OddTestExpression扩展TestExpression{公共函数编译(\树枝\编译器美元编译器){美元编译器- >生(“(”)- >subcompile (美元这- >getNode (“节点”))- >生(“% 2 ! = 0”)- >生(“)”);}}
上面的例子显示了如何创建测试,使用一个节点类。节点类能够访问一个sub-node调用节点
。正在测试这个sub-node包含的值。当奇怪的
过滤器是用于代码,如:
1
{%如果my_value是奇数%}
的节点
sub-node将包含的一种表达my_value
。基于节点测试也有访问参数
节点。该节点将包含其他各种参数,提供给您的测试。
如果你想通过一个变量的位置或命名参数测试,设置is_variadic
选项真正的
。测试支持动态名称(见动态过滤器语法)。
标签
最激动人心的特性之一像树枝的模板引擎是定义新的可能性语言结构。这也是最复杂的功能,你需要了解树枝的内部是如何工作的。
大部分的时间,不需要一个标签:
- 如果你的标签生成一些输出,使用函数代替。
如果你的标签修改一些内容并返回它,使用过滤器代替。
例如,如果您想创建一个标签,将减价格式化的文本转换为HTML,创建一个
减价
而不是过滤:1
{{| * *减价* *文本减价}}
如果你想使用这个过滤大量的文本,包装的应用标签:
1 2 3 4 5 6
{%应用减价%}标题= = = = =比创建一个标签可以* *组成* *过滤器。{%endapply%}
如果你的标签不输出任何东西,只存在由于副作用,创建一个函数返回,通过调用它过滤器标签。
例如,如果您想创建一个日志的文本标签,创建一个
日志
函数,通过调用它做标签:1
{%做日志(日志有些东西)%}
如果你还想创建一个标记为一门新语言构造,太棒了!
让我们创建一个集
标签,允许简单的变量的定义在一个模板。可以使用标签像:
1 2 3 4 5
{%集name = "价值" %}{{名称}}{#应该输出值#}
请注意
的集
标签是核心扩展的一部分,因此总是可用的。内置的版本是更强大和默认支持多个作业。
需要三个步骤来定义一个新的标签:
- 定义一个象征性的解析器类(负责解析模板代码);
- 定义一个节点类(负责将解析代码转换为PHP);
- 注册标记。
注册一个新的标签
通过调用添加一个标签addTokenParser
方法\树枝\环境
实例:
1 2
美元嫩枝=新\树枝\环境(美元加载程序);美元嫩枝- >addTokenParser (新Project_Set_TokenParser ());
定义一个象征性的解析器
现在,让我们看看这个类的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
类Project_Set_TokenParser扩展\嫩枝\TokenParser\AbstractTokenParser{公共函数解析(\树枝\令牌美元令牌){美元解析器=美元这- >解析器;美元流=美元解析器- >getStream ();美元的名字=美元流- >期望(\树枝\令牌::NAME_TYPE)- >getValue ();美元流- >期望(\树枝\令牌::OPERATOR_TYPE,“=”);美元价值=美元解析器- >getExpressionParser ()- >parseExpression ();美元流- >期望(\树枝\令牌::BLOCK_END_TYPE);返回新Project_Set_Node (美元的名字,美元价值,美元令牌- >getLine (),美元这- >getTag ());}公共函数getTag(){返回“设置”;}}
的getTag ()
方法必须返回标签我们想解析,在这里集
。
的parse ()
只要解析器遇到一个方法被调用集
标签。它应该返回一个\ \树枝\节点
(实例代表了节点Project_Set_Node
调用创建在下一节中解释)。
解析过程简化由于很多方法可以调用标记流($ this - >解析器- > getStream ()
):
getCurrent ()
:获取当前令牌在流。next ()
:移动到下一个记号流,但返回旧的。测试类型($)
,测试(美元值)
或测试(类型,价值美元)
:决定当前某一特定类型的令牌或价值(或两者)。值可能是一些可能的值的数组。期望($(类型,价值美元消息[$]])
:如果当前令牌不是给定类型/值的语法错误。否则,如果类型和值是正确的,返回的令牌和流移动到下一个记号。看()
没有消费:看下一个记号。
解析表达式是通过调用来完成的parseExpression ()
像我们一样的集
标签。
提示
阅读现有的TokenParser
类是最好的学习方法解析过程的所有细节。
定义一个节点
的Project_Set_Node
类本身很短:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
类Project_Set_Node扩展\嫩枝\节点\节点{公共函数__construct(美元的名字,\树枝\节点\ \ AbstractExpression表达式美元价值,美元行,美元标签= null){父::__construct ([“价值”= >美元价值]、[“名字”= >美元的名字),美元行,美元标签);}公共函数编译(\树枝\编译器美元编译器){美元编译器- >addDebugInfo (美元这)- >写($上下文[\”。美元这- >getAttribute (“名字”)。' \ ']= ')- >subcompile (美元这- >getNode (“价值”))- >生(”;\ n”);}}
编译器实现流体接口并提供方法,帮助开发人员生成漂亮的和可读的PHP代码:
subcompile ()
:编译一个节点。生()
写道:给定字符串。write ()
写道:给定字符串通过增加缩进每一行的开头。字符串()
:写一个引用字符串。repr ()
:写一个PHP的代表一个给定的值(见\树枝\ \ ForNode节点
使用的例子)。addDebugInfo ()
:添加行原始模板文件与当前节点的评论。缩进()
生成的代码(参见:缩进\树枝\ \ BlockNode节点
使用的例子)。减少缩进()
生成的代码(参见:减少缩进\树枝\ \ BlockNode节点
使用的例子)。
创建一个扩展
编写一个扩展的主要动机是经常使用的代码移动到一个可重用的类添加对国际化的支持。可以定义一个扩展标签、过滤器、测试操作符、函数、和节点的游客。
大多数时候,它是有用的为您的项目创建一个扩展,主机所有你想要的具体的标签和过滤器添加到树枝。
提示
当你的代码打包成一个扩展,树枝是足够聪明重新编译您的模板(当一旦你想要改变它auto_reload
启用)。
一个扩展是一个类,实现以下接口:
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
接口\嫩枝\扩展\ExtensionInterface{/ * * *返回标记添加到现有列表解析器实例。* *@return\树枝\ TokenParser \ TokenParserInterface [] * /公共函数getTokenParsers();/ * * *返回节点访问实例添加到现有列表。* *@return\树枝\ NodeVisitor \ NodeVisitorInterface [] * /公共函数getNodeVisitors();/ * * *返回一个列表的过滤器添加到现有列表。* *@return\树枝\ TwigFilter [] * /公共函数getFilters();/ * * *返回一个列表的测试添加到现有列表。* *@return\树枝\ TwigTest [] * /公共函数getTests();/ * * *返回一个函数列表添加到现有列表。* *@return\树枝\ TwigFunction [] * /公共函数getFunctions();/ * * *返回一个运营商列表添加到现有列表。* *@return一元操作符数组> <数组第一个数组,第二个数组的二元操作符* /公共函数getOperators();}
保持您的扩展类的清洁和精益,从内置的继承\树枝\ \ AbstractExtension延伸
实现接口的类,而不是因为它提供空的实现方法:
1 2 3
类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension{}
这个扩展现在什么也不做。我们将在下一节中进行定制。
您可以保存您的扩展文件系统上的任何地方,因为所有的扩展必须注册明确可在你的模板。
你可以注册一个扩展使用addExtension ()
方法在主环境
对象:
1 2
美元嫩枝=新\树枝\环境(美元加载程序);美元嫩枝- >addExtension (新Project_Twig_Extension ());
提示
树枝核心扩展是伟大的例子扩展是如何工作的。
全局变量
全局变量可以在一个扩展注册通过getGlobals ()
方法:
1 2 3 4 5 6 7 8 9 10 11
类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension实现了\嫩枝\扩展\GlobalsInterface{公共函数getGlobals():数组{返回(“文本”= >新文本()];}/ /……}
功能
函数可以在扩展通过注册getFunctions ()
方法:
1 2 3 4 5 6 7 8 9 10 11
类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension{公共函数getFunctions(){返回(新\树枝\ TwigFunction (“lipsum”,“generate_lipsum”),);}/ /……}
过滤器
一个扩展添加一个过滤器,你需要覆盖getFilters ()
方法。该方法必须返回一个数组的过滤器添加到树枝环境:
1 2 3 4 5 6 7 8 9 10 11
类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension{公共函数getFilters(){返回(新\树枝\ TwigFilter (“rot13”,“函数”),);}/ /……}
标签
添加一个标签的扩展可以通过覆盖getTokenParsers ()
方法。该方法必须返回一个数组标记添加到树枝环境:
1 2 3 4 5 6 7 8 9
类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension{公共函数getTokenParsers(){返回(新Project_Set_TokenParser ()];}/ /……}
在上面的代码中,我们添加了一个新的标签,定义的Project_Set_TokenParser
类。的Project_Set_TokenParser
标签类负责解析和编译PHP。
运营商
的getOperators ()
方法允许您添加新操作符。这是如何添加的!
,| |
,& &
运营商:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension{公共函数getOperators(){返回[[“!”= > [“优先”= >50,“类”= > \树枝\节点\ \一元\ NotUnary表达式::类]]、[“| |”= > [“优先”= >10,“类”= > \树枝\节点\ \二进制\ OrBinary表达式::类,结合性的= > \树枝\ ExpressionParser::OPERATOR_LEFT),“& &”= > [“优先”= >15,“类”= > \树枝\节点\ \二进制\ AndBinary表达式::类,结合性的= > \树枝\ ExpressionParser::OPERATOR_LEFT]]];}/ /……}
测试
的getTests ()
方法允许您添加新的测试函数:
1 2 3 4 5 6 7 8 9 10 11
类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension{公共函数getTests(){返回(新\树枝\ TwigTest (“甚至”,“twig_test_even”),);}/ /……}
定义和运行时
树枝过滤器、函数和测试运行时实现可以被定义为任何有效的PHP调用:
- 函数/静态方法:简单的实现和快速(所使用的所有树枝核心扩展);但是很难运行时依赖于外部对象;
- 闭包:简单的实现;
- 对象方法:如果您的运行时代码更加灵活,需要依靠外部对象。
最简单的方法使用方法是定义在扩展本身:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension{私人美元rot13Provider;公共函数__construct(美元rot13Provider){美元这- >rot13Provider =美元rot13Provider;}公共函数getFunctions(){返回(新\树枝\ TwigFunction (“rot13”,(美元这,“rot13”)));}公共函数rot13(美元价值){返回美元这- >rot13Provider- >rot13 (美元价值);}}
这是很方便但不推荐,因为它使模板编译依赖于运行时依赖,即使他们不需要(例如认为依赖,连接到一个数据库引擎)。
你可以从它们的运行时实现解耦扩展定义了注册\树枝\ RuntimeLoader \ RuntimeLoaderInterface
实例等环境,知道如何实例化运行时类(必须autoload-able运行时类):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
类RuntimeLoader实现了\嫩枝\RuntimeLoader\RuntimeLoaderInterface{公共函数负载(美元类){/ /实现逻辑创建元类的实例/ /及其依赖项注入/ /在大多数情况下,这意味着使用依赖注入容器如果(“Project_Twig_RuntimeExtension”= = =美元类){返回新美元类(新Rot13Provider ());}其他的{/ /……}}}美元嫩枝- >addRuntimeLoader (新RuntimeLoader ());
请注意
树枝有PSR-11兼容运行时加载程序(\树枝\ RuntimeLoader \ ContainerRuntimeLoader
)。
现在可以移动到一个新的运行时逻辑Project_Twig_RuntimeExtension
扩展类和直接使用它:
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日
类Project_Twig_RuntimeExtension{私人美元rot13Provider;公共函数__construct(美元rot13Provider){美元这- >rot13Provider =美元rot13Provider;}公共函数rot13(美元价值){返回美元这- >rot13Provider- >rot13 (美元价值);}}类Project_Twig_Extension扩展\嫩枝\扩展\AbstractExtension{公共函数getFunctions(){返回(新\树枝\ TwigFunction (“rot13”,(“Project_Twig_RuntimeExtension”,“rot13”]),/ /或新\树枝\ TwigFunction (“rot13”,“Project_Twig_RuntimeExtension:: rot13”),);}}
测试一个扩展
功能测试
您可以创建扩展的功能测试,通过创建以下测试目录中的文件结构:
1 2 3 4 5 6 7 8 9 10 11
设备/过滤器/ foo。测试棒。测试函数s/ foo.test bar.test tags/ foo.test bar.test IntegrationTest.php
的IntegrationTest.php
文件应该是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
使用嫩枝\测试\IntegrationTestCase;类Project_Tests_IntegrationTest扩展IntegrationTestCase{公共函数getExtensions(){返回(新Project_Twig_Extension1 (),新Project_Twig_Extension2 ()];}公共函数getFixturesDir(){返回__DIR__。“/夹具/”;}}
装置的例子可以发现在树枝库测试/理解/夹具目录中。