番外篇:原生twig模板引擎详解(上集:设计师篇)
原生twig模板引擎详解(上集:设计师篇)
前文:本文是《云客drupal源码分析》系列的番外篇,由于drupal采用了twig模板引擎,因此该篇将详细介绍twig,但不会涉及drupal任何内容,是原生twig的使用详解,分上下两集,上集供设计师阅读,讲解在模板中的使用,下集供php程序开发人员阅读,讲解程序调用、扩展开发。
本篇为上集,额外介绍了安装等必要内容,以便首次接触人员查阅,纯模板设计师可以跳过。
官网地址:https://twig.symfony.com/
本文写作时官网已经发布了V1.35.3 及V2.4.8
安装:
需求:
和php一样,发布大版本后,以前的版本依然会维护一段时间,因此twig目前有两个版本都在维护
1.X版本:至少PHP 5.2.7,从1.34开始至少需要PHP 5.3.3.
2.X版本:至少PHP 7.0.0
包括drupal8在内,许多项目都还在使用1.X,这里以1.X做主要介绍,有多种方法安装twig,官方推荐使用composer,运行如下命令即可:
composer require twig/twig:~1.0
为了提高性能twig还提供了有限功能的php层面的C扩展,详见官网,这里我们以下载文件方式来安装。
安装:
到这里下载文件包:https://github.com/twigphp/Twig/tags
本文选择: v1.35.3 解压下载的Twig-1.35.3.tar.gz 由于twig使用psr-0的自动加载方式,所以里面的lib就是我们需要的库文件,将其复制到网站根目录即可
你可以在本机通过phpstudy等软件安装运行环境,这里以C:\root\twigtest作为网站根目录,以http://www.twigtest.com/作为域名
根目录建立index.php,内容如下:
<?php
require_once 'lib/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_Array(array(
'index' => 'Hello {{ name }}!',
));
$twig = new Twig_Environment($loader);
echo $twig->render('index', array('name' => 'Fabien'));
访问域名,即可看到结果,安装完成。
开始使用:
假设你已经按前一步设置好了目录与文件,我们来从文件系统加载一个模板文件,进行显示:
在根目录建立子目录“templates”用以存放模板文件,在其中建立文本文件“index.html.twig”,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>twig</title>
</head>
<body>
hello world ! {{name}}
</body>
</html>
在网站根目录重新建立index.php,内容如下:
<?php
require_once 'lib/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem('templates');
$twig = new Twig_Environment($loader, array(
'cache' => 'templates_cache',
));
echo $twig->render('index.html.twig', array('name' => 'yunke'));
然后访问站点http://www.twigtest.com/index.php,你将会看到:
hello world ! yunke
同时在站点根目录下多出了一个子目录:
templates_cache
其中存放着为提高性能编译为php文件的模板文件,默认只编译一次,如原文件发生变化需重新编译(删除缓存)
在以上代码中Twig_Environment是twig的中枢对象,类接收两个参数:
\Twig_Environment::__construct (Twig_LoaderInterface $loader = null, $options = array())
$loader是实现了\Twig_LoaderInterface接口的加载器,用于加载模板文件
$options是选项数组,控制twig行为,有效选项如下:
debug:是否处于调试模式,布尔值,默认为false
charset:模板使用的字符编码,默认为 UTF-8
base_template_class:基础模板类,用于被编译后的模板php文件,默认为Twig_Template
cache:可选值有储存编译后模板的目录,或者为false以禁用编译缓存,这是默认值,也可以是缓存对象(接口:Twig_CacheInterface的实例,见开发者篇)
auto_reload:布尔值,在原模板文件改变时是否重新加载,如果没有提供那么将根据debug选项决定
strict_variables:是否使用严格变量模式,默认为false,为真时模板中使用无效变量将抛出错误,否则为NULL
autoescape:是否全局自动转义,可能的值有:false(禁止转义),true(默认值,等效为html),html(转义html实体),js(转义js),css(转义css),url(转义url),html_attr(转义html和属性),name(基于模板扩展名设置自动转义策略),PHP callback(由一个php回调来返回转义策略,该回调接收模板的名字做参数)
optimizations:模板编译时的优化选项,“-1”代表开启全部优化(默认值),“0”代表禁用优化,还有其他值详见下集中优化扩展一节
渲染代码:$twig->render('index.html.twig', array('name' => 'yunke'));原型为:
render($name, array $context = array())
第一个参数是模板的文件名,用于加载模板
第二个是传递给模板的变量数组,键名为在模板中的变量名,键值为对应的变量值,该变量数组称为模板的“上下文”,在模板中通过“_context”能访问到该数组,下文将多次提到“上下文”就是指该变量数组
模板文件命名:
twig模板是一个段文本内容,可以存放在数据库、php变量、文件等地方,如果是文本文件,命名是任意的,任意扩展名均可,但通常使用“.twig”或“.html.twig”作为扩展名
----上集部分(设计师篇)----
默认界定符:
{% ... %} 执行标签,用于执行语句,如控制结构
{{ ... }} 打印标签,用于打印表达式的计算结果
{# ... #} 注释标签,用于注释,类似程序中的注释:/* ..*/
#{…} 插值标签,用于向表达式中的字符串插入变量值
这些定界符只是twig的默认值,是可以完全自定义的,详见下集开发者篇
字面量Literals:
字符串:如"Hello World",可用“\”转义特殊字符,如'It\'s good','c:\\Program Files'
数字:包括整数和浮点数,如4/3.14
数组:如["foo", "bar"],和php不同,不存在键名,她是一个简单的数字索引数组,可嵌套。如:
{% set a = ["one", ["two", "three"]] %}
要想输出“three”可这样访问:{{ a[1][1] }},注意采用点号语法时不能这样:{{ a.1.1 }},而要这样:{{ (a.1).1 }},这有点违反直觉
哈希表:等效于php的关联数组,如:{% set a = { 'foo': 'vfoo', 'bar': 'vbar' } %},键值可以是字面量、变量或表达式,其中键名也可以不用引号,不要引号时,并不等于键名采用了一个变量,这有点违反直觉,请看代码:
{% set foo = "yunke" %}
{% set a = { foo: 'vfoo', 'bar': 'vbar' } %}
{{ a.yunke }}
这将不会有任何输出,foo并不被认为是变量,而是字面量,要想输出'vfoo'应该使用{{ a.foo }},如果要想将foo当做变量看待,那么需要为其加上括号,如下:
{% set foo = "yunke" %}
{% set a = { (foo): 'vfoo', 'bar': 'vbar' } %}
{{ a.yunke }}
此时就可以输出'vfoo'了, 加括号实际是采用了表达式键名,此时还可以这样:
{% set foo = 'foo' %}
{ (foo): 'foo', (1 + 1): 'bar', (foo ~ 'b'): 'baz' }
数组和哈希表可以混合使用,且可以嵌套
布尔值:true / false,没有引号
空值:null,代表值不存在,它有个别名:none
访问变量内容:
{{yunke}}
直接显示变量“yunke”的内容,界定符内可以有空格,如{{ yunke }} ,下同
{{yunke.name}}
用点号可以访问对象的属性或数组的元素,这里“yunke”可以是对象也可以是数组,name是属性名或数组键名
{{yunke[‘name’]}}
也可以用下标,但不能用于对象,此时“yunke”只能是数组
{{ attribute(foo, 'data-bar') }}
如果属性名中包含特殊字符时可以使用属性函数,如“-”可能和减号混淆,属性函数可明确语义
如果变量不存在,将得到一个NULL值(在显示上什么也没有),如果选项中“strict_variables”值为真,那么将抛出错误,在内部点号“.”方式实际是执行以下逻辑,以foo.bar为列:
1、检查foo是否是一个数组且bar是其的有效键名,如果是则返回,否则继续
2、检查foo是否是一个对象,bar为其的一个属性,如果是则返回,否则继续
3、如果foo是一个对象,检查bar是否为一个有效的方法,是则调用,否则继续
4、如果foo是一个对象,检查是否存在getBar方法存在,是则调用,否则继续
5、如果foo是一个对象,检查是否存在isBar方法存在,是则调用,否则继续
6、如果都不满足,最后返回NULL值
而foo['bar']只适用于数组,只进行两步检查:
1、检查foo是否是一个数组且bar是其的有效键名,如果是则返回,否则继续
2、返回NULL值
所以主题开发者可以优先使用点号方式,注意foo.bar中bar可以是数字,代表数组的数字索引,如果此时使用中括号方式,数字加不加引号都没有关系
如果想访问一个变量的动态属性,可以使用attribute函数代替,见后
{{ "字面量" }} {{ "{{}}" }}
可以通过这种方式直接输出字面量
全局变量:
是指在模板中总是有效的全局变量,默认有如下:
_self:引用到当前模板,值为不带路径的模板文件名
_context:引用到当前上下文,也就是$twig->render()的第二个参数,她总是数组
_charset:引用到当前模板的字符集,值如“UTF-8”
开发者可以通过扩展提供更多全局变量,见开发者篇
设置变量:
模板开发者可以在模板中创建变量,使用“set”:
{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}
操作符:
操作符优先级由低到高依次为(以“,”分隔):
b-and, b-xor, b-or, or, and, ==, !=, <, >, >=, <=, in, matches, starts with, ends with, .., +, -, ~, *, /, //, %, is, **, |, [], .
点号操作符优先级最高,所有操作符如下:
数学运算符:
在模板中可以使用算术运算符号:
+(加),-(减),*(乘),/(除),%(求余),
//(相除后向下取整,舍去法取整,如20//7结果为2,-20//7为3),
**(幂运算,2**3等于8,2的3次方)
逻辑运算符:
and(与),or(或),not(非)
按位与、或、异或:b-and, b-or, b-xor
逻辑运算符是大小写敏感的,必须全小写
比较运算符:
可用常规运算符如:==, !=, <, >, >=, <=
注意没有“===”、“<>”,额外的运算符有:starts with、ends with
{% if 'Fabien' starts with 'F' %}
{% endif %}
{% if 'Fabien' ends with 'n' %}
{% endif %}
他们检查是否以字符串开始或结束,是大小写敏感的
更加复杂的比较运算符可用使用正则表达式:matches,示例如下:
{% if phone matches '/^[\\d\\.]+$/' %}
{% endif %}
注意正则表达式中有“\”时,需要双转义
包含操作符:
使用“in”或“not in”以测试是否被包含:
{{ 1 in [1, 2, 3] }}
{{ 'cd' in 'abcde' }}
{% if 1 not in [1, 2, 3] %} 等价于:{% if not (1 in [1, 2, 3]) %}
测试操作符:
在twig中设置了一些测试表达式,可用“is”、“is not”操作符进行测试:
{{ num is odd }}
这测试一个数是否为奇数,更多测试表达式如下:
constant、defined、divisibleby、empty、even、iterable、null、odd、sameas
见:https://twig.symfony.com/doc/1.x/
其他操作符:
“|”:管道符,用于应用一个过滤器
“..”:双点号,用于创建一个数组
如在{% set a= 1..50 %}中a将是元素从1开始50结束的数组,等效于{{ range(1, 50) }}
“~”:连接符,完全等同于php的点号“.”,用于将操作数转化为字符并连接再一起
“?:”:三元操作符,与php等效,从 Twig 1.12.0开始,以下用法是等效的:
{{ foo ?: 'no' }} 和 {{ foo ? foo : 'no' }} 相同
{{ foo ? 'yes' }} 和 {{ foo ? 'yes' : '' }} 相同
“??”:假设{{ foo ?? 'no' }},如果foo被定义了且不为null那么就返回foo的值,否则返回“no”
表达式插入字符串中:
可以通过“#{ }”向双引号内的字符串中插入表达式的值(单引号无效;必须是表达式,单独用于模板无效):
{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}
空格控制:
默认情况下,标签后的第一个换行符被删除,但随后的换行、制表tab、空格等空白不会被修改,可以使用“spaceless”标签去移除一段内容的所有空白:
{% spaceless %}
<div>
<strong>foo bar</strong>
</div>
{% endspaceless %}
这将输出:
<div><strong>foo bar</strong></div>
也可以在标签内使用减号来删除围绕标签的前或后空白:
{{- value -}}
此时减号被重载为空白修饰符,不必成对使用
过滤器:
过滤器用于对变量进行修改,使用管道符号“|”分隔,可以使用链式写法,前面的输出是后面的输入,如对变量name去除html标签,并采用标题格式(每个单词首字母大写):
{{ name|striptags|title }}
过滤器可以带参数,如将数组元素连接起来:
{{ list|join(', ') }}
也可以对整段内容使用过滤器,如将一句话大写:
{% filter upper %}
This text becomes uppercase
{% endfilter %}
twig默认提供的过滤器请见:\Twig_Extension_Core::getFilters
或官方文档:https://twig.symfony.com/doc/1.x/
开发者可以通过扩展提供更多过滤器
字符转义:
有些变量中的内容会影响html标签,比如尖括号、单双引号、和号&等等,那么就需要转义,有两种处理办法(可混用):
1、使用选项参数autoescape自动转义全部模板变量,见前文介绍,默认是开启的,转义策略是html
2、将autoescape设置为false关闭全局变量自动转义,此时模板开发者需要对不信任的变量手动转义,操作如下:
{{ yunke.risk|e }}
形式上这和使用过滤器是一样的,“e”是“escape”的缩写,默认是使用“html”转义,需要指定其他策略请:
{{ yunke.risk|e('js') }}
可用的策略参数有:js、css、html、url、html_attr
除对单个变量转义和全局转义外,还可以对模板里一段内容中的全部变量转义(局部转义):
{% autoescape 'js' %}
只要在这个标签内部的任何变量都会转义,注意是变量,也就是{{}}里的内容,模板内容不会转义
{% endautoescape %}
省略参数则默认使用html转义,开启局部或全局变量转义时,需要输出不转义内容要使用如下方式:
{{ var|raw }}
不管全局转义如何设置,autoescape标签将改变局部的转义模式,如下:
{% autoescape 'html' %}
{{ var }} {#不论全局转义如何设置 var只进行html转义 #}
{{ var|raw }} {# var 不转义#}
{{ var|escape }} {# var 只转义一次 不会双转义 #}
{% endautoescape %}
注意:标签autoescape不会影响到其内部使用included包含进来的模板
开启全局或局部转义后有如下规则需要注意:
不对字面量转义:
{{ "Twig<br />" }} {# 不转义 #}
{% set text = "Twig<br />" %}
{{ text }} {# 将转义#}
表达式结果:
{{ foo ? "Twig<br />" : "<br />Twig" }} {# 不转义 #}
{% set text = "Twig<br />" %}
{{ foo ? text : "<br />Twig" }} {#不论foo为真或假 都会转义结果#}
{% set text = "Twig<br />" %}
{{ foo ? text|raw : "<br />Twig" }} {#不论foo为真或假 都会转义结果 #}
{% set text = "Twig<br />" %}
{{ foo ? text|escape : "<br />Twig" }} {# 当转义策略和外部策略相同时不会再次转义#}
过滤器顺序:
转义被用在过滤器后,输出前,如:
{{ var|upper }} 等价于 {{ var|upper|escape }}
过滤器“raw”应该被用在最后:
{{ var|raw|upper }} {# 将转义 #}
{{ var|upper|raw }} {# 不转义#}
如果最后一个过滤器和当前转义策略相同,那么不会被再次运用:
{% autoescape 'js' %}
{{ var|escape('html') }} {# 将再次对结果运用js转义 #}
{{ var }} {# 只运用js转义 #}
{{ var|escape('js') }} {# 不会再次转义 #}
{% endautoescape %}
变量转义有局限性,假设在全局转义为js时,以下代码的结果不是你想象的样子:
{{ yunke.risk|raw ~ yunke.risk }}
她看起来是原文本加js转义后的结果,但实际上全部是js转义的结果,和没有加“raw”过滤器时效果一样,所以我们在设计模板时应当尽量避免复杂的转义并充分测试,
函数:
可以像php一样使用函数,如:
{% for i in range(0, 3) %}
{{ i }},
{% endfor %}
从Twig 1.12开始调用函数时,可不按照参数顺序赋值,可以指定函数原型中的参数名,如:
{% for i in range(low=1, high=10, step=2) %}
{{ i }},
{% endfor %}
这样使得语义更加明确,可以忽略参数顺序,仅为部分参数赋值,其他参数使用默认值,也可以顺序方式和指定参数名方式混用,但顺序方式传递的参数必须在指定名字的参数前面,如:
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
函数的参数名可在官方手册对应函数介绍的参数一节看到:
https://twig.symfony.com/doc/1.x/
开发者可以通过扩展提供更多函数
控制结构:
用于条件判断、循环遍历等,控制结构位于{% ... %}中:
遍历结构:
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
没有遍历内容时可以显示默认值:
{% for user in users %}
*<li>{{ user.username|e }}</li>
{% else %}
No users have been found.
{% endfor %}
遍历变量键名:
<h1>Members</h1>
<ul>
{% for key in users|keys %}
<li>{{ key }}</li>
{% endfor %}
</ul>
遍历键和值:
<h1>Members</h1>
<ul>
{% for key, user in users %}
<li>{{ key }}: {{ user.username|e }}</li>
{% endfor %}
</ul>
添加条件遍历,仅遍历条件为真的条目:
<ul>
{% for user in users if user.active %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
遍历部分内容:
<h1>Top Ten Members</h1>
<ul>
{% for user in users|slice(0, 10) %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
在for结构中可以使用特殊变量loop,如:
loop.index:当前迭代了第几次,从1开始
loop.index0:当前迭代了第几次,从0开始
loop.revindex:剩余迭代次数,最后是1
loop.revindex0:剩余迭代次数,最后是0
loop.first:是否为第一次迭代
loop.last:是否为最后一次迭代
loop.length:迭代的条目总数
loop.parent:引用到外层循环上下文
在指定循环条件时,loop变量是无效的
注意在for中没有break 或者 continue结构,遍历可以针对数组或者是实现了迭代器接口的对象
条件判断:
{% if users|length > 0 %}
show user
{% endif %}
也可以测试是否为空:
{% if users %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}
取反:
{% if not user.subscribed %}
<p>You are not subscribed to our mailing list.</p>
{% endif %}
也可以多分枝条件判断:
{% if 3 > yunke|length > 0 %}
show 0-3
{% elseif 5 > yunke|length >= 3 %}
show 3-5
{% else %}
show 5-..
{% endif %}
是否为true规则和php一样:
空字符串:false
数字0: false
字符串:true
非零数字: true
空白字符串:true
字符串“0”:false
空数组:false
NULL:false
非空数组:true
对象:true
作用域控制:
使用with标签可以创建一个新的作用域,里面的变量仅在内部有效,标签外部无效,示例如下:
{% with %}
{% set foo = 42 %}
{{ foo }} foo is 42 here
{% endwith %}
变量foo在该标签以外是访问不到的,以上列子还可以换一种方式书写:
{% with { foo: 42 } %}
{{ foo }} foo is 42 here
{% endwith %}
也就是将变量集中到哈希中传入;默认情况下新建的作用域内可以访问外部变量,可以使用以下方式禁止:
{% set bar = 'bar' %}
{% with { foo: 42 } only %}
{# only foo is defined #}
{# bar is not defined #}
{% endwith %}
和普通编程语言相比没有while(){}、do{}while()等结构,但twig的强大之处就是可以通过添加扩展的方式让她有,不过默认是没有的
加载其他模板:
在一个模板中可以通过如下方式加载其他模板:
{% include('sidebar.html') %}
也可以使用变量,如:
{% include var ~ '_foo.html' %}
实际上include关键词后可以是任意表达式,更复杂的如:
{% include var|default('index') ~ '_foo.html' %}
通过这种方式包含其他模板后,在include语句位置能使用的有效变量,在被加载的模板中也有效,如下:
{% for box in boxes %}
{{ include('render_box.html') }}
{% endfor %}
在render_box.html中可以使用变量box,被加载的模板名依赖于加载器的类型,如果是文件系统加载器,那么可以使用路径名。
模板继承:
一个模板可以把一个区域定义成块,这样其他模板可以声明继承该模板,声明后就成了该模板的子模板,相对的该模板就是父模板,子模板可以通过块名覆写父模板中定义的块,如果不覆写将使用父模板的内容,这称为模板继承,父模板声明块和子模板覆写块都使用如下方式:
{% block blockName %}…{% endblock %}
在子模板中声明继承需在第一行使用如下语句:
{% extends "base.html" %}
这里extends可以使用表达式以加载不同模板,如:
{% extends request.ajax ? "base_ajax.html" : "base.html" %}
模板继承示例如下,比如a.html.twig是父模板,内容如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>twig</title>
</head>
<body>
{% block yunke %}
<div>我是父模板a.html.twig中的内容</div>
{% endblock %}
</body>
</html>
b.html.twig是子模板,内容如下:
{% extends "a.html.twig" %}
{% block yunke %}
<div>我是子模板b.html.twig中的内容</div>
{{ parent() }}
{% endblock %}
使用echo $twig->render('b.html.twig');将输出:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>twig</title>
</head>
<body>
<div>我是子模板b.html.twig中的内容</div>
<div>我是父模板a.html.twig中的内容</div>
</body>
</html>
如你所见{{ parent() }}用于输出父模板中的块内容
不管是子模板还是父模板,他们共享相同的上下文变量
需要注意如果B继承了A,也就是说B是A的子模板,那么在输出时B里面只能对A的块进行覆写,不能输出块外的内容,但b里面可进行变量赋值等非输出操作,在b里的逻辑先于a执行,因此在b中对变量或上下文的赋值将应用在a中
宏命令Macros:
当有些模板内容会被反复多次用到时,可以将其定义为宏,在使用的地方进行宏调用即可,里面需要用到的变量可以通过宏参数传入,这有点像编程语言中定义函数,新建一个模板,在里面定义宏(如同在函数库文件中定义函数),假设模板名为macrod.html.twig,内容如下:
{% macro input(name, value = "", type = "text", size = 20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}
{% macro user(name="匿名用户", tel = "未知") %}
用户名:{{ name }} 电话号码:{{ tel }}
{% endmacro %}
这就在一个文件中声明了两个宏,在其他模板中需要用到时,可以这样:
{% import "macrod.html.twig" as lib %}
<p>{{ lib.input('username') }}</p>
<p>{{ lib.user('yunke',"13812345678") }}</p>
<p>{{ lib.user() }}</p>
这将输出:
<p> <input type="text" name="username" value="" size="20" />
</p>
<p>用户名:yunke 电话号码:13812345678
</p>
<p>用户名:匿名用户 电话号码:未知
</p>
导入语句{% import "macrod.html.twig" as lib %}只需要放在宏调用前即可,上列是将整个文件引用进来然后给了一个别名“lib”,你也可以只导入某具体的宏,输出效果是一样的,如下:
{% from 'macrod.html.twig' import user as id %}
<p>{{ id('yunke',"13812345678") }}</p>
在上面的列子中,如果在macrod.html.twig中出现任何宏声明之外的内容,将会被忽略,不会引起错误,我们应该认识到宏模板文件应该只用来定义宏
如果在进行宏调用时传递了多于的参数,那么多于的参数将以列表方式储存在特殊变量varargs
中,在宏内可以使用for结构去遍历她,如下:
{% macro user(name="匿名用户", tel = "未知") %}
用户名:{{ name }} 电话号码:{{ tel }} {% for arg in varargs %}额外参数:{{ arg }}{% endfor %}
{% endmacro %}
此时如果没有传递多于参数,将不会有额外部分的显示,换句话说varargs
不包含有声明的参数
在宏声明时最佳实践是在参数上定义默认值,否则在宏内使用该参数时应当给出默认值
模板编码标准:
在编写模板代码时推荐遵从官方的标准编码风格,这不是强制的,但这样有较好的可读性,也叫做官方推荐最佳实践,简要如下:
1、在开始定界符后和结束定界符前,有且仅有一个空格
2、当使用空白控制符“-”时,不要在她和定界符之间放置任何空白
3、在操作符前后各使用一个空格,有且仅有一个
4、在数组的“,”和哈希表的“:”之后有且仅有一个空格
5、在开始圆括号后、结束圆括号前不要放置任何空白
6、在引号前后不要放置空白
7、在操作符|, ., .., []前后不要放置空白
8、过滤器和函数的圆括号前后不要空白
9、使用小写变量名,单词间用下划线
10、对齐标签,统一缩进
官网最佳实践网址为:
https://twig.symfony.com/doc/1.x/coding_standards.html
补充:
1、模板开发者可以在这里在线测试自己的模板:
可以通过多种格式(yaml、json、xml、ini)传递变量,然后显示
2、twig模板引擎是高度可扩展的,开发者可以通过扩展提供更多全局变量、过滤器、函数、操作符、控制结构等等,请见开发者篇