24小时服务热线:
186-6908-0178
当前位置:首页 >行业动态
微信公众平台

扫描左侧二维码关注驭云思创官方微信,实时了解公司最新动态!

ThinkPHP模板引擎实现和常见问题
云南驭云思创 时间:2019-02-22

模板引擎由来

早期做PHP开发WEB应用都是把PHP代码和HTML模板混在一起,模板引擎的诞生主要就是为了解决后端与前端的完全分离(现在来看其实是属于不完全分离)的问题,让开发与美工可以分工合作(虽然实际上最终模板工作大多仍然是由后端开发人员完成),从而提高开发效率和便于维护。

伴随着PHP的快速成长,模板引擎也越来越多,但大致分为解释型和编译型两种,目前主流的模板引擎大多数是编译型的,也就是会先把模板编译成PHP文件执行,只要模板文件本身不变化,就不需要重新编译,例如老牌的Smarty。解释型的模板引擎每次执行的时候都会进行模板解析流程,例如小强(tinybutstrong)。

ThinkPHP从一开始就内置了一个基于XML标签库技术的编译型模板引擎,早期参考自Struts,并且不断在汲取新的思想不断进化。

如何选择模板引擎

目前主流框架都带有模板引擎组件或者封装了模板引擎的实现,因此选择内置的解决方案是最佳之选,功能和稳定性都有保证。目前最流行的模板引擎当属Laravel自带的Blade模板引擎,以及Symfony自带的Twig模板引擎。

通过安装模板引擎扩展,你可以在ThinkPHP中轻松使用包括Angular、Twig和Blade在内的模板引擎,甚至完全不使用模板引擎而是直接用PHP文件作为模板。

因为近几年三大前端框架(React/Vue/Angular)的流行,前后端分离开发逐渐成为主流,因此从ThinkPHP5.0开始定位为API开发而设计,导致模板引擎的概念已经被弱化了。ThinkPHP5.1版本的模板引擎进行过一次内部的重构,使得模板标签更加易用和接近PHP语法。

至少对于大部分新的应用来说,应该选择更主流的前后端分离设计,尽量减轻服务端的压力,也更方便前后端单独测试。你会在市面上不经意的看到采用Vue和ThinkPHP的产品(之前几期的ThinkPHP开发者周刊曾经报道过几个)。如果是维护一些老项目尤其是内容管理产品的时候,仍然可能会用到模板引擎。

鉴于这种情况,下一个版本的ThinkPHP框架将不会内置模板引擎,但有需要使用模板引擎的开发者仍然可以使用官方独立出来的think-template类库,具体使用可以参考这篇文章

后面的篇幅,我们主要来总结下ThinkPHP内置的模板引擎的使用和技巧。

模板执行流程

系统内部的模板引擎调用关系如下:

视图(View) <=> 模板驱动(Driver) <=> 模板引擎(Template)

视图和模板引擎之间增加了一个驱动层,所以可以很方便的替换其它的模板引擎。通常我们在控制器中调用的assign/fetch等方法其实都是调用的thinkView类的方法。当然,如果有必要,你也完全可以直接在控制器中操作模板引擎类,只是不方便切换其它模板引擎。

以fetch方法为例,我们看下最终的调用过程:

thinkController->fetch();thinkView->fetch();thinkiewdriverThink->fetch();thinkTemplate->fetch();

如果你调用fetch方法的时候没有传入要渲染的完整模板文件名,则会在第三步的时候自动识别要渲染的模板文件。

很显然,最关键是最后一步,模板编译和执行的流程则全部由

thinkTemplate->fetch();

方法完成,这个环节大体又可以分成几个流程。

1、判断和读取页面渲染缓存

如果当前模板设置了页面输出缓存并且已经渲染输出过,如果是则会读取缓存中的输出内容直接输出。

if (!empty($this->config['cache_id']) && $this->config['display_cache']) {
   // 读取渲染缓存
   $cacheContent = $cache->get($this->config['cache_id']);

   if (false !== $cacheContent) {
       echo $cacheContent;
       return;
   }}

2、定位模板文件

定位实际的模板文件操作由模板引擎类的parseTemplateFile方法实现,这个方法的逻辑其实和视图驱动类的parseTemplate方法是类似的,如果最终的模板文件不存在则会抛出一个模板文件不存在的异常。

$template = $this->parseTemplateFile($template);

3、判断编译缓存

如果当前的模板文件已经编译过,会判断缓存是否还有效,有效的话就不用重复解析直接读取缓存的解析内容。由checkCache方法负责完成。

if (!$this->checkCache($cacheFile)) {
   // 缓存无效 重新模板编译
   $content = file_get_contents($template);
   $this->compiler($content, $cacheFile);}

4、模板编译并缓存

这一步骤是模板引擎最核心的环节,也是功能最复杂的地方,由compiler方法负责完成,主要是解析当前模板文件中的模板标签语法为PHP可执行代码,然后生成一个模板解析缓存文件,也就是所谓的模板“编译”,其中使用了大量的正则表达式替换技术,虽然正则解析有一定的性能开销,但得益于一次解析多次调用的缓存原理,基本上模板解析的性能开销不会影响实际使用的性能。

模板编译方法的关键代码是parse方法,parse方法负责对模板文件中的标签进行解析,然后写入编译缓存文件,编译缓存默认使用的是文件缓存,支持扩展。

5、读取编译缓存

模板编译的过程只是生成了模板编译缓存文件,并没有真正载入模板,这一步骤就是载入模板编译缓存,然后导入模板变量。实现方法可以参考think emplatedriverFile类的read方法。

public function read($cacheFile, $vars = []){
   $this->cacheFile = $cacheFile;

   if (!empty($vars) && is_array($vars)) {
       // 模板阵列变量分解成为独立变量
       extract($vars, EXTR_OVERWRITE);
   }

   //载入模版缓存文件
   include $this->cacheFile;}