仿制smarty模板

1 篇文章 0 订阅
1 篇文章 0 订阅

仿制smarty的模板技术,写了一个自己的mySmarty,供大家参考,和玩转

这是一个仿制的smarty模板
可以给简单变量以及数组变量赋值
在模板中可以使用foreach循环以及if条件语句


demo.php

<?php
    require_once 'MySmarty/MySmarty.class.php';
require_once 'MySmarty/MySmartyCompile.class.php';
    $minismarty = new MySmarty();
    //缓存开关
    $minismarty->caching = true;
    
    //定义变量
    $webname = 'my Smarty test';
    $message = array(
        'title'=>'this is title',
        'content',
        'userMessage'=>array(
            array('name'=>'this is johnny','gender'=>1),
            array('name'=>'this is lei','gender'=>1),
            array('name'=>'this is nicolas','gender'=>1),
            array('name'=>'this is ada','gender'=>0),
        ),
        'bookMessage'=> array(
            array('name'=>'this is johnny','id'=>1),
            array('name'=>'this is lei','id'=>2),
            array('name'=>'this is nicolas','id'=>3),
            array('name'=>'this is ada','id'=>4),
        ),
        array(
            'friendMessage'=>array(
                array('name','id'),
                array('johnny',1),
                array('nicolas',2),
                array('leiyuqing2222222222222',3),
            ),
        ),
        
    );
    
    $message1 = array(
        'userMessage'=>array(
            array('name'=>'this is johnny','gender'=>1),
            array('name'=>'this is lei','gender'=>1),
            array('name'=>'this is nicolas','gender'=>1),
            array('name'=>'this is ada','gender'=>0),
        ),
        'bookMessage'=> array(
            array('name'=>'this is book1','id'=>1),
            array('name'=>'this is book2','id'=>2),
            array('name'=>'this is book3','id'=>3),
            array('name'=>'this is book4','id'=>4),
        ),
    );
    
    $simpleArr = array('cc','dd','ee');
    $dyadicArr = array(
        array('ff','gg','hh'),
        'key'=>array('ii','jj','kk'),
    );
    $keyArr = array(
        'aa'=>'AA',
        'bb'=>'BB',
        'cc'=>'cc',
    );

    $userMessage = array(
        array('name'=>'this is johnny','gender'=>1),
        array('name'=>'this is lei','gender'=>1),
        array('name'=>'this is nicolas','gender'=>1),
        array('name'=>'this is ada','gender'=>0),
    );
    $gender = 0;
    $minismarty->assign('webname', $webname);
    $minismarty->assign('message', $message);
    $minismarty->assign('message1', $message1);
    $minismarty->assign('userMessage', $userMessage);
    $minismarty->assign('simpleArr', $simpleArr);
    $minismarty->assign('dyadicArr', $dyadicArr);
    $minismarty->assign('keyArr', $keyArr);
    $minismarty->assign('gender', $gender);
    //启动编译模板文件
    $minismarty->display('demo.tpl.html');


MySmarty/MySmarty.class.php

<?php

class MySmarty {
    //模板文件
    public $template_dir = 'templates';
    //编译文件
    public $compile_dir = 'templates_c';
    //缓存文件
    public $cache_dir = 'cache';
    //模板变量
    public $_tpl_var = array();
    //是否开启缓存
    public $caching = false;
    
    public $left_delimiter = "{";
    
    public $right_delimiter = '}';


    public function __construct() {
        $this->checkDir();
    }

    //检查目录是否建好
    private function checkDir() {
        if (!is_dir($this->template_dir)) {
            exit('模板文件目录templates不存在!请手动创建');
        }
        if (!is_dir($this->compile_dir)) {
            exit('编译文件目录templates_c不存在!请手工创建!');
        }
        if (!is_dir($this->cache_dir)) {
            exit('缓存文件目录'.$this->cache_dir.'不存在!请手工创建!');
        }
    }
    
    //模板变量注入方法
    public function assign($tpl_var, $var = null) {
        if (isset($tpl_var) && !empty($tpl_var)) {
            $this->_tpl_var[$tpl_var] = $var;
        } else {
            exit('模板变量名没有设置好');
        }
    }
    
