PHP模板解析入门

学习:李炎恢PHP视频第二季

模板引擎的特点:

1.鼓励分离:让更个系统的可读性和维护性得到提高。

2.促进分工:使得程序员和美工去专心处理自己的设计。

3.比PHP更容易解析:编译文件和缓存文件加载更快、占资源更少。

4.增加安全性:可限制模板设计师进行不安全的操作的能力避免误删误访问等

一个PHP对应一个模板文件,经过调用模板类并且经过模板解析类之后,生成编译过后的php编译文件,为了加快访问速度,并且生成纯静态的HTML文件,作为缓存文件。

 

简易目录:

1.cache文件夹存放静态HTML文件,加快访问速度

2.config文件夹存放系统变量的XML文件

3.includes 存放模板解析类库

-------------Parser.class.php 模板文件解析类  用于解析tpl文件,将其解析成php文件

------------Templates.class.php    模板类      负责提供给index.php调用传递数据和调用模板文件

4.templates文件夹     存放tpl文件

5.Templates文件夹 存放解析过后的tpl文件,为PHP文件

6.Index.php 调用入口文件

7.template.inc.php     初始化模板文件

8.Test.php 测试文件类

 

 

 

 

 

 

 

 

 

 

 

 

 

一、定义初始化变量

template.inc.php

<?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',ROOT_PATH.'/cache/');

//是否开启缓冲区
define('IS_CACHE',true);
//开启缓冲区
IS_CACHE&&ob_start();

//引入模板类
require ROOT_PATH.'/includes/Templates.class.php';

//实例化模板类
$_tpl = new Templates();

 

二.入口文件index.php

引入初始化文件

require  dirname(__FILE__).'/template.inc.php';
global $_tpl;
//声明一个变量
$_name = '周起';
$_array = array(1,2,3,4,5,6,7);

//调用方法注入变量给模板
$_tpl->assign('name',$_name);
$_tpl->assign('content','学习PHP');
$_tpl->assign('a',1);
$_tpl->assign('array',$_array);


//载入tpl文件
$_tpl->display('index.tpl');

 

 

 

 

 

三、Templates.class.php

