Smarty模板是业内最知名的PHP模板引擎之一,它实现了前后端的分离,使PHP程序员和前端程序员各行其事,方便了多人的分工合作。从这里我们可以看出,模板引擎集中解决了代码和表现分离这件事情。但是,实现项目开发过程中,绝大多数情况下都会使用一款框架,当然每个PHP框架也都实现了自己的模板引擎。虽然我没有精读过Smarty模板的源码,但是我对使用过的框架源码还有一定的基础,所以对模板引擎原理有一定的理解。
模板语法
每个模板引擎都有一套自己约束的标签写法和解析规则,如:Smarty模板默认定界符是"{ }
",ThinkPHP的默认定界符也是"{ }
",Laravel模板默认定界符是"{{ }}
",也有一些模板引擎使用"<{ }>
"作为默认的定界符。
处理流程
核心方法
模板类的主要实现assign
和display
两个基础方法。
/**
* 模板变量赋值
* @access public
* @param mixed $name 变量名
* @param mixed $value 变量值
* @return $this
*/
public function assign($name, $value = '')
{
if (is_array($name)) {
$this->data = array_merge($this->data, $name);
} else {
$this->data[$name] = $value;
}
return $this;
}
/**
* 渲染模板文件
* @access public
* @param string $template 模板文件
* @param array $data 模板变量
* @return void
*/
public function display($template, $data = [])
{
// 判断模板文件是否存在
if (!is_file($template))
{
exit('template not exists:' . $template);
}
$vars = array_merge($this->data, $data);
/**此处约省略1000000字*/
extract($vars, EXTR_OVERWRITE);
include $template;
}
模板解析类的主要实现parse
方法
/**
* 模板解析
* @access public
* @param string $content 要解析的模板内容
* @return true
*/
public function parse(&$content)
{
$content = preg_replace($pattern, $replacement, $content);
/**此处约省略1000000字*/
return true;
}
整体过程
- 请求从入口进来到达控制器,实例化模板类,通过
assign
方法注入要展示的数据,通过display
方法绑定要展示的模板。 - 在模板类中引入相关配置,如:定界符、模板路径、缓存类型、缓存路径、缓存时间。实例化模板解析类,调起模板编译方法。
- 在模板解析类中,通过缓存类型、编译文件、更新模板文件否、过期时间等等判断决定,是否生成
PHP+HTML
的混合文件,如果需要生成,就调起parse
方法按约定的规则解析标签内容,写入编译缓存文件。 - 最后,分解[
extract
]模板变量,载入[include
]缓存文件,显示页面数据。
由于我源出某派,不得不说某派的产品框架虽然有自己的模板引擎,并且号称语法上基本忠于Smarty
模板,但是最后一步绝对使用eval
执行编译缓存文件,这与Smarty
模板不同。并且在尔后的模板类中将assign
和display
升级成为一个page("template.html", $pagedata);
方法,把参数注入改成由page
方法的第二个参数实现。而 Smarty
模板最终实现方法如下:
这里HHVM_VERSION
常量全局没有定义,所以它和其他框架一样,默认使用include
载入缓存文件。这里不得不喷一下SHOPXC
的模板类,真是相当的丑陋和简单,连extract
都不愿意使用使用,导致最后在模板中每个模板变量都得使用$output['xxx']
来接收。注:不是恶意评论SHOPXC
产品,只是这种做法实现别扭。
综上所述: 实现一款简易的PHP模板引擎除了良好的OOP
基础外,还需要掌握include, extract, is_file, file_exists, filemtime, file_get_contents, file_put_contents, preg_replace, str_replace, ob_start, ob_get_clean, eval, array_merge
等方法的使用,其中正则表达式尤为重要。
--END
--