    //文件编译
    public function display($file) {
        //模板文件
        $tpl_file  = $this->template_dir.'/'.$file;
        if (!file_exists($tpl_file)) {
            exit('ERROR:模板文件不存在!');
        }
        //编译文件
        $parse_file = $this->compile_dir.'/'.md5($file).$file.'.php';
        //只有当编译文件不存在或者是模板文件被修改过了
        //才重新编译文件
        if (!file_exists($parse_file) || filemtime($parse_file) < filemtime($tpl_file)) {
            $compile = new MySmartyCompile($tpl_file);
            $compile->parse($parse_file);
        }
        // 如果缓存没有开启
        if (!$this->caching) {
            include $parse_file;
            return ;
        }
        
        //开启了缓存才加载缓存文件,否则直接加载编译文件
        //缓存文件
        $cache_file = $this->cache_dir.'/'.md5($file).$file;
        //只有当缓存文件不存在,或者编译文件已被修改过
        //重新生成缓存文件
        if (!file_exists($cache_file) || filemtime($cache_file) < filemtime($parse_file)) {
            ob_start();
            //引入缓存文件
            include $parse_file;
            //缓存内容
            $content = ob_get_contents();
            ob_end_clean();
            //生成缓存文件
            if (!file_put_contents($cache_file, $content)) {
                exit('缓存文件生成出错!');
            }
        }
        //载入缓存文件
        include $cache_file;
    }
}


MySmarty/MySmartyCompile.class.php

<?php

class MySmartyCompile {
    //模板内容
    private $content = '';
    
    //构造函数
    public function __construct($tpl_file) {
        $this->content = file_get_contents($tpl_file);
    }
    

    //解析普通变量,如把{$name}解析成$this->_tpl_var['name']
    public function replaceSimpleVar() {
        /**
         * 这个正则表达式是指匹配简单变量
         * 不匹配$xxx.或者$xxx[或者$xxx]既不匹配对象和数组
         * 也不匹配关键字,比如{foreach}等等
         */
        $pattern = '/\{\$(\w+(?!\.|\[|\]))\}/';
        if (preg_match($pattern, $this->content)) {
            $this->content = preg_replace($pattern, '<?php echo \$this->_tpl_var["$1"]?>', $this->content);
        }
    }
    
    public function replaceVar() {
        /**
         *   $webname.name.cc.dd
         *   $webname.name.cc[1].dd
         *   $author[1][12].name[0]
         *   $title[1]
         *   $title.dd
         *   $title.dd[1]
         */
        // \$(\w+(\.|(\[\d+\])+)+)+(\w+(\[\d+\])?)?
        // \$((\w+(\.|(\[\d+\])+)+)+(\w+(\[\d+\])?)?)
//        $pattern = '/\{\$((\w+(\.|(\[\d+\])+)+)+(\w+(\[\d+\])?)?)\}/';
        $pattern = '/\{\$([^\{\}]+)\}/';
        $matches = array();
        preg_match_all($pattern, $this->content,$matches);
        for($i = 0; $i < count($matches[1]); $i++) {
            $str = $this->parseVar($matches[1][$i]);
            $str = "<?php echo ".$str."?>";
            $this->content = str_replace($matches[0][$i], $str, $this->content);
        }
    }
    

    /**
     * 传进来的是一个变量的字串,比如$name,$name[1],$message.name[2]之内的
     * 把这个字符串转化成我们的$this->_tpl_var开头的变量
     * 比如$name => $this->_tpl_var['name']
     * $name[1] =>$this->_tpl_var['name'][1]
     * $message.name[2] => $this->_tpl_var['message']['name'][2]
     * 返回传化后的字符串
     * @param type $arrStr
     * @return str
     */
    public function parseVar($arrStr) {
        
        $str = '$this->_tpl_var';
        $explodeFlag = '.';
        $subStr = explode($explodeFlag, $arrStr);
        for ($i = 0; $i < count($subStr); $i++) {
            $pattern = '/(\w+)((\[\d+\])+)/';
            if(!preg_match($pattern, $subStr[$i], $matches)) {
                $str .= "["."'".$subStr[$i]."'"."]";
                continue;   
            }
            $str .= "["."'".$matches[1]."'"."]".$matches[2];
        }
        return $str;
    }
    
    /**
     * 这个函数的作用是替换模板文件中的foreach语句块,和if语句块
     */
    public function replaceSpecialWords() {
        /**
         * 这个正则表达式匹配{{XXX}}里面的东西,既if语句块和foreach语句块
         * 这个xxx不能包含{{
         * 如果这个语句块是if,那个就要以{/if}结尾,如果是foreach,那么就以{/foreach}结尾
         */
        $pattern = '/\{(\{(\w+)(?:(?!\{\{).|\n)+\{\/\2\})\}/';
        $matches = array();

        preg_match_all($pattern, $this->content, $matches);
        for($i = 0; $i < count($matches[1]);$i++) {
            $str = $this->parseSpecialWords($matches[1][$i]);
            $this->content = str_replace($matches[0][$i], $str, $this->content);
        }
    }

