zoukankan      html  css  js  c++  java
  • 手写模板引擎

    项目结构:

    • index.php:主文件,要调用的文件。
    • templates.inc.php:模板配置文件。
    • cache:目录:存放静态缓存文件。
    • config:目录:配置文件,模板引擎的系统变量。
      •  profile.xml:用于模板引擎中的系统变量。
    • includes:目录:核心文件
      • Parser.class.php:解析类,用于解析模板文件中的变量,将模板中的变量转换为php代码。
      • Template.class.php:模板类,提供调用的方法,注入变量,显示模板文件。
    • templates:目录:存放模板文件。
    • templates_c:目录:存放编译文件,通过解析类解析后的文件,其中的代码,是html与php代码结合,相当于平时的混写PHP文件。

    模板变量规定:

    --普通边变量
    {$name}
    
    --注释
    {#}其中为注释内容...{#}
    
    --if判断
    {if ($a)}
    {/if}
    
    --if else 判断
    {if ($a)}
    
    {else}
    
    {/if}
    
    --foreach循环
    {foreach $array(key, value)}
    {@key}{@value}
    {/foreach}
    
    --include包含
    {#include file="abc.php"}
    

    代码:

    templates.inc.php,配置文件代码:

    <?php 
        //创建网站根目录常量
        define('ROOT_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR);
        //模板文件目录
        define('TPL_DIR', ROOT_PATH . 'templates' . DIRECTORY_SEPARATOR);
        //编译文件目录
        define('TPL_C_DIR', ROOT_PATH . 'templates_c' . DIRECTORY_SEPARATOR);
        //缓存目录
        define('CACHE', ROOT_PATH . 'cache' . DIRECTORY_SEPARATOR);
        //是否开启缓存
        define('IS_CACHE', true);
        IS_CACHE ? ob_start() : NULL;
        
        //引入模板类
        require ROOT_PATH . 'includes' . DIRECTORY_SEPARATOR . 'Template.class.php';
    ?>
    

    index.php代码:

    <?php 
        //设置输出编码格式
        header('Content-Type:text/html;charset="UTF-8"');
        
        //引入模板配置文件
        require dirname(__FILE__) . DIRECTORY_SEPARATOR . 'templates.inc.php';
        
        //实例化模版类
        $template = new Templates();
        
        //注入变量
        $template->assign('title', '自定义模板引擎');
        $template->assign('content', '自定义模板引擎文件!');
        
        //调用display方法
        $template->display('index.tpl');
    ?>
    

     index.tpl模板文件代码:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    
    <html>
    	<head>
    		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    		<title>{$title}</title>
    	</head>
    	<body>
    		{$content}
    		<!--{webname}-->
    	</body>
    </html>
    

     profile.xml配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
    	<taglib>
    		<name>webname</name>
    		<value>系统变量</value>
    	</taglib>
    	<taglib>
    		<name>pagesize</name>
    		<value>10</value>
    	</taglib>
    </root>
    

     Parser.class.php解析文件:

    <?php 
        /**
         * 模板解析类
         */
        class Parser
        {
            //存储模板内容
            private $_tplcontent;
            
            /**
             * 构造函数,获取模板内容
             * @param unknown $_tplPath
             */
            public function __construct($_tplPath)
            {
                if ( !$this->_tplcontent = file_get_contents($_tplPath) ) {
                    exit('ERROR:获取模板文件出错!');
                }
            }
            
            //解析普通变量
            private function parVar()
            {
                //替换变量的正则表达式
                $_patten = '/{$([w]+)}/';
                //如果匹配成功,则替换变量
                if ( preg_match($_patten, $this->_tplcontent) ) {
                    //[ 1 ],正则表达式,得到替换的内容。
                    $this->_tplcontent = preg_replace($_patten, '<?php echo $this->_vars["1"];?>', $this->_tplcontent);
                } else {
                    exit($this->_tplcontent);
                }
            }
            
            /**
             * 解析if语句
             */
            private function parIf()
            {
                $_varStartIf    = '/{ifs+($([w]+))}/';
                $_varMidlleElse = '/{else}/';
                $_varEndIf      = '/{/if}/';
                
                if ( preg_match($_varStartIf, $this->_tplcontent) ) {
                    if (preg_match($_varEndIf, $this->_tplcontent) ) {
                       //替换头if
                       $this->_tplcontent = preg_replace($_varStartIf, '<?php if ($this->_vars["1"]) { ?>', $this->_tplcontent);
                       //替换尾if
                       $this->_tplcontent = preg_replace($_varEndIf, '<?php } ?>', $this->_tplcontent);
                       //替换 else
                       if ( preg_match($_varMidlleElse, $this->_tplcontent) ) {
                           $this->_tplcontent = preg_replace($_varMidlleElse, '<?php } else { ?>', $this->_tplcontent);
                       }
                    } else {
                        exit('ERROR:if语句没有关闭!');
                    }
                }
            }
            
            /**
             * 解析Foreach
             */
            private function parForeach()
            {
                $_pattenStartForeach  = '/{foreachs+$([w]+)(([w]+),([w]+))}/';
                $_pattenMiddleForeach = '/{@([w]+)}/';
                $_pattenEndForeach    = '/{/foreach}/';
                if ( preg_match($_pattenStartForeach, $this->_tplcontent) ) {
                    if ( preg_match($_pattenEndForeach, $this->_tplcontent) ) {
                        //替换开头和结尾
                        $this->_tplcontent = preg_replace($_pattenStartForeach, '<?php foreach ($this->_vars["1"] as $2=>$3) { ?>', $this->_tplcontent);
                        $this->_tplcontent = preg_replace($_pattenEndForeach, '<?php } ?>', $this->_tplcontent);
                        
                        //替换中间内容
                        if ( preg_match($_pattenMiddleForeach, $this->_tplcontent) ) {
                            $this->_tplcontent = preg_replace($_pattenMiddleForeach, '<?php echo $1; ?>', $this->_tplcontent);
                        }
                    } else {
                        exit('ERROR:foreach标签没有关闭!');
                    }
                }
            }
            
            /**
             * 解析include
             */
            private function parInclude()
            {
                $_pattenInclude = '/{#includes+file="([w.-]+)"}/';
                
                if ( preg_match($_pattenInclude, $this->_tplcontent,$_file) ) {
                    //判断被包含文件是否存在
                    if ( !file_exists($_file[1]) || empty($_file[1]) ) {
                        exit('ERROR:包含文件出错!');
                    }
                    
                    //替换为PHP代码
                    $this->_tplcontent = preg_replace($_pattenInclude, '<?php include "1"; ?>', $this->_tplcontent);
                }
            }
            
            /**
             * 解析系统变量
             */
            private function parConfig()
            {
                $_pattenConfig = '/<!--{([w]+)}-->/';
                if (preg_match($_pattenConfig, $this->_tplcontent) ) {
                    $this->_tplcontent = preg_replace($_pattenConfig, '<?php echo $this->_config["1"]; ?>', $this->_tplcontent);
                }
            }
            
            /**
             * 解析注释
             */
            private function parCommon()
            {
                $_patten = '/{#}(.*){#}/';
                if ( preg_match($_patten, $this->_tplcontent) ) {
                    $this->_tplcontent = preg_replace($_patten, '<?php /* 1 */ ?>', $this->_tplcontent);
                }
            }
            
             /**
              * 解析文件方法
              */
             public function compile($_compileName)
             {
                 //解析普通变量
                 $this->parVar();
                 //解析if语句
                 $this->parIf();
                 //解析include
                 $this->parInclude();
                 //解析系统变量
                 $this->parConfig();
                 //解析注释
                 $this->parCommon();
                 //解析foreach
                 $this->parForeach();
                 //经过解析变量,最后生成编译文件
                 if ( !file_put_contents($_compileName, $this->_tplcontent) ) {
                     exit('ERROR:编译文件出错!');
                 }
             }
        }
    ?>
    

     Template.class.php模板文件:

    <?php 
        /**
         * 模板类
         */
        class Templates
        {
            //存储注入的变量
            private $_vars = array();
            //存储配置文件
            private $_config = array();
            
            /**
             * 构造函数
             */
            public function __construct()
            {
                //验证各个目录是否存在s
                if ( !is_dir(TPL_DIR) || !is_dir(TPL_C_DIR) || !is_dir(CACHE) ) {
                    exit('ERROR:目录不存在,请添加!');
                }
                
                //读取系统变量
                $_root = simplexml_load_file('config/profile.xml');
                $_tagLib = $_root->xpath('/root/taglib');
                
                foreach ($_tagLib as $tag) {
                    $this->_config["$tag->name"] = $tag->value;
                }
            }
            
            /**
             * 注入变量
             */
            public function assign($_varName, $_varValue)
            {
                //判断变量名称是否为空
                if ( !isset($_varName) || empty($_varName) ) {
                    exit('ERROR:变量名不能为空!');
                } else {
                    //将变量注入到数组中去
                    $this->_vars[$_varName] = $_varValue;
                }
            }
            
            /**
             * 显示模板文件
             */
            public function display($filename)
            {
                //获取模板路径
                $_tplPath = TPL_DIR . $filename;
                
                //判断模板文件是否存在
                if ( !file_exists($_tplPath) ) {
                    exit('ERROR:模板文件不存在!');
                }
                
                //编译后文件的文件名
                $_compileName = TPL_C_DIR . md5($filename) . $filename . '.php';
                //缓存文件的文件名
                $_cacheFileName = CACHE . md5($filename) . $filename . '.html';
                
                
                //第二次载入相同文件,直接载入缓存文件
                if ( IS_CACHE ) {
                    //判断缓存文件和编译文件是否都存在,如果都存在则直接执行缓存文件
                    if ( file_exists($_cacheFileName) && file_exists($_compileName) ) {
                        //判断模板文件和编译文件是否修改
                        if ( filemtime($_compileName) >= filemtime($_tplPath) && filemtime($_cacheFileName) >= filemtime($_compileName) ) {
                            include $_cacheFileName;
                            return;
                        }
                    }
                }
                
                
                //如果编译文件还不存在,或者模板文件被修改(检查最后修改时间),则重新生成编译文件
                if ( !file_exists($_compileName) || filemtime($_compileName) < filemtime($_tplPath) ) {
                    //引入解析类
                    require ROOT_PATH . 'includes' . DIRECTORY_SEPARATOR . 'Parser.class.php';
                    //声明类的时候,传入模板文件路径
                    $parser = new Parser($_tplPath);
                    //调用解析方法的时候,传入编译文件名称
                    $parser->compile($_compileName);
                }
                
                
                //载入编译文件,载入后已经输出到浏览器
                include $_compileName;
            
                //是否开启了缓存
                if ( IS_CACHE ) {
                    //接受缓冲文件,并生成缓存文件
                    file_put_contents($_cacheFileName, ob_get_contents());
                
                    //清除缓冲区,意思就是清除了编译文件加载的内容
                    ob_end_clean();
                
                    //载入缓存文件,直接输出到浏览器
                    include $_cacheFileName;
                }
            }
        }
    ?>
    

    生成的编译文件:

    1d7c7a527b6335cc7a623305ca940e1findex.tpl.php

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    
    <html>
    	<head>
    		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    		<title><?php echo $this->_vars["title"];?></title>
    	</head>
    	<body>
    		<?php echo $this->_vars["content"];?>
    		<?php echo $this->_config["webname"]; ?>
    	</body>
    </html>
    

    生成的缓存文件:

    1d7c7a527b6335cc7a623305ca940e1findex.tpl.html

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    
    <html>
    	<head>
    		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    		<title>自定义模板引擎</title>
    	</head>
    	<body>
    		自定义模板引擎文件!		系统变量	</body>
    </html>
    

    ------------

    源码:网盘的Smarty文件夹下,名称【手写模板引擎】。

  • 相关阅读:
    mahout下的KMeans Clustering实现
    信号量、互斥体和自旋锁
    找出二叉树中两个节点的最低共同父节点
    C/C++中volatile关键字
    函数指针和指针函数
    java终止线程的通用机制
    我的一次重构实践
    新手浅谈Future
    线程中添加线程
    死锁
  • 原文地址:https://www.cnblogs.com/KTblog/p/5158202.html
Copyright © 2011-2022 走看看