自定义php模板引擎

模板引擎的思想是来源于MVC(Model View Controller)模型,即模型层、视图层、控制器层。

在Web端,模型层为数据库的操作;视图层就是模板,也就是Web前端;Controller就是PHP对数据和请求的各种操作。模板引擎就是为了将视图层和其他层分离开来,使php代码和html代码不会混杂在一起。因为当php代码和html代码混杂在一起时,将使代码的可读性变差,并且代码后期的维护会变得很困难。

大部分的模板引擎原理都差不多,核心就是利用正则表达式解析模板,将约定好的特定的标识语句编译成php语句,然后调用时只需要include编译后的文件,这样就讲php语句和html语句分离开来了。甚至可以更进一步将php的输出输出到缓冲区,然后将模板编译成静态的html文件,这样请求时,就是直接打开静态的html文件,请求速度大大加快。

简单的自定义模板引擎就是两个类,第一个是模板类、第二个是编译类。



首先是编译类:

class CompileClass {
    private $template;      // 待编译文件
    private $content;       // 需要替换的文本
    private $compile_file;       // 编译后的文件
    private $left = '{';       // 左定界符
    private $right = '}';      // 右定界符
    private $include_file = array();        // 引入的文件
    private $config;        // 模板的配置文件
    private $T_P = array();     // 需要替换的表达式
    private $T_R = array();     // 替换后的字符串

    public function __construct($template, $compile_file, $config) {}

    public function compile() {
        $this->c_include();
        $this->c_var();
        $this->c_staticFile();
        file_put_contents($this->compile_file, $this->content);
    }

    // 处理include
    public function c_include() {}

    // 处理各种赋值和基本语句
    public function c_var() {}

    // 对静态的JavaScript进行解析
    public function c_staticFile() {}
}

编译类的大致结构就是上面那样,编译类的工作就是根据配置的文件,将写好的模板文件按照规则解析,替换然后输出到文件中。这个文件的内容是php和html混杂的,但在使用模板引擎进行开发时并不需要在意这个文件,因为我们要编写的是模板文件,也就是html和我们自己定义的标签混合的一个文件。这样View和其他两层就分离开来了。

在这个自定义模板引擎中,我的左右定界符就是大括号,具体的解析规则就是放在__construct()中

// 需要替换的正则表达式
$this->T_P[] = "/$this->left\s*\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\xf7-\xff]*)\s*$this->right/";
$this->T_P[] = "/$this->left\s*(loop|foreach)\s*\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\xf7-\xff]*)\s*$this->right/";
$this->T_P[] = "/$this->left\s*(loop|foreach)\s*\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\xf7-\xff]*)\s+"
        . "as\s+\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\xf7-\xff]*)$this->right/";
$this->T_P[] = "/$this->left\s*\/(loop|foreach|if)\s*$this->right/";
$this->T_P[] = "/$this->left\s*if(.*?)\s*$this->right/";
$this->T_P[] = "/$this->left\s*(else if|elseif)(.*?)\s*$this->right/";
$this->T_P[] = "/$this->left\s*else\s*$this->right/";
$this->T_P[] = "/$this->left\s*([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\xf7-\xff]*)\s*$this->right/";

// 替换后的字符串
$this->T_R[] = "<?php echo \$\\1; ?>";
$this->T_R[] = "<?php foreach((array)\$\\2 as \$K=>\$V) { ?>";
$this->T_R[] = "<?php foreach((array)\$\\2 as &\$\\3) { ?>";
$this->T_R[] = "<?php } ?>";
$this->T_R[] = "<?php if(\\1) { ?>";
$this->T_R[] = "<?php } elseif(\\2) { ?>";
$this->T_R[] = "<?php } else { ?>";
$this->T_R[] = "<?php echo \$\\1; ?>";

上面的解析规则包含了基本的输出和一些常用的语法,if、foreach等。利用preg_replace函数就能对模板文件进行替换。具体情况如下

<!--模板文件-->
{$data}
{foreach $vars}
    {if $V == 1 }
        <input value="{V}">
    {elseif $V == 2}
        <input value="123123">
    {else }
        <input value="sdfsas是aa">
    {/if}
{/foreach}

{ loop $vars as $var}
    <input value="{var}">
{ /loop }
// 解析后
<?php echo $data; ?>
<?php foreach((array)$vars as $K=>$V) { ?>
    <?php if( $V == 1) { ?>
        <input value="<?php echo $V; ?>">
    <?php } elseif( $V == 2) { ?>
        <input value="123123">
    <?php } else { ?>
        <input value="sdfsas是aa">
    <?php } ?>
<?php } ?>

<?php foreach((array)$vars as &$var) { ?>
    <input value="<?php echo $var; ?>">
<?php } ?>

