模板引擎的思想是来源于MVC(Model View Controller)模型,即模型层、视图层、控制器层。
在Web端,模型层为数据库的操作;视图层就是模板,也就是Web前端;Controller就是PHP对数据和请求的各种操作。模板引擎就是为了将视图层和其他层分离开来,使PHP代码和HTML代码不会混杂在一起。因为当PHP代码和HTML代码混杂在一起时,将使代码的可读性变差,并且代码后期的维护会变得很困难。
大部分的模板引擎原理都差不多,核心就是利用正则表达式解析模板,将约定好的特定的标识语句编译成PHP语句,然后调用时只需要include编译后的文件,这样就讲PHP语句和html语句分离开来了。甚至可以更进一步将PHP的输出输出到缓冲区,然后将模板编译成静态的html文件,这样请求时,就是直接打开静态的html文件,请求速度大大加快。
简单的自定义模板引擎就是两个类,第一个是模板类、第二个是编译类。
首先是编译类:
public function compile() {
$this->c_include();
$this->c_var();
$this->c_staticFile();
file_put_contents($this->compile_file,$this->content);
}
// 处理include
public function c_include() {}
// 处理各种赋值和基本语句
public function c_var() {}
// 对静态的JavaScript进行解析
public function c_staticFile() {}
}
编译类的大致结构就是上面那样,编译类的工作就是根据配置的文件,将写好的模板文件按照规则解析,替换然后输出到文件中。这个文件的内容是PHP和html混杂的,但在使用模板引擎进行开发时并不需要在意这个文件,因为我们要编写的是模板文件,也就是html和我们自己定义的标签混合的一个文件。这样View和其他两层就分离开来了。
在这个自定义模板引擎中,我的左右定界符就是大括号,具体的解析规则就是放在__construct()中
// 替换后的字符串
$this->T_R[] = "<?php echo \$\1; ?>";
$this->T_R[] = "<?php foreach((array)\$\2 as \$K=>\$V) { ?>";
$this->T_R[] = "<?php foreach((array)\$\2 as &\$\3) { ?>";
$this->T_R[] = "<?php } ?>";
$this->T_R[] = "<?php if(\1) { ?>";
$this->T_R[] = "<?php } elseif(\2) { ?>";
$this->T_R[] = "<?php } else { ?>";
$this->T_R[] = "<?php echo \$\1; ?>";
上面的解析规则包含了基本的输出和一些常用的语法,if、foreach等。利用preg_replace函数就能对模板文件进行替换。具体情况如下
<input value="{var}">
{ /loop }
// 解析后
<?php echo $data; ?>
<?php foreach((array)$vars as $K=>$V) { ?>
<?php if( $V == 1) { ?>
<input value="<?php echo $V; ?>">
<?php } elseif( $V == 2) { ?>
<input value="123123">
<?php } else { ?>
<input value="sdfsas是aa">
<?php } ?>
<?php } ?>
<?php foreach((array)$vars as &$var) { ?>
<input value="<?php echo $var; ?>">
<?php } ?>
编译类的工作大致就是这样,剩下的include和对JavaScript的解析都和这个大同小异。
然后就是模板类
public function __construct($array_config=array()) {}
// 单步设置配置文件
public function setConfig($key,$value=null) {}
// 注入单个变量
public function assign($key,$value) {}
// 注入数组变量
public function assignArray($array) {}
// 是否开启缓存
public function needCache() {}
// 如果需要重新编译文件
public function reCache() {}
// 显示模板
public function show($file) {}
}
整个模板类的工作流程就是先实例化模板类对象,然后利用assign和assignArray方法给模板中的变量赋值,然后调用show方法,将模板和配置文件传入编译类的实例化对象中然后直接include编译后的PHP、html混编文件,显示输出。简单的流程就是这样,详细的代码如下
<div class="jb51code">
<pre class="brush:php;">
public function show($file) {
$this->file = $file;
if(!is_file($this->path())) {
exit("找不到对应的模板文件");
}
$compile_file = $this->_arrayConfig['compile_dir']. md5($file). '.PHP';
$cache_file = $this->_arrayConfig['cache_dir']. md5($file). $this->_arrayConfig['suffix_cache'];
// 如果需要重新编译文件
if($this->reCache($file) === false) {
$this->_compileTool = new CompileClass($this->path(),$this->_arrayConfig);
if($this->needCache()) {
// 输出到缓冲区
ob_start();
}
// 将赋值的变量导入当前符号表
extract($this->_value,EXTR_OVERWRITE);
if(!is_file($compile_file) or filemtime($compile_file) < filemtime($this->path())) {
$this->_compileTool->vars = $this->_value;
$this->_compileTool->compile();
include($compile_file);
}
else {
include($compile_file);
}
// 如果需要编译成静态文件
if($this->needCache() === true) {
$message = ob_get_contents();
file_put_contents($cache_file,$message);
}
}
else {
readfile($cache_file);
}
}