<?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)){
            exit('ERROR:模板目录或编译目录或缓存目录不存在,请手动添加');
        }

        //解析xml系统变量
        $_sxe = simplexml_load_file('config/profile.xml');
        $_taglib = $_sxe->xpath('/root/taglib');
        foreach ($_taglib as $tag){
            $this->_config["$tag->name"] = $tag->value;
        }

    }

    //用于注入变量
    /**
     * @param $_var模板变量名
     * @param $_value模板变量值
     */
    public function assign($_var,$_value)
    {
        if (isset($_var)||!empty($_var)

){
            $this->_vars[$_var] = $_value;
        }else{
            exit('ERROR:请设置模板变量');
        }
    }

    //display()  生成编译文件和缓存文件
    /**
     * @param $_file模板文件路径
     */
    public function display($_file)
    {
        //设置模板路径
        $_tplFile = TPL_DIR.$_file;
        //判断模板文件是否存在
        if (!file_exists($_tplFile)){
            exit('ERROR:'.$_file.'文件不存在');
        }

        //生成编译文件路径
        $_parFile = TPL_C_DIR.md5($_file).$_file.'.php';
        //生成缓存文件
        $_cacheFile = CACHE.md5($_file).$_file.'.html';
        //当第二次相同文件的时候,直接载入缓存文件,避开编译
        if (IS_CACHE){
            //缓存文件和编译文件同时存在
            if (file_exists($_cacheFile)&&file_exists($_parFile)){
                //并且模板文件修改时间大于编译文件
                if (filemtime($_cacheFile)>=filemtime($_parFile)&&filemtime($_parFile)>=filemtime($_tplFile)){
                    //直接载入静态文件
                    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;
        }
    }
}

 

当实例化Template对象的时候,会先判断各个目录是否存在

index.php调用$_tpl->assign()方法的时候,调用Template对象中的assign方法,在Template中有一个私有属性$_var为数组类,用于存放assign中的参数

//用于注入变量
/**
 * @param $_var模板变量名
 * @param $_value模板变量值
 */
public function assign($_var,$_value)
{
    if (isset($_var)||!empty($_var)

){
        $this->_vars[$_var] = $_value;
    }else{
        exit('ERROR:请设置模板变量');
    }
}

如果变量不存在,或者变量为空的话,提示设置模板变量,否则,以变量名为数组索引,变量值为相对索引的值,存放入$_var私有变量中。

如果出现相同变量名就会出现因为索引相同,出现覆盖的情况。

//声明一个变量
$_name = '周起';

//调用方法注入变量给模板
$_tpl->assign('name',$_name);
$_tpl->assign('content','学习PHP');
$_tpl->assign('content','不想成为码农');

die();

这个时候掉用assign()方法,assign()方法改写为

public function assign($_var,$_value)
{
    if (isset($_var)||!empty($_var)){
        $this->_vars[$_var] = $_value;
    }else{
        exit('ERROR:请设置模板变量');
    }
    print_r($this->_vars);
}

 

在网页端打开访问

 

 

 

当调用Template类中display()方法的时候,传递进来一个模板文件名称

$_tpl->display(‘index.tpl’);

/**
 * @param $_file模板文件路径
 */
public function display($_file)
{
    //设置模板路径
    $_tplFile = TPL_DIR.$_file;
    //判断模板文件是否存在
    if (!file_exists($_tplFile)){
        exit('ERROR:'.$_file.'文件不存在');
    }

    //生成编译文件路径
    $_parFile = TPL_C_DIR.md5($_file).$_file.'.php';
    //生成缓存文件
    $_cacheFile = CACHE.md5($_file).$_file.'.html';
    //当第二次相同文件的时候,直接载入缓存文件,避开编译
    if (IS_CACHE){
        //缓存文件和编译文件同时存在
        if (file_exists($_cacheFile)&&file_exists($_parFile)){
            //并且模板文件修改时间大于编译文件
            if (filemtime($_cacheFile)>=filemtime($_parFile)&&filemtime($_parFile)>=filemtime($_tplFile)){
                //直接载入静态文件
                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;
    }
}

 

1.接收到tpl模板名称,定位到该模板文件路径,并且检测模板文件是否存在。

2.并且同时声明两个变量用于存放模板编译文件路径和纯静态HTML缓存文件路径

3.模板文件和编译文件和静态文件规则是这样:

如果编译文件和缓存文件都不存在,直接载入编译文件,并且生成静态缓存文件

当编译文件不存在或者模板文件被修改的时候,重新生成编译文件,并且生成静态缓存文件

当第二次访问相同页面,并且编译文件和缓存文件同时存在,并且缓存文件的修改时间大于编译文件的时候,直接访问静态缓存文件

知识点:file_put_contents() 函数把一个字符串写入文件中。

缓存机制:http://www.cnblogs.com/usa007lhy/p/5421545.html(推荐)

filemtime() 函数返回文件内容上次的修改时间。

fileatime() 函数返回指定文件的上次访问时间。

 

当第一次或者模板文件发生改变的时候,重新解析模板文件,并且将其解析

//当编译文件不存在或者模板文件被修改的时候   重新生成编译文件
if (!file_exists($_parFile ||filemtime($_parFile)<filemtime($_tplFile))){
    //引入模板解析类
    require ROOT_PATH.'/includes/Parser.class.php';
    //实例化对象的时候将模板文件路径传递进去
    $_parser = new Parser($_tplFile);
    $_parser->compile($_parFile);
}

 

 

<?php
class Parser{

    //保存模板内容
    private $_tpl;


    //接收模板文件路径
    public function __construct($_tplFile)
    {
        if (!$this->_tpl = file_get_contents($_tplFile)){
            exit('ERROR:模板文件读取错误');
        }
    }
    public function compile($_parFile)
    {
        $this->parConfig();
        $this->parInclude();
        $this->parForeach();
        $this->parCommon();
        $this->parIf();
        $this->parVar();
        //生成编译文件之前解析模板内容
        if (!file_put_contents($_parFile,$this->_tpl)){
            exit('ERROR:编译文件生成出错');
        }
    }

    //解析if语句
    private function parIf(){
        $_pattenIf = '/\{if\s+\$([\w]+)\}/';
        $_pattenEndIf = '/\{\/if\}/';
        $_pattenElse = '/\{else\}/';
        if (preg_match($_pattenIf,$this->_tpl)){
            if (preg_match($_pattenEndIf,$this->_tpl)){
                //匹配if开头
                $this->_tpl = preg_replace($_pattenIf,'<?php if($this->_vars[\'$1\']){?>',$this->_tpl);
                //匹配if结尾符并且更改为php形式
                $this->_tpl = preg_replace($_pattenEndIf,"<?php };?>",$this->_tpl);
                //匹配else
                if (preg_match($_pattenElse,$this->_tpl)){
                    $this->_tpl = preg_replace($_pattenElse,"<?php }else{;?>",$this->_tpl);
                }
            }else{
                exit('ERROR:IF语句没有关闭');
            }
        }
    }

    //解析PHP代码注释
    private function parCommon(){
        $_pattern = '/\{#\}(.*)\{#\}/';
        if (preg_match($_pattern,$this->_tpl)){
            $this->_tpl = preg_replace($_pattern,'<?php /*$1*/;?>',$this->_tpl);
        }
    }
    //解析普通变量
    private function parVar(){
        $_pattern = '/\{\$([\w]+)\}/';
        if (preg_match($_pattern,$this->_tpl)){
            $this->_tpl = preg_replace($_pattern,"<?php echo \$this->_vars['$1'];?>",$this->_tpl);
        }
    }

    //解析foreach语句
    private function parForeach(){
        $_patternForeach = '/\{foreach\s+\$([\w]+)\(([\w]+),([\w]+)\)\}/';
        $_patternEndForeach = '/\{\/foreach\}/';
        $_patternVar = '/\{@([\w]+)\}/';
        if (preg_match($_patternForeach,$this->_tpl)){
            if (!preg_match($_patternEndForeach,$this->_tpl)){
                exit('ERROR:foreach语句没有闭合');
            }
            $this->_tpl = preg_replace($_patternForeach,'<?php foreach(\$this->_vars["$1"] as \$$2=>\$$3){?>',$this->_tpl);
            $this->_tpl = preg_replace($_patternEndForeach,'<?php }?>',$this->_tpl);

            //判断foreach是否有数据
            if (preg_match($_patternVar,$this->_tpl)){
                $this->_tpl = preg_replace($_patternVar,'<?php echo \$$1?>',$this->_tpl);
            }
        }

    }

    //解析include语句
    private function parInclude(){
        $_patternInclude = '/\{include\s+file=\"([\w\.\-]+)\"\}/';
        if (preg_match($_patternInclude,$this->_tpl,$_file)){
            if ((!file_exists($_file[1])) || empty($_file)){
                exit('ERROR:文件'.$_file[1].'不存在');
            }
            $this->_tpl = preg_replace($_patternInclude,"<?php include '$1'?>",$this->_tpl);
        }
    }

    //解析系统变量
    private function parConfig(){
        $_pattern = '/<!-- \{([\w]+)\} -->/';
        if (preg_match($_pattern,$this->_tpl)){
            $this->_tpl = preg_replace($_pattern,"<?php echo \$this->_config['$1'];?>",$this->_tpl);
        }
    }
}

 

1.引入模板解析类,并且实例化模板解析类,再将模板文件传入给模板解析类作为初始化参数,构造函数获取到模板文件类型,赋值给模板解析类的私有属性$_tpl

2.当在Template中调用Parser模板解析类中的compile()方法的时候,将编译文件名传递进去。

3.将模板文件放入编译文件之前,先解析tpl中的所有模板标签

public function compile($_parFile)
{
    $this->parConfig();
    $this->parInclude();
    $this->parForeach();
    $this->parCommon();
    $this->parIf();
    $this->parVar();
    //生成编译文件之前解析模板内容
    if (!file_put_contents($_parFile,$this->_tpl)){
        exit('ERROR:编译文件生成出错');
    }
}

已解析if语句为例:

{if $a}
    <div>我是1号界面</div>
    {else}
    <div>我是2号界面</div>
{/if}

//解析if语句
private function parIf(){
    $_pattenIf = '/\{if\s+\$([\w]+)\}/';
    $_pattenEndIf = '/\{\/if\}/';
    $_pattenElse = '/\{else\}/';
    if (preg_match($_pattenIf,$this->_tpl)){
        if (preg_match($_pattenEndIf,$this->_tpl)){
            //匹配if开头
            $this->_tpl = preg_replace($_pattenIf,'<?php if($this->_vars[\'$1\']){?>',$this->_tpl);
            //匹配if结尾符并且更改为php形式
            $this->_tpl = preg_replace($_pattenEndIf,"<?php };?>",$this->_tpl);
            //匹配else
            if (preg_match($_pattenElse,$this->_tpl)){
                $this->_tpl = preg_replace($_pattenElse,"<?php }else{;?>",$this->_tpl);
            }
        }else{
            exit('ERROR:IF语句没有关闭');
        }
    }
}

 

流程是先查找模板中是否存在{if $X}形式的模板标签,如果存在就查找{/if}

如果{/if}存在的情况在区分是否有else,然后通过字符串替换,将其变成PHP形式的输出

 

知识点:

preg_match() 函数用于进行正则表达式匹配,成功返回 1 ,否则返回 0 。

int preg_match( string pattern, string subject [, array matches ] )

pattern

正则表达式

subject

需要匹配检索的对象

matches

可选,存储匹配结果的数组, $matches[0] 将包含与整个模式匹配的文本,$matches[1] 将包含与第一个捕获的括号中的子模式所匹配的文本,以此类推

preg_replace (正则表达式, 替换成, 字符串, 最大替换次数【默认-1,无数次】, 替换次数)

 

转载于:https://www.cnblogs.com/zhouqi666/p/7168412.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
管理后台文件夹:admin,管理员帐号:admin,密码:admin 运行本程序,需要你的主机或者虚拟主机配置有php+mysql+zend optimizer的系统运行环境。 本机调试可以自己配置系统运行环境,配置教程请下载PHP环境一键安装包, 下载地址:http://www.wygk.net.cn/dow/phpmysqlzend.rar 如虚拟主机调试,则将网站文件全部上传到空间商指定的文件夹下,再执行安装过程,安装路径是你的网址后加install/index.php,比如你的网址是http://www.abc.com,则安装路径是http://www.abc.com/install/index.php,如在本机调试,安装地址是http://127.0.0.1/install/index.php或者http://localhost/install/index.php   网软分类信息系统是一款基于php+mysql的建站系统.为在各种服务器上架设分类信息以及地方门户网站提供完美的解决方案。 网软分类信息系统整站生成静态,拥有世界一流的用户体验,卓越的访问速度和负载能力。   网软分类信息系统能让你在最短的时间架设一个专业的分类信息/地方门户网站,是一款专注分类信息领域的CMS内容管理系统,能以最低的成本,最少的人力投入,在最短的时间内架设一个功能齐全,性能优异规模庞大并且易于维护的网站平台。 什么是分类信息网? 分类信息网是互联网新兴起的网站类型。如同在网上打小广告。涉及日常生活的方方面面信息资讯。在这些网站里面我们可以获得免费,便利的信息发布服务,包括二手物品交易、二手车买卖、房屋租售、宠物、招聘、兼职、求职、交友活动、生活服务信息。分类信息又称分类广告,我们日常在电视、报刊上所看到的广告,往往是不管你愿不愿意,它都会强加给你,我们称这类广告为被动广告;而人们主动去查询招聘、租房、旅游等方面的信息,对这些信息,我们称它为主动广告。在信息社会逐步发展的今天,被动广告越来越引起人们的反感,而主动广告却受到人们的广泛青睐。几乎每个地方的晚报、日报、生活娱乐报都少不了分类信息的身影,而且办得越好的报纸,分类信息的篇幅往往越大。由此就产生了分类信息网。 谁适合使用分类信息网? 想建立一个地区平台或行业平台的分类信息网站,让更多的人在自己的网站上注册会员在线发布信息,在线申请开设店铺发布信息和商品,或者想拥有58同城网,赶集网,百姓网式的平台分类信息网站的人。 特色功能: 1、在线支付:整合在线支付接口如支付宝,财付通,网银支付接口。 2、整合论坛:整合discuz X论坛,整合phpwind论坛(业界独家整合,双向同步)。 3、查看联系方式收费:可设置查看信息的联系方式收费,以及扣除的金币数量。 4、整合QQ登录:整合QQ应用登录。 5、手机访问:新增手机登录访问模块,支持手机浏览发布信息。 6、商家自定义banner背景图:商家自定义修改上传店铺背景banner图片。 7、会员注册积分推广:通过点击会员推广链接,可给该会员增加积分。 8、会员注册,发布信息IP及地域限制:可限制注册会员及发布信息所在地。 9、自定义信息字段模型:各栏目类别分类信息模型选项字段完全自定义。 10、SEO优化:站务,信息,新闻,店铺,商家黄页 可设置不同的路径显示方式,包括静态,伪静态,动态。 11、自助置顶:分类信息可限时分类置顶以及首页置顶,并扣除相应金币,会员可自行操作,扣除的金币数也可在后台设置。 12、地图标注:整合地图标注接口设置(包括 baidu,51ditu,google地图)。 13、广告管理:后台可以设置漂浮广告,对联广告以及横幅广告,并且可设置不同页面显示不同的广告。 14、告别注册机:验证码可设置文验证码,数字求和,颜色,英文数字自由搭配的选择设置,以及图案干扰背景设置 (设置干扰点数值,干扰线数值),可限制单个地区或IP发贴限制,更改信息发布文件名以及会员注册文件名,验证问答设置等等功能彻底摆脱注册机的困扰 。 15、自助刷新,套红,加粗信息标题:会员可自行对发布的信息进行标题套红,加粗,以及刷新操作,扣除相应金币。 16、地方门户:通过启用新闻,优惠券,团购等插件,可将系统延伸为地方门户系统。 17、多城市版本:支持多城市间自由切换,不同城市可采用独立二级域名,系统根据浏览者IP自动跳转所在城市,分站有独立的广告管理,每个分站可设置独立管理员,分站拥有独立的SEO优化设置可设置不同的关键词描述。 18、分站管理员:系统总管理员可以给不同分站设置不同的管理员,可实现地方性站长加盟,单独管理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值