简单的自定义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 XML备份Mysql数据库
May 27 PHP
php file_get_contents函数轻松采集html数据
Apr 22 PHP
PHP 数组基础知识小结
Aug 20 PHP
PHP mb_convert_encoding文字编码的转换函数介绍
Nov 10 PHP
php实现在线生成条形码示例分享(条形码生成器)
Dec 30 PHP
PHP新建类问题分析及解决思路
Nov 19 PHP
PHP实现递归目录的5种方法
Oct 27 PHP
Laravel 5.4重新登录实现跳转到登录前页面的原理和方法
Jul 13 PHP
laravel5.4生成验证码的实例讲解
Aug 05 PHP
php打开本地exe程序,js打开本地exe应用程序,并传递相关参数方法
Feb 06 PHP
PHP有序表查找之二分查找(折半查找)算法示例
Feb 09 PHP
Laravel框架实现定时Task Scheduling例子
Oct 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中使用foreach和引用导致程序BUG的问题介绍
2012/09/05 PHP
dedecms集成财付通支付接口
2014/12/28 PHP
WordPres对前端页面调试时的两个PHP函数使用小技巧
2015/12/22 PHP
PHP利用超级全局变量$_POST来接收表单数据的实例
2016/11/05 PHP
JavaScript的document对象和window对象详解
2010/12/30 Javascript
javascript判断用户浏览器插件安装情况的代码
2011/01/01 Javascript
利用javascript实现web页面中指定区域打印
2013/10/30 Javascript
JavaScript设计模式之外观模式介绍
2014/12/28 Javascript
jQueryMobile之Helloworld与页面切换的方法
2015/02/04 Javascript
javascript制作的滑动图片菜单
2015/05/15 Javascript
Winform客户端向web地址传参接收参数的方法
2016/05/17 Javascript
javascript 数组去重复(在线去重工具)
2016/12/17 Javascript
JS设置CSS样式的方式汇总
2017/01/21 Javascript
微信小程序中的swiper组件详解
2017/04/14 Javascript
总结4个方面优化Vue项目
2019/02/11 Javascript
微信小程序实现左滑动删除效果
2020/03/30 Javascript
layui实现鼠标移动到单元格上显示数据的方法
2019/09/11 Javascript
[04:28]2014DOTA2国际邀请赛 采访小兔子LGD挺进钥匙体育馆
2014/07/14 DOTA
Python函数嵌套实例
2014/09/23 Python
详解Django中的form库的使用
2015/07/18 Python
Python利用带权重随机数解决抽奖和游戏爆装备问题
2016/06/16 Python
python 列表删除所有指定元素的方法
2018/04/19 Python
python交换两个变量的值方法
2019/01/12 Python
pycharm中显示CSS提示的知识点总结
2019/07/29 Python
nginx搭建基于python的web环境的实现步骤
2020/01/03 Python
jupyter notebook 恢复误删单元格或者历史代码的实现
2020/04/17 Python
12个不为大家熟知的HTML5设计小技巧
2016/06/02 HTML / CSS
澳大利亚领先的睡衣品牌:Peter Alexander
2016/08/16 全球购物
会计系毕业个人自荐信格式
2013/09/23 职场文书
法律专业个人实习自我鉴定
2013/09/23 职场文书
机械制造专业大学生自我鉴定
2014/09/19 职场文书
先进教育工作者事迹材料
2014/12/23 职场文书
世界地球日活动总结
2015/02/09 职场文书
2016年优秀少先队辅导员事迹材料
2016/02/26 职场文书
总结Python连接CS2000的详细步骤
2021/06/23 Python
《艾尔登法环》1.03.3补丁上线 碎星伤害调整
2022/04/06 其他游戏