zoukankan      html  css  js  c++  java
  • php模板引擎的原理与简单实例

    模板引擎其实就是将一个带有自定义标签的字符串,通过相应的规则解析,返回php可以解析的字符串,这其中正则的运用是必不可少的,所以要有一定的正则基础。
    总体思想,引入按规则写好的模板,传递给标签解析类(_HtmlTag)进行解析,再把解析好的字符串传递给php进行解析渲染输出
    首先定义了一个_HtmlTag类:

    class _HtmlTag{
      protected $taglist = "if|elseif|else|loop|for|while|=|:=|:e|:html|:";
      protected $string;
      protected $tpldir;
      function __construct($dir, $tpl) {
        $this->findReg = "/\{[ ]*($this->findReg)(\s*[\{\}]+\s*)?\}/i";
        $this->tpldir = $dir;
        $this->tpl =$tpl;
      }
    }
    • 先解析下render方法
      在render方法中对传入的字符串$s,进行相应的替换渲染,用到了extends方法,import方法,''开头的各种过滤方法,后面会一一进行详解.

      function render($s) {
      $s = str_replace(['<--{', '}-->'], ['{','}'], $s);
      if (strpos($s, "{extends") !== false) {
        $s = $this->_extends($s);
      } 
      $s = preg_replace('/[\n\r]+/s', "\n", $s);
      $s = preg_replace('/\r+/s', "", $s);
      $s = preg_replace('/\t+/s', "\t", $s);
      
      $this->string = str_replace(["\{", "\}"], ["\HKH1", "\HKH2"], $s);
      preg_match_all("/\{[ ]*(import)(\s*[^\{\}]+\s*)?\}/i", $s, $d);
      foreach($d[0] as $k => $v) {
        call_user_func_array(array($this, "_import"), array($v, $d[1][$k], $d[2][$k]));
      }
      static $m = array("=" => "_defaultEcho", ":=" =>" _xssEcho", ":e" => "_htmlencodeEcho", ":" => "_echo", ":html" => "_htmldecodeEcho");
      preg_match_all($this->findReg, $this->string, $d);
      foreach ($d[0] as $k => $v) {
        if (isset($m[$d[1][$k]])) {
            $medth = $m[$d[1][$k]];
         } else {
            $medth = '_' . $d[1][$k];
         }
          call_user_func_array(array($this, $medth), array($v, $d[1][$k], $d[2][$k]));
      }
      }
    • _extends方法

      使用 个方法是为了判断是否有继承相应的模板文件,在子文件中用{extends 模板名},一般只要填写文件名,如index,系统会自动去构造完整的文件路径,一般在上一级目录,这个在realTpl()方法时会有详解
      function _extends($tpl) {
      preg_match("/\{\s*extends\s+([^\{\}]+?)\s*\}/i", $tpl, $d);
      list($search, $arg) = $d;
      if (stripos($arg, ".") !==0 ) {
        $arg = '../' . $arg;
      }
      $file = $this->tpl->me->realTpl($arg . ".htm");
      $basetpl = file_get_contents($file);
      preg_match_all("/\{block\s+([^\{\}]+?)\s*\}(.+)\{\/block\}/s", $tpl, $ds);
      foreach($ds[1] as $n => $name) {
        $basetpl = preg_replace("/\{block\s+name={$name}\s*\}/", $ds[2][$n], $basetpl);
      }
      $basetpl = preg_replace("/\{block\s+name=.+?\}/", ' ', $basetpl);
      return $basetpl;
      }
    • _import 方法

      _import 方法是用来对{import 文件名}标签进行解析,默认也是上层目录,会把该文件的内容解析到当前标签位置,同时支持使用:函数名|参数1, 参数2,...的方式进行字符串的回调.这中的display方法后面会有详解
      function _import ($search, $tag, $arg) {
      $arg = trim($arg);
      if (stripos($arg, ".") !== 0) {
        $arg = '../' . $arg;
      }
      $file = $this->tpl->me->realTpl($arg . ".htm");
      if (file_exists($file)) {
        $this->string = str_replace($search, file_get_contents($file), $this->string);
      return;
      } else {
          if (stripos($file, "$") !== false) {
              $this->string = str_replace($search, '<? include $this->tpl->display("' . $arg . '"); ?>', $this->string);
               return;
          }
          if (stripos($arg, '|') !== false) {
             list($func, $tmp) = explode("|", trim($arg));
             $kw = explode(', ', $tmp);
          } else {
             $func = trim($arg);
              $kw = array();  
          }
          if (function_exists($func) {
            $tpl_str = call_user_func_array($func, $kw);
             $this->string = str_replace($search, $tpl_str, $this->string);
              return;
          } else {
            $this->string = str_replace($search, $arg, $this->string);
          return;
          }
          system_error($this->tpldir . trim($arg) . ".htm不存在");
      }
      }
    • 现在来看一个标签函数_loop,将标签{loop $data $k $v}或{loop $data $v}替换,实现模板循环,其中的parseAttr方法接下来将详解

      function _loop($search, $tag, $arg) {
      list($attr, $arg) = $this->parseAttr($arg);
      $d = preg_split("/\s+/", trim($arg));
      if (count($d) == 3) {
         $data = $d[0];
         $k = $d[1];
         $v = $d[2];
         $s = "<? \n if(!empty($data)) {\n \t foreach(" . $data . " as {$k} => {$v}){";
      } elseif (count($d) == 2) {
        $data = $d[0];
        $v = $d[1];
        $s = "<? \n if(!empty($data)){\n \t
            foreach(". $datsa . " as {$v}) {";
      }
      
      if (isset($attr['counter'])) {
        $s = "\n \t\t<? if (!isset({$attr['counter']})) { {$attr['counter']} = 0;} ?> \n \t\t" . $s . "\n \t\t{$attr['counter']}++; \n ?>;
      } else {
        $s .= "?>";
      }
      $this->string = str_replace($search, $s, $this->string);
      $this->string = str_replace("{/loop}", "<? \t}\n}?>", $this->string);
      }
    • parseAttr方法

      该方法用来解析标签 的参数,返回的第一个参数是值形式字符串,第二个是键值数组,如{loop $data $k $v}返回$data $k $v 组成 的字符串和一个空数组,{if $k = $v}则返回空字符串和和数组[$k => $v];
      function parseAttr($s) {
      $reg = '/([a-zA-Z0-9_])+\s*=\s*([a-zA-Z0-9_\$]+)/i';
      $arg = preg_replace($reg, '', $s);
      preg_match_all($reg, $s, $d);
      $arr = array();
      foreach($d[1] as $key => $value) {
        $arr[trim($value)] = trim($d[2][$key]);
      }
      return array($arr, $arg);
      }
    • _if 方法

      将{if}{/if}标签解析成原生 的php,简单明了,不多解释
      function _if ($search, $tag, $arg) {
      $replace[0] = "<? if($arg){?>";
      $replace[1] = "<? }?>";
      $this->string = str_replace(array($search, "{/$tag}"), $replace, $this->string);
      }
    • _else方法
      将{else}解析成原生的php
      function _else($search, $tag, $arg) {
      $this->string = str_replace("{else}", "\n" . '<? } else {?>', $this->string);
      }
    • _elseif方法
      将{elseif}解析成原生的php
      function _elseif($search, $tag, $arg) {
      $this->string = str_replace($search, "<? }elseif($arg){?>", $this->string);
      }
      

        

    • _for 方法
      function _for($search, $tag, $arg) {
      $s = trim(trim(preg_replace('/\s+/', ' ; ', $arg)), ';');
      $this->string = str_replace($search, "<? for($s){?>", $this->string);
      $this->string = str_replace("{/for}", "<? \t\n}?>", $this->string);
      }
      

        

    • _while方法
      function _while($search, $tag, $arg) {
      $this->string = str_replace($search , "<? while($aeg){ ?>", $this->string);
      $this->string = str_replace("{/while}", "<? \t\n} ?>", $this->string);
      }
      

        

    • _end方法
      通过这个方法对其$this->string进行修改可以对变量进行如下标签定义:
      {$val}{$arr[$key]}{$arr['key']}{:val()}{$arr.key}{$arr.$key}

      function _end() {
      //{$val}标签解析 
      $this->string = preg_replace('/\{(\$\w+.*)\}/is', "<?=\\1;?>", $this->string);
       //{:$val()}标签解析
      $this->string = preg_replace('/\{\:\s*(\$?\w+.*?)\}/is', "<? =\\1;?>", $this->string);
      //将$val[arg]解析成$val['arg']
      $this->string = preg_replace('/\$([_a-z]+\w*)\[([_a-z]+\w*)\]/is', "$\\1['\\2']", $this->string);
      //支持点号访问数组。如array['key']可以用array.key访问
      $this->string = preg_replace('/\$([_a-z]+\w*)\.(\$[_a-z]+\w*)/is', "$\\1[\\2]", $this->string);
      $this->string = preg_replace('/\$([_a-z]+\w*)\.([_a-z]+\w*)/is', "$\\1['\\2']", $this->string);
      $this->string = preg_replace(array("/<?/", "/<?phps*=/"), array("<?php ", "<?php echo "), $this->string);
      }
      

        

    • _result方法,做最后处理,返回字符串,_end方法在上面已经解释过了

      function result() {
      $this->_end();
      $this->_baseParse($this->stirng);
      return str_replace(array("\HKH1", "\HKH2"), array("\{", "\}"), $this->string);
      }
      

        

      下面再来介绍一下几个过滤的方法

    • _htmlencodeEcho方法

      可以用此种形式对变量进行htmlspecialchars式的过滤spspecialchars函数后面会有介绍{:e $val}

      function _htmlencodeEcho($search, $tag, $arg) {
      if (stripos($arg, '$')) {
        $this->string = str_replace($search, "<? echo sphtmlspecialchars(!empty($arg)?$arg:''))?>", $this->string);
      } else {
        $this->string = str_replace($search, "<? echo sphtmlspecialchars($arg)?>", $this->string);
      }
      }
      

        

    • _htmldecodeEcho方法
      _htmldecodeEcho($search, $tag, $arg) {
      if (stripos($arg, '$')) {
        $this->string = str_replace($search, "<? echo htmlspecialchars_decode(!empty($arg)?$arg:'')?>", $this->string);
      } else {
        $this->string = str_replace($search, "<? echo htmlspecialchars_decode($arg)?>", $this->string);
      }
      }
      

        

    • _defaultEcho方法
      function _defaultEcho($search, $tag, $arg) {
      if (stripos($arg, ',') == false) {
        $arg . = ", ' ' ";
        list($v, $default) = explode(",", $arg);
        $this->string = str_replace($search, "<? echo empty($v)?$default:$v; ?>", $this->string);
      }
      }
      

        

    • _xssEcho方法

      _xssEcho($search, $tag, $arg) {
      $this->string = str_replace($search, "<? echo xssRemove($arg); ?>", $this->string);
      }
      

        

      _echo方法

      function _echo($search, $tag, $arg){
      $this->string = str_replace($search, "<? echo $arg;?>", $this->string);
      }
      

        

    • _baseParse方法,这个对css和js的引入进行处理

      function _baseParse($s) {
      if (strpos($s, "{loadCss") !== false) {
        $s = $this->_loadCss($s);
      }
      if (strpos($s, "{loadJs") !== false) {
        $s = $this->_loadJs($s);
      }
      return $s;
      }
      

        

    • _loadJs方法
      function _loadJs($tpl) {
      preg_match_all('/{loadJss+([^{}]+?)s*}/i', $tpl, $match);
      include "jsmin.php";
      foreach($match[1] as $k => $js) {
        $jsstr = "";
        $cdn = "";
        $if (stripos($js, ',') !== false) {
            $filename = "";
            $md5 = "";
            $js_content = "";
            foreach(explode(',', $js) as $j) {
              $filename .= basename($j, '.js') . ',';
              $md5 .= self::fileMd5($j);
              if (stripos($j, '.min.') == false) {
                $js_content .= JSMin::minify(file_get_contents(WEB_ROOT . $j));
              } else {
                $js_content .= file_get_contents(WEB_ROOT . $j);
              }
              $js_content .= ';'
            }
          $md5 = md5($md5);
          $filename = dirname($j) . '/' .trim($filename, ',') . '.js';
          self::parseJs($js_content, $filename);
          $jsstr = "<script src="{$cdn}{$filename}?v={$md5}"></script>";
        } else {
          $md5 = self::fileMd5($js);
          if (stripos($js, ".min.") === false) {
            $js = self::pareJs(JSMin::minify(file_get_contents(WEB_ROOT . $js)), dirname($js). '/' .basename($js, '.js') . '.min.js');
          }
          $jsstr = "<script src="{$cdn}{$js}?v={$mdt}"></script>";
        }
        $tpl = str_replace($match[0][$k], $jsstr, $tpl);
      }
      return $tpl;
      }
      

        

      _loadCss方法
      function _loadCss($tpl) {
      preg_match_all('/{loadCsss+([^{}]+)s*}/i' , $tpl , $match);
      $cdn = "";
      foreach($match[1] as $k => $css) {
        $cssstr = "";
        if (stripos($css, ',') !== false) {
          $filename = "";
          $md5 = "";
          $css_content = "";
           foreach($explode(',', $css) as $css) {
               $filename .= basename($css, '.css') . ',';
               $md5 = self::fileMd5($css);
                $css_content .= file_get_contents(WEB_ROOT .$css);   
           }
           $md5 = md5($md5);
           $filename = dirname($css) . '/' .trim($filename, ',') . '.css';
            self::parseCss($css_content, $filename);
             $cssstr = "<link rel="stylesheet" href="{$cdn}{$filename}?v={md5}">";
        } else {
          $md5 = self::fileMd5($css);
           if (stripos($css, ".min.") == false) {
              $css = self::parseCss(file_get_contents(WEB_ROOT . $css), dirname($css) . '/' .basename($css, '.css') . '.min.css');
            }
            $cssstr .= "<link rel="stylesheet" href="{$cdn}{$css}?v={$md5}">";
        }
        $tpl = str_replace($match[0][$k], $cssstr, $tpl);
      }
      return $tpl;
      }
      

        

    • 上面两个函数都用到了各自的parseCss,和parseJs,让我们来看下
      static function parseCss($css_content, $filename) {
      $css_content = preg_replace("/[
      	]/", '', $css_content);
      $css_content = preg_replace("/ +/", ' ', $css_content);
      sp_file_pu_contents(WEB_ROOT . $filename, $css_content);
      return $filename;
      }
      static function parseJs($js, $filename) {
      sp_file_put_content(WEB_ROOT . $filename, $js);
      return $filename;
      }
      

        


    上面的_html类和他的方法对传入的字符串进行了标签到原生 Php的替换,可以传入相应的模板,也可以在其中自定义自己的过滤方法。

    下面我们再来看一个template类:

    定义这个类来对模板进行管理,这里会用到上面的_html类,基本定义如下,接下来会介绍其中的一些方法

    class template {
      private $tpl_cache_dir;
      public $ext = ".htm";
      public $default_tpl_dir = false;
      public $me;
    
      function _construct($tpl_cache_dir, $me) {
        $this->sets($tpl_cache_dir);
        $this->me = $me;
      }
      function sets($tpl_cache_dir) {
        $this->tpl_cache_dir = $tpl_cache_dir;
        if (SP_DEBUG == 1) {
          spmkdir($this->tpl_cache_dir);
        }
      }
      function default_template($dir) {
        $this->default_tpl_dir = $dir;
      }
    }
    

      

    • getString方法

      在之前 开启ob_start(),然后获取这之间的ob数据,试用于框架中

      function getString() {
      $s = ob_get_contents();
      ob_clean();
      return $s;
      }
      

        

    • display方法

      function display($tpl, $return = 0, $script = SCRIPT) {
      $tpl .= $this->ext;
      $tpl_name = $this->tpl_cache_dir . $script . '_' . str_replace(['/', '\\'], '_', $tpl);
      if (SP_DEBUG){
        if (SP_DEBUG == 1 || (SP_DEBUG == 2 && !file_exists($tpl_name))) {
          $this->compiles($tpl_name, $tpl);
        }
      }
      return $tpl_name;
      }
      

        

    • compiles方法, 在这里使用了sp_file_put_contentsb函数会在后面介绍
      function compiles($cache_name, $tpl) {
      if (!file_exists($tpl)) {
        if (file_exists($this->default_tpl_dir . $tpl)) {
          $html = file_get_contents($this->default_tpl_dir . $tpl);
        } else {
          spmkdir(dirname($tpl), '777');
          file_get_contents($tpl, 这个是自动生成模板');
        }
      } else {
        $html = file_get_contents($tpl);
      }
      $tag = new _HtmlTag(realpath(dirname($tpl) . '/../'), $this);
      $tag->render($html);
      $html = preg_replace_callback('//s', array(&$this, "make_magic_func"), $tag->result());
      $html = preg_replace('/[\n\r]{1,}/s', "\n", $html);
      @unlink($cache_name);
      sp_file_put_contents($cache_name, $thml);
      }
      

        

    • make_magic_func方法
      function make_magic_func($d) {
      preg_match_all('/(\w+)=\s*(\'|\")([^\'"]+)?\2/m', $d[2], $dd);
      $aa = array();
      foreach($dd[1] as $i => $k) {
        $vs = preg_replace('/([^\-]*[<>]{1,1})\s*(\S+)?\s*/', '\1\'\2\' ', $dd[3][$i]);
        $vs = preg_replace('/=\s*(\S+)?\s*/', '=\'\1\'', $dd[3][$i]);
        if (stripos($d[3][$i], '$') !== false) {
          $vs = preg_replace('/\$([^\']+)\s*/', '{$\1}', $vs);
         }
      }
      }
      

      注意

    • 两个类的相应方法都 定义好了,这其中用到的函数sp_file_pu_contents($file, $data)
      function sp_file_put_contents($file, $data) {
        $dir = dirname($file);
        if (is_writeable($dir)) {
            return file_put_contents($file, $data);
        } else {
            throw new writeFail(sprintf("写入文件%s,失败,目录不可写", $file), 503);
        }
      }
      js压缩类: jsmin.class.php
      链接:http://pan.baidu.com/s/1kUBx0X5 密码:y5jb
      将这两个 类定义在一个文件中,使用进进行相应的配置
      define("WEB_ROOT", dirname(__FILE__) . '/');

       使用

    • 文件准备

      base.htm,main.htm,test.js,test1.js,test.css,test1.css,indx.php,im.htm
    • main.htm:

      {import ./im}
      {block name=head}
      {block name=body}
      {block name=foot}
    • base.htm
      {extends ./main}
      {block head}
      {loadCss test.css,test1.css}
      {loop $data $k $v}
      <p>
      我是头部
      </p>
      {/loop}
      {if $bb == 'aa'}
      <p>
      我是if 测试
      </p>
      {elseif $bb = 'bb'}
      <p>
      我是elesif测试
      </p>
      {else}
      <p>
      我是eles测试
      </p>
      {/if}
      {for $i=0 $i<10 $i++}
      <p>
      我是for循环
      </p>
      {/for}
      {while $con >5}
      <? $con--;?>
      <p>
      我是while循环
      </p>
      {/while}
      {/block}
      {block foot}
      <p>
      我是尾部
      </p>
      {loadJs test1.js,test.js}
      {/block}
    • im.htm
      我是import进来的
    • index.php
    <?php
    define('WEB_ROOT',dirname(__FILE__) . '/');
    function sp_file_put_contents($file, $data) {
        $dir = dirname($file);
        if (is_writeable($dir)) {
            return file_put_contents($file, $data);
        } else {
            throw new writeFail(sprintf("写入文件%s,失败,目录不可写", $file), 503);
        }
    }
    class _HtmlTag{
        protected $taglist = "if|elseif|else|loop|for|while|=|:=|:e|:html|:";
        protected $string;
        protected $tpldir;
        public $findReg;
        function __construct($dir, $tpl) {
            $this->findReg = "/{[ ]*($this->taglist)(s*[^{}]+s*)?}/i";
            $this->tpldir = $dir;
            $this->tpl =$tpl;
        }
        function render($s) {
            $s = str_replace(['<--{', '}-->'], ['{','}'], $s);
            if (strpos($s, "{extends") !== false) {
                $s = $this->_extends($s);
            } 
            $s = preg_replace('/[
    
    ]+/s', "
    ", $s);
            $s = preg_replace('/
    +/s', "", $s);
            $s = preg_replace('/	+/s', "	", $s);
            $this->string = str_replace(["{", "}"], ["HKH1", "HKH2"], $s);
            preg_match_all("/{[ ]*(import)(s*[^{}]+s*)?}/i", $s, $d);
            foreach($d[0] as $k => $v) {
                call_user_func_array(array($this, "_import"), array($v, $d[1][$k], $d[2][$k]));
            }
            static $m = array("=" => "_defaultEcho", ":=" =>" _xssEcho", ":e" => "_htmlencodeEcho", ":" => "_echo", ":html" => "_htmldecodeEcho");
            preg_match_all($this->findReg, $this->string, $d);
            foreach ($d[0] as $k => $v) {
                if (isset($m[$d[1][$k]])) {
                    $medth = $m[$d[1][$k]];
                } else {
                    $medth = '_' . $d[1][$k];
                }
                call_user_func_array(array($this, $medth), array($v, $d[1][$k], $d[2][$k]));
            }
        }
        function _extends($tpl) {
            preg_match("/{s*extendss+([^{}]+?)s*}/i", $tpl, $d);
            list($search, $arg) = $d;
            if (stripos($arg, ".") !==0 ) {
                $arg = '../' . $arg;
            }
            //$file = $this->tpl->me->realTpl($arg . ".htm");
            $file = $arg . ".htm";
            $basetpl = file_get_contents($file);
            preg_match_all("/{blocks+([^{}]+?)s*}(.+?){/block}/s", $tpl, $ds);
            foreach($ds[1] as $n => $name) {
                $basetpl = preg_replace("/{blocks+name={$name}s*}/", $ds[2][$n], $basetpl);
            }
            $basetpl = preg_replace("/{blocks+name=.+?}/", ' ', $basetpl);
    
            return $basetpl;
        }
        function _import ($search, $tag, $arg) {
    
            $arg = trim($arg);
            if (stripos($arg, ".") !== 0) {
                $arg = '../' . $arg;
            }
            //$file = $this->tpl->me->realTpl($arg . ".htm");
            $file = $arg . ".htm";
            if (file_exists($file)) {
                $this->string = str_replace($search, file_get_contents($file), $this->string);
                return;
            } else {
                if (stripos($file, "$") !== false) {
                $this->string = str_replace($search, '<? include $this->tpl->display("' . $arg . '"); ?>', $this->string);
                return;
            }
            if (stripos($arg, '|') !== false) {
                list($func, $tmp) = explode("|", trim($arg));
                $kw = explode(', ', $tmp);
            } else {
                $func = trim($arg);
                $kw = array();  
            }
            if (function_exists($func)) {
                $tpl_str = call_user_func_array($func, $kw);
                $this->string = str_replace($search, $tpl_str, $this->string);
                return;
            } else {
                $this->string = str_replace($search, $arg, $this->string);
                return;
            }
                system_error($this->tpldir . trim($arg) . ".htm不存在");
            }
        }
    
        function mytest(){
            return $this->string;
        }
    
    
        function _baseParse($s) {
            if (strpos($s, "{loadCss") !== false) {
                $s = $this->_loadCss($s);
            }
            if (strpos($s, "{loadJs") !== false) {
                $s = $this->_loadJs($s);
            }
            return $s;
        }
    
        function fileMd5($f) {
            if (stripos($f, "http://") !== false) {
                return md5($f);
            }
                return md5_file(WEB_ROOT . $f);
        }
    
        function _loadCss($tpl) {
            preg_match_all('/{loadCsss+([^{}]+)s*}/i' , $tpl , $match);
            $cdn = "";
            foreach($match[1] as $k => $css) {
                $cssstr = "";
                if (stripos($css, ',') !== false) {
                    $filename = "";
                    $md5 = "";
                    $css_content = "";
                    foreach(explode(',', $css) as $css) {
                        $filename .= basename($css, '.css') . ',';
                        $md5 = self::fileMd5($css);
                        $css_content .= file_get_contents(WEB_ROOT .$css);   
                    }
                    $md5 = md5($md5);
                    $filename = dirname($css) . '/' . trim($filename, ',') . '.css';
                    self::parseCss($css_content, $filename);
                    $cssstr = "<link rel="stylesheet" href="{$cdn}{$filename}?v={$md5}">";
                    } else {
                        $md5 = self::fileMd5($css);
                        if (stripos($css, ".min.") == false) {
                            $css = self::parseCss(file_get_contents(WEB_ROOT . $css), dirname($css) . '/' .basename($css, '.css') . '.min.css');
                        }
                        $cssstr .= "<link rel="stylesheet" href="{$cdn}{$css}?v={$md5}">";
                }
                $tpl = str_replace($match[0][$k], $cssstr, $tpl);
            }
            return $tpl;
        }    
    
    
        function _loadJs($tpl) {
    
            preg_match_all('/{loadJss+([^{}]+?)s*}/i', $tpl, $match);
            //var_dump($match);die;
            include "jsmin.class.php";
            foreach($match[1] as $k => $js) {
                $jsstr = "";
                $cdn = "";
                if (stripos($js, ',') !== false) {
                    $filename = "";
                    $md5 = "";
                    $js_content = "";
                    foreach(explode(',', $js) as $j) {
                        $filename .= basename($j, '.js') . ',';
                        $md5 .= self::fileMd5($j);
                        if (stripos($j, '.min.') === false) {
                            $js_content .= JSMin::minify(file_get_contents(WEB_ROOT . $j));
                        } else {
                            $js_content .= file_get_contents(WEB_ROOT . $j);
                        }
                        $js_content .= ';';
                    }
                    $md5 = md5($md5);
    
                    $filename = dirname($js) . '/' .trim($filename, ',') . '.js';
                    self::parseJs($js_content, $filename);
                    $jsstr = "<script src="{$cdn}{$filename}?v={$md5}"></script>";
                } else {
                    $md5 = self::fileMd5($js);
                    if (stripos($js, ".min.") === false) {
                        $js = self::pareJs(JSMin::minify(file_get_contents(WEB_ROOT . $js)), dirname($js). '/' .basename($js, '.js') . '.min.js');
                    }
                    $jsstr = "<script src="{$cdn}{$js}?v={$mdt}"></script>";
                }
                $tpl = str_replace($match[0][$k], $jsstr, $tpl);
            }
            return $tpl;
        }
    
        static function parseCss($css_content, $filenme) {
    
            $css_content = preg_replace("/[
    	]/", '', $css_content);
            $css_content = preg_replace("/ +/", ' ', $css_content);
            // echo WEB_ROOT . $filenme;
    
            sp_file_put_contents(WEB_ROOT . $filenme, $css_content);
    
            return $filenme;
        }
    
        static function parseJs($js, $filenme) {
    
            sp_file_put_contents(WEB_ROOT . $filenme, $js);
    
            return $filenme;
    
        }
    
        function _loop($search, $tag, $arg) {
            list($attr, $arg) = $this->parseAttr($arg);
            $d = preg_split("/s+/", trim($arg));
            if (count($d) == 3) {
                $data = $d[0];
                $k = $d[1];
                $v = $d[2];
                $s = "<? 
     if(!empty($data)) {
     	 foreach(" . $data . " as {$k} => {$v}){";
            } elseif (count($d) == 2) {
                $data = $d[0];
                $v = $d[1];
                $s = "<? 
     if(!empty($data)){
     	 foreach(". $datsa . " as {$v}) {";
            }
    
            if (isset($attr['counter'])) {
                $s = "
    		 <? if (!isset({$attr['counter']})) { {$attr['counter']} = 0;} ?> 
     		" . $s . "
     		{$attr['counter']}++; 
     ?>";
                } else {
                $s .= "?>";
            }
            $this->string = str_replace($search, $s, $this->string);
            $this->string = str_replace("{/loop}", "<? 	}
    }?>", $this->string);
        }
        function parseAttr($s) {
            $reg = '/([a-zA-Z0-9_])+s*=s*([a-zA-Z0-9_$]+)/i';
            $arg = preg_replace($reg, '', $s);
            preg_match_all($reg, $s, $d);
            $arr = array();
            foreach($d[1] as $key => $value) {
                $arr[trim($value)] = trim($d[2][$key]);
            }
            return array($arr, $arg);
        }
        function _if ($search, $tag, $arg) {
            $replace[0] = "<? if($arg){?>";
            $replace[1] = "<? }?>";
            $this->string = str_replace(array($search, "{/$tag}"), $replace, $this->string);
        }
        function _else($search, $tag, $arg) {
            $this->string = str_replace("{else}", "
    " . '<? } else {?>', $this->string);
        }
        function _elseif($search, $tag, $arg) {
         $this->string = str_replace($search, "<? }elseif($arg){?>", $this->string);
        }
    
        function _for($search, $tag, $arg) {
          $s = trim(trim(preg_replace('/s+/', ' ; ', $arg)), ';');
          $this->string = str_replace($search, "<? for($s){?>", $this->string);
          $this->string = str_replace("{/for}", "<? 	
    }?>", $this->string);
        }
        function _while($search, $tag, $arg) {
          $this->string = str_replace($search , "<? while($arg){ ?>", $this->string);
          $this->string = str_replace("{/while}", "<? 	
    } ?>", $this->string);
        }
        function _end() {
            //{$val}标签解析 
            $this->string = preg_replace('/\{(\$\w+.*)\}/is', "<?=\\1;?>", $this->string);
            //{:$val()}标签解析
            $this->string = preg_replace('/\{\:\s*(\$?\w+.*?)\}/is', "<? =\\1;?>", $this->string);
            //将$val[arg]解析成$val['arg']
            $this->string = preg_replace('/\$([_a-z]+\w*)\[([_a-z]+\w*)\]/is', "$\\1['\\2']", $this->string);
            //支持点号访问数组。如array['key']可以用array.key访问
            $this->string = preg_replace('/\$([_a-z]+\w*)\.(\$[_a-z]+\w*)/is', "$\\1[\\2]", $this->string);
            $this->string = preg_replace('/\$([_a-z]+\w*)\.([_a-z]+\w*)/is', "$\\1['\\2']", $this->string);
            $this->sring = preg_replace(array("/<?/", "/<?phps*=/"), array("<?php ", "<?php echo "), $this->string);
        }
        function result() {
            $this->_end();
            $this->string = $this->_baseParse($this->string);
            return str_replace(array("\HKH1", "\HKH2"), array("\{", "\}"), $this->string);
        }
    }
    $data = ['aa','bb','cc'];
    $bb = 'aa';
    $con = 11;
    $tag = new _HtmlTag('./index', '');
    $str = file_get_contents('base.htm');
    $tag->render($str);
    $html = $tag->result();
    var_dump($html);
    echo '</br>----------------------------</br>';
    sp_file_put_contents("./index.html", $html);
    include "index.html";
    • 运行index.php查看结果

       


      总结
      
      我们来总结一下这个类的使用过程,先实例话template类,调用template::display()方法,其中会调用template::compiles()方法,这个方法会实例化_HhtmlTag类,调用_HtmlTag::render方法,在这个方法里,对传入的带字符串进行一个结构的完整化,包括调用_HtmlTag::_extends,_HtmlTag::_import,_HtmlTag::_loop,_HtmlTag::_if,_HtmlTag::elseif,_HtmlTag::else,_HtmlTag::_for,_HtmlTag::while接着调用_HtmlTag::result方法,里面调用_HtmlTag::_end方法对变量进行php原生化和_HtmlTag::_baseParse()引入css和js,最后返回解析好的完整模板字符串。
  • 相关阅读:
    (C++)string类杂记
    理解inode
    WPF 未能加载文件或程序集“CefSharp.Core.dll”或它的某一个依赖项
    网络抓包 Fiddler
    网络抓包 wireshark
    Vistual Studio Community 2017 账号的许可证过期,公安网激活方法
    C#实现图片叠加,图片上嵌入文字,文字生成图片的方法
    编写html与js交互网页心得:编写两个按钮切换显示不同的图片
    WPF当中StaticResource调用方法
    WPF通过DynamicResource的用法
  • 原文地址:https://www.cnblogs.com/aksir/p/6777582.html
Copyright © 2011-2022 走看看