PHP的模板引擎有很多,比如著名的Smarty,Dwoo等,今天我们来编写一个自己的迷你版的模板引擎,来了解一下其中的原理,这对以后使用其他模板引擎好处很多。
一、首先我们来建立好相关的目录。

二、我们首先来编写好index.tpl这个模板文件。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>兰亭楼</title> </head> <body> <div class="title">{$title}</div> <div class="content">{$content}</div> <div class="auth">{$auth}</div> </body> </html>
其中{$***}中的内容就是需要我们模板来解析的。
三、我们来编写控制文件IndexController.php。
<?php /* * 编写自己的模板引擎中的控制文件 */ require ‘miniengine.class.php’; //引入模板引擎文件 $engine = new MiniEngine(); //实例化模板类 $title = “”; $content = “”; $auth = “”; //定义三个变量 防止高版本PHP报错 $engine->assign(‘titile’,”创建自己的模板引擎”); //分配字符串给标签 $engine->assign(‘content’,”本文我们将学会创建自己的模板引擎”); $engine->assign(‘auth’,”兰亭楼”); $engine->display(“index.tpl”); //使用模板引擎来编译模板与控制文件
四、了解模板引擎的原理
为什么我们不先写模板引擎文件而是要先写好 模板 跟 控制文件呢?因为写好这两个文件,我们就会去思考,如何去编写MiniEngine.class.php这个类文件。
首先我们按照控制文件 IndexController.php中出现的方法来编写 MiniEngine.class.php中应该有的方法。总共有2个方法 assign 和 display。
代码如下:
/* * Description of engine *It's a simple PHP template engine * @author Lantinglou */ class MiniEngine { function assign($tagname,$value) { //两个参数分别是1.标签名称 2.分配值 } function display($tplname) { //参数 模板文件名称 } }
下一步该做什么呢?就是思考如何利用 MiniEngine.class.php 中的方法,让 index.tpl中的标签可以获取到 IndexController.php 赋予的值。
原理很简单,就是用PHP的正则表达式把模板中的标签替换成对应的PHP代码,然后产生HTML+PHP的混合文件,执行混合文件即可。
五、编写 MiniEngine.class.php类。
首先我们来编写 assigin方法:
function assign($tagname,$value) { //两个参数分别是1.标签名称 2.分配值 $this->engine_vars[$tagname] = $value; //这里是把 传入的 $value 赋给engine_vars这个数组中$tagname键 }
接下来是比较复杂的就是编写 display方法,要用到正则表达式。
function display($tplname) { //参数 模板文件名称 $file_path ='template/'.$tplname; //设置模板文件路径 $com_path = 'template_c/com_'.$tplname.'.php'; //设置混合文件路径 if (!file_exists($com_path) || filemtime($file_path) > filemtime($com_path))// 1.不存在混合文件才执行 2.模板文件修改时间晚于混合文件修改时间才执行 { $file_content = file_get_contents($file_path); //读取模板文件 $pattern = '/\{\s*$([a-zA-Z_][a-zA-Z0-9_]*)\s*\}/i'; //正则表达式匹配规则 用来匹配标签 ()中是要捕获的内容 $replacement = '<?php echo $this->engine_vars[""] ?>'; //替换内容 是捕获的内容 $new_file = preg_replace($pattern, $replacement, $file_content); //正则表达式替换 file_put_contents($com_path, $new_file); //输出替换后的混合文件到 混合文件路径 } include $com_path; //执行混合后的文件 }
最终产生的类文件如下:
<?php /** * Description of engine *It's a simple PHP template engine * @author Lantinglou */ class MiniEngine { function assign($tagname,$value) { //两个参数分别是1.标签名称 2.分配值 $this->engine_vars[$tagname] = $value; } function display($tplname) { //参数 模板文件名称 $file_path ='template/'.$tplname; //设置模板文件路径 $com_path = 'template_c/com_'.$tplname.'.php'; //设置混合文件路径 if (!file_exists($com_path) || filemtime($file_path) > filemtime($com_path))// 1.不存在混合文件才执行 2.模板文件修改时间晚于混合文件修改时间才执行 { $file_content = file_get_contents($file_path); //读取模板文件 $pattern = '/\{\s*$([a-zA-Z_][a-zA-Z0-9_]*)\s*\}/i'; //正则表达式匹配规则 $replacement = '<?php echo $this->engine_vars[""] ?>'; //替换内容 $new_file = preg_replace($pattern, $replacement, $file_content); //正则表达式替换 file_put_contents($com_path, $new_file); //输出替换后的混合文件 } include $com_path; //执行混合后的文件 } }
六、扩展发挥
- 如果传递给标签的变量是数组怎么办?原理跟上面是一样的,在display中使用preg_match_all() 执行对应的匹配规则判断是否存在数组变量,如果存在则用 preg_replace 执行对应的替换内容 如:<?php echo $this->engine_vars[“${1}”][${2}]
- 我想增加 foreach if等方法怎么办?原理还是跟上面一样。参考如下代码:
function parseForeach() { //foreach语句匹配模式 $modeForeach = '/\{foreach\s+key=([\w]+)\s+item=([\w]+)\s+from=$([\w]+)\}/'; $modeEndForeach = '/\{\/foreach\}/'; $modeVar = '/\{@([\w]+)\}/'; //在模板文件中匹配模式,如果匹配成功,则替换成相应的php语言中的foreach语句 if (preg_match($modeForeach, $this->_tpl)) { if (preg_match($modeEndForeach, $this->_tpl)) { $this->_tpl = preg_replace($modeForeach, "<?php foreach($this->_vars['$3'] as $$1=>$$2){?>", $this->_tpl); $this->_tpl = preg_replace($modeVar, "<?php echo $$1;?>", $this->_tpl); $this->_tpl = preg_replace($modeEndForeach, "<?php }?>", $this->_tpl); } else { exit('Foreach语句没有关闭!'); } } }
最新评论