简单的自定义php模板引擎


Posted in PHP onAugust 26, 2016

模板引擎的思想是来源于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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
用PHP实现Ftp用户的在线管理的代码
Mar 06 PHP
php 进度条实现代码
Mar 10 PHP
PHP sprintf()函数用例解析
May 18 PHP
PHP中运用jQuery的Ajax跨域调用实现代码
Feb 21 PHP
php中用加号与用array_merge合并数组的区别深入分析
Jun 03 PHP
php图片的二进制转换实现方法
Dec 15 PHP
ucenter通信原理分析
Jan 09 PHP
php绘制一个扇形的方法
Jan 24 PHP
PHP中的随机性 你觉得自己幸运吗?
Jan 22 PHP
浅谈PHP中的面向对象OOP中的魔术方法
Jun 12 PHP
php读取本地json文件的实例
Mar 07 PHP
PHP Primary script unknown 解决方法总结
Aug 22 PHP
PHP flush 函数使用注意事项
Aug 26 #PHP
PHP获取不了React Native Fecth参数的解决办法
Aug 26 #PHP
简单的pgsql pdo php操作类实现代码
Aug 25 #PHP
php mongodb操作类 带几个简单的例子
Aug 25 #PHP
php flush无效,IIS7下php实时输出的方法
Aug 25 #PHP
php 的反射详解及示例代码
Aug 25 #PHP
php 输入输出流详解及示例代码
Aug 25 #PHP
You might like
一个比较简单的PHP 分页分组类
2009/12/10 PHP
PHP连接操作access数据库实例
2015/03/30 PHP
几个优化WordPress中JavaScript加载体验的插件介绍
2015/12/17 PHP
php中__toString()方法用法示例
2016/12/07 PHP
利用PHP访问MySql数据库的逻辑操作以及增删改查的实例讲解
2017/08/30 PHP
使用jQuery实现dropdownlist的联动效果(sharepoint 2007)
2011/03/30 Javascript
向当前style sheet中插入一个新的style实现方法
2013/04/01 Javascript
js 上下左右键控制焦点(示例代码)
2013/12/14 Javascript
javascript下拉列表中显示树形菜单的实现方法
2015/11/17 Javascript
相册展示PhotoSwipe.js插件实现
2016/08/25 Javascript
js绘制购物车抛物线动画
2020/11/18 Javascript
纯JS实现轮播图
2017/02/22 Javascript
使用vue.js编写蓝色拼图小游戏
2017/03/17 Javascript
jquery与js实现全选功能的区别
2017/06/11 jQuery
js 将canvas生成图片保存,或直接保存一张图片的实现方法
2018/01/02 Javascript
Nuxt升级2.0.0时出现的问题(小结)
2018/10/08 Javascript
JS使用对象的defineProperty进行变量监控操作示例
2019/02/02 Javascript
JavaScript原型继承和原型链原理详解
2020/02/04 Javascript
[02:16]卖萌的僵尸 DOTA2神话信使飞僵小宝来袭
2014/03/24 DOTA
[02:19]2018年度DOTA2最佳核心位选手-完美盛典
2018/12/17 DOTA
[01:02:09]Liquid vs TNC 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.21
2020/07/19 DOTA
Python中unittest用法实例
2014/09/25 Python
Python中__init__.py文件的作用详解
2016/09/18 Python
利用Python实现颜色色值转换的小工具
2016/10/27 Python
分析运行中的 Python 进程详细解析
2019/06/22 Python
Python TCP通信客户端服务端代码实例
2019/11/21 Python
python如何提取英语pdf内容并翻译
2020/03/03 Python
使用phonegap进行本地存储的实现方法
2017/03/31 HTML / CSS
美国学校用品、教室和教学商店:Discount School Supply
2018/04/04 全球购物
欧洲最大的高尔夫零售商:American Golf
2019/09/02 全球购物
PHP面试题-$message和$$message的区别
2015/12/08 面试题
WINDOWS域的具体实现方式是什么
2014/02/20 面试题
应届毕业生的个人自我鉴定
2013/10/24 职场文书
护士岗位求职应聘自荐书范文
2014/02/12 职场文书
2015年副班长工作总结
2015/05/15 职场文书
《钓鱼的启示》教学反思
2016/02/18 职场文书