编译类的工作大致就是这样,剩下的include和对JavaScript的解析都和这个大同小异。



然后就是模板类

class Template {
    // 配置数组
    private $_arrayConfig = array(
        'root' => '',       // 文件根目录
        'suffix' => '.html',       // 模板文件后缀
        'template_dir' => 'templates',      // 模板所在文件夹
        'compile_dir' => 'templates_c',       // 编译后存放的文件夹
        'cache_dir' => 'cache',     // 静态html存放地址
        'cache_htm' => false,       // 是否编译为静态html文件
        'suffix_cache' => '.htm',       // 设置编译文件的后缀
        'cache_time' => 7200,        // 自动更新间隔
        'php_turn' => true,      // 是否支持原生php代码
        'debug' => 'false',
    );
    private $_value = array();
    private $_compileTool;      // 编译器
    static private $_instance = null;
    public $file;        // 模板文件名
    public $debug = array();        // 调试信息

    public function __construct($array_config=array()) {}

    // 单步设置配置文件
    public function setConfig($key, $value=null) {}

    // 注入单个变量
    public function assign($key, $value) {}

    // 注入数组变量
    public function assignArray($array) {}

    // 是否开启缓存
    public function needCache() {}

    // 如果需要重新编译文件
    public function reCache() {}

    // 显示模板
    public function show($file) {}

}

整个模板类的工作流程就是先实例化模板类对象,然后利用assign和assignArray方法给模板中的变量赋值,然后调用show方法,将模板和配置文件传入编译类的实例化对象中然后直接include编译后的php、html混编文件,显示输出。简单的流程就是这样,详细的代码如下

public function show($file) {
    $this->file = $file;
    if(!is_file($this->path())) {
        exit("找不到对应的模板文件");
    }

    $compile_file = $this->_arrayConfig['compile_dir']. md5($file). '.php';
    $cache_file = $this->_arrayConfig['cache_dir']. md5($file). $this->_arrayConfig['suffix_cache'];

    // 如果需要重新编译文件
    if($this->reCache($file) === false) {
        $this->_compileTool = new CompileClass($this->path(), $compile_file, $this->_arrayConfig);

        if($this->needCache()) {
            // 输出到缓冲区
            ob_start();
        }
        // 将赋值的变量导入当前符号表
        extract($this->_value, EXTR_OVERWRITE);

        if(!is_file($compile_file) or filemtime($compile_file) < filemtime($this->path())) {
            $this->_compileTool->vars = $this->_value;
            $this->_compileTool->compile();
            include($compile_file);
        }
        else {
            include($compile_file);
        }

        // 如果需要编译成静态文件
        if($this->needCache() === true) {
            $message = ob_get_contents();
            file_put_contents($cache_file, $message);
        }
    }
    else {
        readfile($cache_file);
    }
}

在show方法中,我首先判断模板文件存在,然后利用MD5编码生成编译文件和缓存文件的文件名。然后就是判断是否需要进行编译,判断的依据是看编译文件是否存在和编译文件的写入时间是否小于模板文件。如果需要编译,就利用编译类进行编译,生成一个php文件。然后只需要include这个编译文件就好了。

为了加快模板的载入,可以将编译后的文件输出到缓冲区中,也就是ob_start()这个函数,所有的输出将不会输出到浏览器,而是输出到默认的缓冲区,在利用ob_get_contents()将输出读取出来,保存成静态的html文件。

具体的使用如下

require('Template.php');

$config = array(
    'debug' => true,
    'cache_htm' => false,
    'debug' => true
);

$tpl = new Template($config);
$tpl->assign('data', microtime(true));
$tpl->assign('vars', array(1,2,3));
$tpl->assign('title', "hhhh");
$tpl->show('test');

缓存后的文件如下

<!DOCTYPE html>
<html>
    <head>
        <title>hhhh</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        1466525760.32                                    <input value="1">
                                                <input value="123123">
                                                <input value="sdfsas是aa">

                    <input value="1">
                    <input value="2">
                    <input value="3">
                <script src="123?t=1465898652"></script>
    </body>
</html>

一个简单的自定义模板引擎就完成了,虽然简陋但是能用,而且重点在于造轮子的乐趣和收获。

完整代码可见我的 github

欢迎访问我的个人博客:www.czrzchao.com

时间: 2024-08-14 12:03:26

自定义php模板引擎的相关文章

PHP 自定义 Smarty 模板引擎类 高洛峰 细说PHP

