**引子:**今天遇到一个问题,模板中某些变量有时候没有定义,如果php开启了E_ALL报错级别,那么会出现一个notice,提示变量未定义。 一开始的想法是通过三元表达式(<?=isset($var)?$var:'';?>)来给每个变量设定一个默认值,后来发现这种写法太繁琐,而且与volt标签格格不入。 再后来想到屏蔽E_NOTICE报告,但开发环境下还是很不恰当的。 最后发现volt引擎有一个过滤器Filters功能,解决了这个问题,写法如:{{ var|default('str') }} ,在$var为空的时候,将会将该处设置为str,当然,str可以是空字符串。 本文就探究一下什么是过滤器,以及如何自定义过滤器。
第一,模板渲染的是什么? 在使用volt模板引擎的时候,模板是如何渲染的呢?大家都知道,在控制器方法执行完毕后,框架会自动渲染视图View,而这时候由于你使用了volt模板引擎,里面特有的一套标签语法并非php语法,所以视图无法直接include模板文件而作为php脚本解释。这时候视图会做一件事情,它会先将模板代码中的标签替换为php代码,保存到模板缓存文件中,缓存位置是你在di中配置视图的时候自己设置的,每个缓存文件都对应一个模板文件,第二次渲染视图的时候将直接取缓存文件,避免了每次都要解析volt标签,所以性能上与原生php模板并无差距,某些情况下可能更好(布局以及模板继承时,会将多层模板合并为一个,减少了文件读取次数)。
第二,标签'原形' {{ var|default('str') }}究竟被转化为什么?我们可以通过观察模板缓存看到,这个标签大概被转化为
<!-- lang: php -->
<?php echo (empty($var) ? ('str') : ($var)); ?>
第三,理解管道'|' 熟悉shell的朋友这一步一眼了然,标签的作用机制是:将$var的值传给下一个过滤器default处理,过滤器还可附加其他参数,只要如同函数调用一样增加参数即可,如上的 ('str');变量类型的标签最终会转化为echo语句。
第四,自定义过滤器 通过官方手册可以知道,在di中注册volt模板引擎的时候,可以添加自定义的filter方法,先看一段代码:
<!-- lang: php -->
//volt模板引擎服务
$di->set('voltService', function($view, $di) {
$volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
$volt->setOptions(array(
"compiledPath" => APP_PATH."/runtime/volt/",
"compiledExtension" => ".compiled"
));
//自定义过滤器
$compiler = $volt->getCompiler();
$compiler->addFilter('int', function($resolvedArgs, $exprArgs) {
return 'intval(' . $resolvedArgs . ')';
});
return $volt;
});
上面我们首先通过getCompiler拿到"编译器对象"$compiler,在$compiler中通过addFilter添加自定义的过滤器,上面的代码添加了一个叫做 "int" 的过滤器,其作用是将$var的值转化为整形。
第五,理解 当volt模板解析的时候,假如有一个标签{{ var|int }},这时候$compiler会检查对象中有没有int过滤器,显然是有的,那么会调用匿名函数,将返回值字符串放到echo 中,如上定义的int过滤器,最终返回的就是 intval($val),该标签最终被替换为
<!-- lang: php -->
<?php echo intval($val);?>
之后交由PHP解释并返回html代码到浏览器。 细节:$resolvedArgs是什么? 它是变量名和附加参数的拼接,比如上面int没有附加参数,那么它的值就是 '$var',注意这里是原义字符串,不要当成变量。 假设我们定义了一个过滤器 trim,我们想要 {{ var|trim('%') }}的结果为 trim($val,'%')的值,那么在定义过滤器匿名函数的时候就需要这么写
<!-- lang: php -->
$compiler->addFilter('concat', function($resolvedArgs, $exprArgs) {
return 'trim('.$resolvedArgs.')';
});
return $volt;
也就是$resolvedArgs这个变量的值是:$val,'%' 注意,这里是原义字符串,为了表示方便,省略了两边的单引号,$var变量名永远在开头。 最终标签被替换为php代码:<?php echo trim($val,'%');?>
那么$exprArgs是什么呢?它是一个数组,保存了每个参数的类型、原义字符串、所属脚本等等,一般用不到,不过特殊情况下可能使用,比如内置的 default过滤器,在 default('') 指定参数的时候,返回的是 empty($var)?'':$var 这里就需要从$exprArgs得到每个参数的信息。
自定义模板函数也是同样的原理,目的都是将标签转换为php代码,并保存到模板缓存中。
感言: 模板引擎并不神秘,只要理解其工作机制,代码实现原理,每个人都能实现自己的模板引擎。