简单的自定义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 相关文章推荐
Ajax PHP分页演示
Jan 02 PHP
php编程每天必学之验证码
Mar 03 PHP
详解PHP实现异步调用的4种方法
Mar 14 PHP
php打包压缩文件之ZipArchive方法用法分析
Apr 30 PHP
thinkPHP连接sqlite3数据库的实现方法(附Thinkphp代码生成器下载)
May 27 PHP
yii2.0实现创建简单widgets示例
Jul 18 PHP
PHP判断密码强度的方法详解
May 26 PHP
浅析PHP开发规范
Feb 05 PHP
PHP实现的多维数组排序算法分析
Feb 10 PHP
PHP ADODB生成HTML表格函数rs2html功能【附错误处理函数用法】
May 29 PHP
PHP的mysqli_stmt_init()函数讲解
Jan 24 PHP
Yii框架中使用PHPExcel的方法分析
Jul 25 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编程语言开发动态WAP页面
2006/10/09 PHP
php巧获服务器端信息
2006/12/06 PHP
用PHP查询搜索引擎排名位置的代码
2010/01/05 PHP
解决文件名解压后乱码的问题 将文件名进行转码的代码
2012/01/10 PHP
php使用curl检测网页是否被百度收录的示例分享
2014/01/31 PHP
jQuery+PHP+ajax实现微博加载更多内容列表功能
2014/06/27 PHP
php实现的click captcha点击验证码类实例
2014/09/23 PHP
PHP实现文件下载详解
2014/11/27 PHP
PHP自定义多进制的方法
2016/11/03 PHP
Laravel框架控制器的middleware中间件用法分析
2019/09/30 PHP
jQuery+JSON实现AJAX二级联动实例分析
2015/12/18 Javascript
javascript实现简单的全选和反选功能
2016/01/05 Javascript
jQuery CSS3自定义美化Checkbox实现代码
2016/05/12 Javascript
解决同一页面中两个iframe互相调用jquery,js函数的方法
2016/12/12 Javascript
原生JS轮播图插件
2017/02/09 Javascript
javascript过滤数组重复元素的实现方法
2017/05/03 Javascript
bootstrap table插件的分页与checkbox使用详解
2017/07/23 Javascript
vue中七牛插件使用的实例代码
2017/07/28 Javascript
Vue.js 中的实用工具方法【推荐】
2019/07/04 Javascript
深入理解Vue keep-alive及实践总结
2019/08/21 Javascript
微信小程序如何实现在线客服功能
2019/10/16 Javascript
vue等两个接口都返回结果再执行下一步的实例
2020/09/08 Javascript
小程序实现上下切换位置
2020/11/16 Javascript
[00:10]神之谴戒
2019/03/06 DOTA
Python中SOAP项目的介绍及其在web开发中的应用
2015/04/14 Python
Python异常处理知识点总结
2019/02/18 Python
Python 存取npy格式数据实例
2020/07/01 Python
python如何快速拼接字符串
2020/10/28 Python
详解移动端HTML5音频与视频问题及解决方案
2018/08/22 HTML / CSS
利达恒信公司.NET笔试题面试题
2016/03/05 面试题
园林设计师自荐信
2013/11/18 职场文书
信息服务专业毕业生求职信
2014/03/02 职场文书
材料采购员岗位职责
2015/04/03 职场文书
行政申诉状范文
2015/05/20 职场文书
java版 简单三子棋游戏
2022/05/04 Java/Android
Python Flask实现进度条
2022/05/11 Python