smarty模板引擎类简单工作原理 利用Smarty 模板引擎类对模板文件中的变量进行编译,编译过程其实就是利用正则表达式翻译成PHP文件.例如 模板文件中{$title} 利用正则表达式找到并替换成  <?php echo $this->vars['title'];?> 自定义 Smarty 模板引擎类 smarty.class.php页面 <?php /*  * 自定义Smarty模板引擎类  */         class Smarty{             priva

前端学PHP之自定义模板引擎

什么是网站模板?准确地说,是指网站页面模板,即每个页面仅是一个板式,包括结构.样式和页面布局,是创建网页内容的样板,也可以理解为已有的网页框架.可以将模板中原有的内容替换成从服务器端数据库中动态内容,目的是可以保持页面风格一致 PHP是一种HTML内嵌式的在服务器端执行的脚本语言,所以大部分PHP开发出来的Web应用,初始的开发模板就是混合层的数据编程.虽然通过MVC设计模式可以把程序应用逻辑与网页呈现逻辑强制性分离,但也只是将应用程序的输入.处理和输出分开,网页呈现逻辑(视图)还会有HTML代

PHP的自定义模板引擎

前面的话 在大多数的项目组中,开发一个Web程序都会出现这样的流程:计划文档提交之后,前端工程师制作了网站的外观模型,然后把它交给后端工程师,它们使用后端代码实现程序逻辑,同时使用外观模型做成基本架构,然后工程被返回到前端工程师继续完善.就这样工程可能在后端工程师和前端工程师之间来来回回好几次.由于后端工程师不干预任何相关HTML标签,同时也不需要前端代码和后端代码混合在一起.前端工程师只需要配置文件,动态区块和其他的界面部分,不必要去接触那些错综复杂的后端代码.因此,这时候有一个很好的模板支持

web框架--tornado框架之模板引擎

使用Tornado实现一个简陋的任务表功能demo来讲解tornado框架模板引擎 一.demo目录结构 二.具体文件内容 2.1.commons.css .body{ margin: 0; background-color: bisque; } 2.2.index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title

phalcon模板引擎(volt)自定义过滤器

引子:今天遇到一个问题,模板中某些变量有时候没有定义,如果php开启了E_ALL报错级别,那么会出现一个notice,提示变量未定义. 一开始的想法是通过三元表达式( )来给每个变量设定一个默认值,后来发现这种写法太繁琐,而且与volt标签格格不入. 再后来想到屏蔽E_NOTICE报告,但开发环境下还是很不恰当的. 最后发现volt引擎有一个过滤器Filters功能,解决了这个问题,写法如:{{ var|default('str') }} ,在$var为空的时候,将会将该处设置为str,当然,s

(转)浅谈dedecms模板引擎工作原理及自定义标签

理解织梦模板引擎有什么意义?一方面可以更好地自定义标签.更多在于了解织梦系统,理解模板引擎是理解织梦工作原理的第一步.理解织梦会使我们写php代码时更顺手,同时能学习一些php代码的组织方式. 这似乎不是那么简单,如果你只想学习自定义标签,可以看一下“是否需要自定义标签”和““扩展标签””就够了. 一解析式引擎 如果你还没用过dedecms的标签,先用一下,也可以看一下“dedecms网页模板编写”.熟悉一下memberlist这个标签,下面会以这个标签为例. 织梦提供的模板分析引擎有解析式和编

php模板引擎的原理与简单实例

模板引擎其实就是将一个带有自定义标签的字符串,通过相应的规则解析,返回php可以解析的字符串,这其中正则的运用是必不可少的,所以要有一定的正则基础.总体思想,引入按规则写好的模板,传递给标签解析类(_HtmlTag)进行解析,再把解析好的字符串传递给php进行解析渲染输出首先定义了一个_HtmlTag类: class _HtmlTag{ protected $taglist = "if|elseif|else|loop|for|while|=|:=|:e|:html|:"; prote

前端学PHP之Smarty模板引擎

前面的话 对PHP来说,有很多模板引擎可供选择,但Smarty是一个使用PHP编写出来的,是业界最著名.功能最强大的一种PHP模板引擎.Smarty像PHP一样拥有丰富的函数库,从统计字数到自动缩进.文字环绕以及正则表达式都可以直接使用,如果觉得不够,SMARTY还有很强的扩展能力,可以通过插件的形式进行扩充.另外,Smarty也是一种自由软件,用户可以自由使用.修改,以及重新分发该软件.本文将详细介绍Smarty模板引擎 概述 Smarty是一个php模板引擎.更准确的说,它分离了逻辑程序和外

Spring Boot基础教程8-web应用开发-模板引擎jsp

一.spring boot的web应用开发,是基于spring mvc 二.Spring boot 在spring默认基础上,自动配置添加了以下特性: 1.   包含了ContentNegotiatingViewResolver和BeanNameViewResolver beans. 2.   对静态资源的支持,包括对WebJars的支持. 3.   自动注册Converter,GenericConverter,Formatter beans. 4.   对HttpMessageConverte