    public function parseSpecialWords($str) {
        /**
         * 匹配出{}或是{{}或是{}}里面的字符串
         */
        $pattern = '/\{([^\{\}]+)\}/';
        preg_match_all($pattern, $str, $matches);
        /**
         * 匹配出第一行里面的变量,并且将他替换掉
         */
        $reg = '/\((\$([^\s]+))/';
        preg_match($reg, $matches[0][0], $var);
        $tmp = $this->parseVar($var[2]);
        $matches[1][0] = str_replace($var[1], $tmp, $matches[1][0]);
        /**
         * flag用来记录这个foreach语句还是if语句块
         * flag = fo or if
         */
        $flag = substr($matches[1][0], 0,2);
        /**
         * replace 根据每一行情况不同,赋不同的php语句,替换掉伪代码
         */
        $replace = '';
        $count = count($matches[1]);
        for ($i = 0; $i < $count; $i++) {
            /**
             * 匹配循环里面需要输出的变量
             */
            $reg1 = '/^\$\w+$/';
            if (preg_match($reg1, $matches[1][$i])) {
                /**
                 * 找到需要输出的变量
                 */
                $replace = "<?php echo ".$matches[1][$i]." ?>";
            } else if($matches[1][$i] == $matches[1][$count-1] || substr($matches[1][$i],0,1) == '/'){
                /**
                 * 找到结束的地方,用}替换掉
                 */
                $replace = "<?php } ?>";
            } else if(substr($matches[1][$i],0,4) == 'else'){
                /**
                 * 如果是else开头,那么应该在else前面加上一个}然后再在语句后面加上一个{
                 */
                $replace = "<?php }".$matches[1][$i]."{ ?>";
            } else if($flag == 'if' && substr($matches[1][$i],0,2) == 'fo'){
                /**
                 * 这种情况是if语句块里面嵌套foreach语句,因为if的条件变量和foreach的循环变量一般不会相同所以要特殊考虑
                 * 既如果用一个flag来控制if怎么走,然后foreach里面的循环变量数组如果是message,那么我们得要找到message的值
                 */
                //示例字符串foreach ($dyadicArr as $value) 
                $reg = '/\((\$([^\s]+))/';
                preg_match($reg, $matches[0][$i], $var);
                $tmp = $this->parseVar($var[2]);
                $matches[1][$i] = str_replace($var[1], $tmp, $matches[1][$i]);
                $flag = 'fo';
                $replace = "<?php ".$matches[1][$i]."{ ?>";
            } else {
                /**
                 * 找到循环开始的地方,应该在语句后面加上一个{
                 */
                $replace = "<?php ".$matches[1][$i]."{ ?>";
            }
            $str = str_replace($matches[0][$i], $replace, $str);
        }
       return $str;
    }
    
    //模板编译
    public function parse($parse_file) {
        ini_set('pcre.backtrack_limit', -1);
//        ini_set('pcre.recursion_limit', 99999);
        //解析foreach和if语句块
        $this->replaceSpecialWords();
        //解析变量
        $this->replaceVar();
        //编译完成后,生成编译文件
        
        if (!file_put_contents($parse_file, $this->content)) {
            exit('编译文件生成出错!');
        }
    }
}


templates/demo.tpl.html

<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>**********这个是一个简单变量**************</p>
        {$webname}
        <p>**********这是一个数组里面的元素************</p>
        {$message[1].friendMessage[1][0]}
        <p>*************这里是一个If语句块***********</p>
        {{if($gender == 1)}
            <p>male</p>
        {else}
            <p>female</p>
        {/if}}
        <p>***************这里是一个简单的for循环**************</p>
    {{foreach ($dyadicArr as $value)}
        {foreach ($value as $v)}
            <p>{$v}</p>
        {/foreach}
    {/foreach}}
    <p>*************这是foreach循环里面嵌套一个If语句块********</p>
    {{foreach ($userMessage[1] as $k => $v)}
        {if($v == 1)}
            <p>{$k} => male</p>
        {else if($v == 0)}
            <p>{$k} => female</p>
        {else }
            <p>{$k} => {$v}</p>
        {/if}
    {/foreach}}
    <p>*******这里是3重foreach语句块嵌套********</p>
    {{foreach ($message1 as $key1 => $value1)}
        {$key1} =>
        {foreach ($value1 as $key2 => $value2)}

            {foreach ($value2 as $key3 => $value3)}
                <p>{$key3}=>{$value3}</p>
            {/foreach}
        {/foreach}
    {/foreach}}
    </body>
</html>


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值