zoukankan      html  css  js  c++  java
  • PHP--关于模板的原理和解析(php模板原理)

    此内容用作笔记,以备日后查看,此内容为学习李炎恢课程而来,并非自己所创,如有问题请私信~

    将PHP代码和静态HTML代码进行分离,使代码的可读性和维护性得到显著提高。

    使用模板引擎:

        我们所说的模板是Web模板,是主要由HTML标记组成的语言来编写的页面,但也有如何表示包含动态生成内容的方式(解析标签)。模板引擎是一种软件库,允许我们从模板生成HTML代码,并指定要包含的动态内容。

      模板引擎的特点:

      1.鼓励分离:让更个系统的可读性和维护性得到提高。
      2.促进分工:使得程序员和美工去专心处理自己的设计。
      3.比PHP更容易解析:编译文件和缓存文件加载更快、占资源更少。
      4.增加安全性:可限制模板设计师进行不安全的操作的能力避免误删误访问等。

    模板处理的流程图

      

    创建模板:

      1、创建初始模板所需要的文件夹和文件。

      a) index.php主文件,用于编写业务逻辑。
      b) template.inc.php模板初始化文件,用于初始模版信息。
      c) templates目录存放所有的模板文件。
      d) templates_c目录存放所有编译文件。
      e) cache目录存放所有缓存文件。
      f) includes目录存放所有的类文件。
      g) config目录存放模板系统变量配置文件。

      

      以下是源码:

    主文件 index.php  

    <?php
       //index.php
      //设置编码为UTF-8
      header('Content-Type:text/html;Charset=utf-8');
      //网站根目录
      define('ROOT_PATH', dirname(__FILE__));
      //存放模板文件夹
      define('TPL_DIR', ROOT_PATH.'/templates/');
      //编译文件夹
      define('TPL_C_DIR', ROOT_PATH.'/templates_c/');
      //缓存文件夹
      define('CACHE_DIR', ROOT_PATH.'/cache/');
      //定义缓存状态
      define('IS_CACHE',true);
      //设置缓存状态开关
      IS_CACHE ? ob_start() : null;
    
      include ROOT_PATH.'/includes/Templates.class.php';
    
      $_name = '方块李';
        
        $array = array(1,2,3,4,5,6);
        $_tpl = new Templates();
        $_tpl->assign('name', $_name);
        $_tpl->assign('a', 5>4);
        $_tpl->assign('array', $array);
        //显示
        $_tpl->display('index.tpl');
    ?>

    模板文件 HTML

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title><!--{webname}--></title>
     
    </head>
    <body>
    {include "test.php"}
    <!-- 这是HTML的注释 -->
    {#}这是一条PHP的注释,在HTML页面里是不显示的,只会在生成的编译文件里显示{#}
    我将被index.php导入
    {$name}这个标签必须经过Parser.class.php这个解析类来解析它1
    <br />
    这里的内容改变了,为什么?
    <br />
    {if $a}
    显示一号皮肤
    {else}
    显示二号皮肤
    {/if}
    <br />
    {foreach $array(key,value)}
    {@key}....{@value} <br />
    {/foreach}
    </body>
    </html>

    模板类:

         //Templates.class.php  
    class Templates {
        //创建一个存放数组的字段
        private $_vars = array();
        private $_config = array();
        //创建一个构造方法
        public function __construct(){
            if(!is_dir(TPL_DIR) || !is_dir(TPL_C_DIR) || !is_dir(CACHE_DIR) ){
                exit('ERROR:模板文件夹或者编译文件夹或者缓存文件夹没有创建!');
            }
            //获取系统变量
            $_sxe = simplexml_load_file(ROOT_PATH.'/config/profile.xml');
            $_taglib = $_sxe->xpath('/root/taglib');
            foreach($_taglib as $_tag){
                $this->_config["$_tag->name"] = $_tag->value;
            }
        }
       
         
        //创建变量注入方法
        /**
         * assign()变量注入方法
         * @param  $_var 要注入的变量名,对应.tpl文件中的需要替换的变量
         * @param  $_values 要注入的变量值
         */
        public function assign($_var,$_values){
            if(isset($_var) && !empty($_var)){
                $this->_vars[$_var] = $_values;
                 
            }else{
                exit('ERROR:请设置变量名!');
            }
             
        }
         
         
        //创建一个显示方法,用来显示编译后的文件
        public function display($_file){
            //设置模板文件的路径
            $_tplFile = TPL_DIR.$_file;
            //判断模板文件是否存在
            if(!file_exists($_tplFile)){
                exit('ERROR:模板文件不存在');
            }
            //设置编译文件名
            $_parFile  = TPL_C_DIR.md5($_file).$_file.'.php';
            //设置缓存文件名
            $_cacheFile = CACHE_DIR.md5($_file).$_file.'.html';
            //判断缓存状态
            if(IS_CACHE){
                //判断缓存文件是否存在
                if(file_exists($_cacheFile) && file_exists($_parFile)){
                    //是否修改过编译文件或者模板文件
                    if(filemtime($_cacheFile)>=filemtime($_parFile) && filemtime($_parFile)>filemtime($_tplFile)){
                        echo '以下是缓存文件内容';
                        echo "<br />";
                        include $_cacheFile;
                        return;
                    }
                }
            }
            //判断编译文件是否存在,模板文件是否修改过
            if(!file_exists($_parFile) || (filemtime($_parFile) < filemtime($_tplFile))){
                 
                //引入模板解析类
                require ROOT_PATH.'/includes/Parser.class.php';
                //实例化对象,生成编译文件
                $_parser = new Parser($_tplFile);//模板文件
                $_parser->compile($_parFile);//编译后文件
                 
            }
     
            //载入编译文件
            include $_parFile;
            if(IS_CACHE){
                //生成缓存文件
                file_put_contents($_cacheFile, ob_get_contents());
                //清除缓冲区
                ob_end_clean();
                //载入缓存文件
                include $_cacheFile;
            }
     
     
        }
    }
    View Code

    解析类:

    //Parser.class.php
        class Parser {
            //获取模板内容
            private $_tpl;
            //构造方法,初始化模板
            public function __construct($_tplFile){
                //判断文件是否存在
                if(!$this->_tpl = file_get_contents($_tplFile)){
                    exit('ERROR:读取模板出错!');
                }
                 
            }
             
            //解析普通变量
            private function parVar(){
                $_pattern = '/{$([w]+)}/';
                if (preg_match($_pattern,$this->_tpl)) {
                    $this->_tpl = preg_replace($_pattern,"<?php echo $this->_vars['$1'] ?>",$this->_tpl);
                }
            }
            //解析IF条件语句
            private function parIf(){
                //开头if模式
                $_patternIf = '/{ifs+$([w]+)}/';
                //结尾if模式
                $_patternEnd = '/{/if}/';
                //else模式
                $_patternElse = '/{else}/';
                //判断if是否存在
                if(preg_match($_patternIf, $this->_tpl)){
                    //判断是否有if结尾
                    if(preg_match($_patternEnd, $this->_tpl)){
                        //替换开头IF
                        $this->_tpl = preg_replace($_patternIf, "<?php if($this->_vars['$1']){ ?>", $this->_tpl);
                        //替换结尾IF
                        $this->_tpl = preg_replace($_patternEnd, "<?php } ?>", $this->_tpl);
                        //判断是否有else
                        if(preg_match($_patternElse, $this->_tpl)){
                            //替换else
                            $this->_tpl = preg_replace($_patternElse, "<?php }else{ ?>", $this->_tpl);
                        }
                    }else{
                        exit('ERROR:语句没有关闭!');
                    }
                }
            }
            //解析foreach
            private function parForeach(){
                $_patternForeach = '/{foreachs+$(w+)((w+),(w+))}/';
                $_patternEndForeach = '/{/foreach}/';
                //foreach里的值
                $_patternVar = '/{@(w+)}/';
                //判断是否存在
                if(preg_match($_patternForeach, $this->_tpl)){
                    //判断结束标志
                    if(preg_match($_patternEndForeach, $this->_tpl)){
                        //替换开头
                        $this->_tpl = preg_replace($_patternForeach, "<?php foreach($this->_vars['$1'] as $$2=>$$3){?>", $this->_tpl);
                        //替换结束
                        $this->_tpl = preg_replace($_patternEndForeach, "<?php } ?>", $this->_tpl);
                        //替换值
                        $this->_tpl = preg_replace($_patternVar, "<?php echo $$1?>", $this->_tpl);
                    }else{
                        exit('ERROR:Foreach语句没有关闭');
                    }
                }
            }
            //解析include
            private function parInclude(){
                $_pattern = '/{includes+"(.*)"}/';
                if(preg_match($_pattern, $this->_tpl,$_file)){
                    //判断头文件是否存在
                    if(!file_exists($_file[1]) || empty($_file[1])){
                        exit('ERROR:包含文件不存在!');
                    }
                    //替换内容
                    $this->_tpl = preg_replace($_pattern, "<?php include '$1';?>", $this->_tpl);
                }
            }
            //解析系统变量
            private function configVar(){
                $_pattern = '/<!--{(w+)}-->/';
                if(preg_match($_pattern, $this->_tpl,$_file)){
                    $this->_tpl = preg_replace($_pattern,"<?php echo $this->_config['$1'] ?>", $this->_tpl);
                     
                }
            }
             
            //解析单行PHP注释
            private function parCommon(){
                $_pattern = '/{#}(.*){#}/';
                if(preg_match($_pattern, $this->_tpl)){
                    $this->_tpl = preg_replace($_pattern, "<?php /*($1) */?>", $this->_tpl);
                }
            }
             
             
            //生成编译文件
            public function compile($_parFile){
                //解析模板变量
                $this->parVar();
                //解析IF
                $this->parIf();
                //解析注释
                $this->parCommon();
                //解析Foreach
                $this->parForeach();
                //解析include
                $this->parInclude();
                //解析系统变量
                $this->configVar();
                //生成编译文件
                if(!file_put_contents($_parFile, $this->_tpl)){
                    exit('ERROR:编译文件生成失败!');
                }
            }
        }
    View Code

    总结:模板引擎的整个过程:

      1、当浏览器请求index.php文件时,实例化模板类对像 $_tpl = new Templates();

      2、当Templates实例化的时候,生成两个数组,一个用来存放模板变量,另一个存放系统变量,通过构造方法,判断文件夹是否存在,同时通过XML文件将系统变量数组初始化

      3、通过模板类Templates的注入方法,assign(),将对应模板index.tpl中变量的index.php的内容注入到模板类的私有变量,完成初始化

      4、模板类Templates类显示方法display() 通过实例化解析类Parser,将取到的注入变量通过解析类进行解析(即替换)

      5、解析(替换)后,将文件写入PHP、HTML混全文件

      6、通过Templates类的显示方法将文件输出:

         1、第一次执行显示方法时,将会把PHP、HTML混合文件,生成纯静态的缓存文件

         2、调用缓存文件,显示页面

         3、当浏览器再次调用显示方法时,首先根据各文件的最后修改时间,判断是否重新生成缓存文件或直接调用已存在的缓存文件

    重点:

      1、通过正则表达式进行字符串的替换

      2、熟悉OOP

    转:https://www.cnblogs.com/fkli/p/4810675.html

  • 相关阅读:
    修改MySQL密码
    struts入门
    监听
    游戏外挂教程(转)
    “无法加载一个或多个请求的类型。有关更多信息,请检索 LoaderExceptions 属性 “之解决
    C# PropertyGrid控件应用心得
    登录时的"记住我"
    自动登录、记住我(保存登陆状态)实现
    UpdatePanel的使用方法
    asp.net中使用基于角色role的Forms验证
  • 原文地址:https://www.cnblogs.com/fps2tao/p/8520258.html
Copyright © 2011-2022 走看看