• 自己用的一款模板解析程序(支持插件和扩展)(思路讨论和使用体验)


    前阶段,有个项目需要使用php,于是组了一个3人组团队进行开发。


    在项目中使用了模板解析,首先申明一下,个人认为:

    1、模板解析并不是只有cms才用,中小型企业或者政府应用软件 尤其OA这样的自动化系统也是非常适用的。

    2、如果运用于网站 则更主要的是模板解析速度和性能,如果应用于软件,更重要的是方便、可复用性强、可移植性强(这点尤其重要)

    3、模板解析说到底就是变量替换,然而网页不比固定的软件界面,会产生的各种情况太多太复杂,因此不建议使用国外的很复杂的模板引擎,虽说开源但里面的代码非高手很难读懂,就算读得懂,也没有这么多时间去读和修改。(国内程序员大部分人都“没时间”,你懂得!!!)

    4、模板解析过程中往往需要一些自定义函数,国外的如果要修改主程序比较复杂,依然是没时间。

    因此让团队成员花了点时间开发了这个模板解析程序,我试了一下效果还可以,性能一般吧,不过我们是用在中小型企业应用里面的.

    企业应用比较繁琐的就是表单,而且领导对表单的要求比较高,口味也很重,同样一个表的操作,新增和修改界面就要不一样,因此使用模板来加载数据是首选.


    该模板解析程序功能比较简单 在后台进行数据绑定后,前台可以无限嵌套循环数据 ,个人比较喜欢的是,支持代码插件,譬如我想在页面中支持if else 判断,便可以自己写个解析程序加载进去,进行解析,同时在还支持了 自定义函数,函数的编写直接使用php代码即可。 以下我做一个说明


    首先页面模板如下:

     <!--open_func-->  这个代表开启函数支持,注意开启后 性能肯定要下降一点,
     <!--import_func="./tplfun.inc"--> //代表加载一些函数库,当然可以加载n个
     <!--import_plug="./plug_if.inc"--> //代表加载了一个插件 ,我手写了一个if else 判断的插件,用来解析 if else 判断
     <html>
    <head>
     
             <!-- BEGIN commonHtml -->  //这个代表一个区域块,后台必须有个array 绑定这个区域块
             
               <plugin:IF> //这个是一个插件  
                  <!--if({miniui}==3)-->
                    设置成了红色
                    <!--if("{default}"=="默认变量")-->
                      设置了默认变量
                     <!--endif-->
                   <!--else-->
                    没有设置成红色
                  <!--endif-->
                 </plugin:IF>
               
               
               <!--include_file="./test.txt"--> // 这里支持 include 一个文件,如果该文件中含有模板解析标记 一样会被解析
               
               <!-- END commonHtml --> //必须要有 结束标记
          
          <style>
           .table th{text-align:center;}
           .table td{
            line-height:25pt;height:25pt
           }
          </style>
           
         </head>
          
         <body>
         <plugin:IF>
          <!--if("{username}"=="admin888")-->
            用户名正确
            <!--else-->
            用户名不正确 
          <!--endif-->
         </plugin:IF>
        
         我是一个{setcolor(upper(username),"red")}
             <div id="main">
               <div style="padding-left:11px;padding-bottom:5px;">
                <!-- BEGIN commonHtml -->{setcolor(miniui,"red")}<!-- END commonHtml -->aa
                
                
             <!-- BEGIN datalist1 -->{setcolor(password,"red")}<!-- END datalist1 -->bb
             </div>
               <script>
              
     
         
             </body>
             </html>
    

    这里注意,变量使用大括号 来表示 {username} 代表一个key是username的变量


    然后需要有个 主程序来运行这个模板

    代码如下:

    View Code
     require("./navtpl.inc");
       $tpl=new navtemplate("./","test.tpl");
       
       $data=array();
       $data[]=array("default"=>"默认变量","miniui"=>"3");
       $tpl->initTpl();
       $data2=array();
       $data2[]=array("password"=>"123");
       $tpl->set_var("username",'admin888'); //设置一个变量
         $tpl->set_block_var("commonHtml",$data);//设置区域块的数据源
       $tpl->set_block_var("datalist1",$data2);//设置区域块的数据源
        $tpl->parse(); //输出模板  结束战斗

    由于开启了函数库 ,因此 我自己写了一个函数文件 来测试 如下

    View Code
    <?php
     
      function len($str)
      {
        return "";
      }
      function lower($str)
      {
        return strtolower($str);
      }
      function upper($str)
      {
        return strtoupper($str);
      }
      function substring($str,$start,$length,$c="utf-8")
      {
        return mb_substr($str,$start,$length,$c);
      }
      function setcolor($st,$color)
      {
        return "<span style='color:$color'>$st</span>";
      }
    ?>

    下面是 一个 if else endif 的 语法解析, 这个插件 我写了一下,加载到了主模板程序,发现还是可以用 注意:只支持if else  不支持elseif 因为没有时间,我就没写elseif的支持

    有兴趣的朋友 可以自行扩展

    代码如下:

    <?php
     
      class plugin_IF_ //插件类 
      {
        var $tag="/\<plugin:IF\>(.*?)\<\/plugin:IF\>/s";
        function getLineCnt($begin,$end,$else,$list,$istrue)
        {
            if($begin==$end || $begin>$end || $begin<0) return "";//标签必须分行写 否则返回空
            $ret="";
           
            for($i=0;$i<=count($list)-1;$i++)
            {
              
                if($istrue)
                {
                    $tmp=$list[$i];
                    if($else>0)
                    {
                         if($i>$else && $i<$end) continue;
                         
                    }
                    if($i==$begin)
                    {
                       $tmp=preg_replace("/<!--if\((.*?)\)-->/s","",$tmp);
                    }
                     if($i==$end)
                    {
                        $tmp=preg_replace("/<!--endif-->/s","",$tmp);
                    }
                    if($i==$else)
                      $tmp=preg_replace("/<!--else-->/s","",$tmp);
                         if(trim($tmp)!="")
                        $ret.=chr(13).$list[$i];  
                }
                else //非正确匹配 则要排除 begin 到end以外的 内容
                {
                     
                    if($i<$begin || $i>$end || ($else>0 && $i>$else && $i<$end))
                    {
                          $tmp=$list[$i];
                          if($i==$begin) $tmp=preg_replace("/<!--if\((.*?)\)-->/s","",$tmp);
                          if($i==$end) $tmp=preg_replace("/<!--endif-->/s","",$tmp);
                          if($i==$else) $tmp=preg_replace("/<!--else-->/s","",$tmp);
                           if(trim($tmp)!="")
                           $ret.=chr(13).$tmp;
                    }
                }
                
            }
            return $ret;
            
        }
        function execFunc($cnt)
        {
           
            $list=explode(chr(13),$cnt);
            
             
            if(count($list)<2) return false;//必须有2行 否则 认为没有条件判读
               
            $copy=array_reverse($list);
            $firstIF="";
            $elseLinenum=0;
            $hasEndif=false;
            $beginLinenum=0;
            $endLinenum=0;
            $i=0;
          
            foreach($list as $line)
            {
                if(preg_match("/<!--if\((.*?)\)-->/s",$line,$match) && $firstIF=="")//代表匹配到第一个if 语句
                {
                    if($match && count($match)>1)
                    {
                        $firstIF=trim($match[1]);
                        $beginLinenum=$i;
                    }
                }
                if(preg_match("/<!--else-->/s",$line,$match) && $elseLinenum==0)//代表匹配到第一个else 语句
                {
                        $elseLinenum=$i;   
                }
                if($firstIF!="" && $elseLinenum>0) break;//节省循环次数
                $i++;
            }
            if(trim($firstIF)=="") return false;//没有开始标记 则认为没有代码 
            $i=count($list)-1;
             foreach($copy as $line)
            {
                 if(preg_match("/<!--endif-->/s",$line))//代表匹配到最后一个if 语句
                {
                    $hasEndif=true;
                    $endLinenum=$i;
                    break;
                }
                $i--;
            }
            
            if(!$hasEndif) return false; //标签写错 则认为没有if判断
            try
            {
               eval("\$ifvalue=(".$firstIF.");");
               if(isset($ifvalue))
               {
                  if($ifvalue)//判断为true 则要获取所有内容
                  {
                    return $this->getLineCnt($beginLinenum,$endLinenum,$elseLinenum,$list,true);
                  }
                  else
                    return $this->getLineCnt($beginLinenum,$endLinenum,$elseLinenum,$list,false);
               }
               else return "";//条件判断写错 会 清除所有内容
            }
            catch(Exception $ex)
            {
                return "";//代码发生未知错误 也会 清除所有内容
            }
          
            
        }
        function getIfList($cnt)
        {
            preg_match_all($this->tag,$cnt,$match,PREG_SET_ORDER);
             
             if($match && count($match)>0)
               {
                foreach($match as $m)
                {
                    $go=true;
                    $i=0;
                    $getresult=$m[1];
                    while($go)
                    {
                        if($i>=20) //最多支持20个标签 防止死循环
                        {
                            $go=false;break;
                        }
                        $tmp=$this->execFunc($getresult);
                        if($tmp===false)
                        {
                            $go=false;break;
                        }
                        $getresult=$tmp;
                   
                        $i++;
                        
                    }
                    $cnt=preg_replace($this->tag,$getresult,$cnt,1);
                }
               }
          
            return $cnt;
           
        }
        function clearTag($cnt)//如果语法错误 则要去除冗余的 标记
        {
             $cnt=preg_replace("/<!--if\((.*?)\)-->/s","",$cnt);
            $cnt=preg_replace("/<!--endif-->/s","",$cnt);
            return $cnt;
        }
        function parse($cnt)//插件必须拥有这个 方法 否则不会执行 插件任何内容
        {
          
           $list=explode(chr(13),$cnt);
          
           $cnt=$this->getIfList($cnt);
           
            $cnt=$this->clearTag($cnt);
            return $cnt;
         
        }
      }
    ?>

    这里插件 要注意的是  必须是一个 class 文件  并且 类名 必须遵循 plugin_类名_ 这样的形式 ,转换到模板 就是

    <plugin:类名>....</plugin:类名> /// 这里大小写 是很敏感的。插件 必须拥有parse方法 返回 解析后的内容

    个人认为的优点:

    1、灵活,扩展性好

    2、修改核心无障碍。

    3、没有老外的那种 一大堆文件和代码的风格,虽说老外写的东西很牛,但是如果开发的是国人的软件 还是用国人的东西比较适合。


    缺点不说了,bug肯定有 ,性能没有老外的好,高手会认为是垃圾,和smarty 不是同一个东西,勿喷


    欢迎 各位有兴趣的 好朋友 提供更好的 语法插件


    以下是一些注意点 :

    1、使用上面程序 必须使用phh5 ,必须开启mb_string  必须支持eval

    2、主程序是utf-8,因此必须使用utf8编码, 理论上 模板是gb2312也没问题,但是我没测试

    3、由于项目还没交付,同时该模板程序还在不断完善和修改,目前是v0.1 ,因此模板主解析程序 我进行了压缩, 后面会放出 带注释版本的 纯原文件。

    4.本模板特别适用于 一线开发人员使用(很忙 很没时间的开发人员), 不建议新手使用(新手还是先学好基础为好),不建议高手使用(高手可能会认为此物是垃圾)


     测试程序 我放到了我的网盘  里面 http://url.cn/7dKBks    (qq的微云)。

    如果不能下载 请留下 邮箱

  • 相关阅读:
    SessionAttributes注解
    数据格式化
    数据类型转换器
    线程的常用方法总结
    线程生命周期
    分析配置DispatcherServlet类时load-on-startup标签作用
    springMVC的执行请求过程
    MyBatis之动态SQL
    MyBatis实现
    Spring框架中的JDK与CGLib动态代理
  • 原文地址:https://www.cnblogs.com/shenyisyn/p/2807896.html
Copyright © 2020-2023  润新知