打造自己的PHP模板引擎

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

一、首先我们来建立好相关的目录。

2014-10-04_164306.png

二、我们首先来编写好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; //执行混合后的文件 
         }
}

六、扩展发挥

  1. 如果传递给标签的变量是数组怎么办?原理跟上面是一样的,在display中使用preg_match_all() 执行对应的匹配规则判断是否存在数组变量,如果存在则用 preg_replace 执行对应的替换内容 如:<?php echo $this->engine_vars[“${1}”][${2}]
  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语句没有关闭!');
        }
    }
}
赞 (0) 打赏

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