PHP中MVC模式的模板引擎开发经验分享


Posted in PHP onMarch 23, 2011

使Web系统的开发与维护更加方便,从而有效的节省人力物力,受到了越来越多企业的青眯。

模板引擎是MVC模式建立过程的重要方法,开发者可以设计一套赋予含义的标签,通过技术解析处理有效的把数据逻辑处理从界面模板中提取出来,通过解读标签的含义把控制权提交给相应业务逻辑处理程序,从而获取到需要的数据,以模板设计的形式展现出来,使设计人员能把精力更多放在表现形式上。下面是我对模板引擎的认识与设计方法:

说的好听些叫模板引擎,实际就是解读模板数据的过程(个人观点^^)。通过我对建站方面的思考认识,网站在展现形式上无非归纳为单条和多条两种形式,那么我们可以设定两种对应标签(如data、list)来处理这两种情况,关键点在于解决两种标签的多层相互嵌套问题,基本适合实现80%界面形式。

解读模板的方法有多种,常用的包括字符串处理(解决嵌套稍麻烦)、正则表达式。在这里我选用的正则表达式,下面是我的处理方法(本文仅提供思路和参考代码,可能不能直接使用)。

模板文件解析类:

<?php 
/* 
* class: 模板解析类 
* author: 51JS.COM-ZMM 
* date: 2011.3.1 
* email: 304924248@qq.com 
* blog: http://www.cnblogs.com/cnzmm/ 
*/ 
class Template { 
public $html, $vars, $bTag, $eTag; 
public $bFlag='{', $eFlag='}', $pfix='zmm:'; 
private $folder, $file; 
function __construct($vars=array()) { 
!empty($vars) && $this->vars = $vars; 
!empty($GLOBALS['cfg_tag_prefix']) && 
$this->pfix = $GLOBALS['cfg_tag_prefix'].':'; 
$this->bTag = $this->bFlag.$this->pfix; 
$this->eTag = $this->bFlag.'\/'.$this->pfix; 
empty(Tags::$vars) && Tags::$vars = &$this->vars; 
} 
public function LoadTpl($tpl) { 
$this->file = $this->GetTplPath($tpl); 
Tags::$file = &$this->file; 
if (is_file($this->file)) { 
if ($this->GetTplHtml()) { 
$this->SetTplTags(); 
} else { 
exit('模板文件加载失败!'); 
} 
} else { 
exit('模板文件['.$this->file.']不存在!'); 
} 
} 
private function GetTplPath($tpl) { 
$this->folder = WEBSITE_DIRROOT. 
$GLOBALS['cfg_tpl_root']; 
return $this->folder.'/'.$tpl; 
} 
private function GetTplHtml() { 
$html = self::FmtTplHtml(file_get_contents($this->file)); 
if (!empty($html)) { 
$callFunc = Tags::$prefix.'Syntax'; 
$this->html = Tags::$callFunc($html, new Template()); 
} else { 
exit('模板文件内容为空!'); 
} return true; 
} 
static public function FmtTplHtml($html) { 
return preg_replace('/(\r)|(\n)|(\t)|(\s{2,})/is', '', $html); 
} 
public function Register($vars=array()) { 
if (is_array($vars)) { 
$this->vars = $vars; 
Tags::$vars = &$this->vars; 
} 
} 
public function Display($bool=false, $name="", $time=0) { 
if (!empty($this->html)) { 
if ($bool && !empty($name)) { 
if (!is_int($time)) $time = 600; 
$cache = new Cache($time); 
$cache->Set($name, $this->html); 
} 
echo $this->html; flush(); 
} else { 
exit('模板文件内容为空!'); 
} 
} 
public function SetAssign($souc, $info) { 
if (!empty($this->html)) { 
$this->html = str_ireplace($souc, self::FmtTplHtml($info), $this->html); 
} else { 
exit('模板文件内容为空!'); 
} 
} 
private function SetTplTags() { 
$this->SetPanelTags(); $this->SetTrunkTags(); $this->RegHatchVars(); 
} 
private function SetPanelTags() { 
$rule = $this->bTag.'([^'.$this->eFlag.']+)\/'.$this->eFlag; 
preg_match_all('/'.$rule.'/ism', $this->html, $out_matches); 
$this->TransTag($out_matches, 'panel'); unset($out_matches); 
} 
private function SetTrunkTags() { 
$rule = $this->bTag.'(\w+)\s*([^'.$this->eFlag.']*?)'.$this->eFlag. 
'((?:(?!'.$this->bTag.')[\S\s]*?|(?R))*)'.$this->eTag.'\\1\s*'.$this->eFlag; 
preg_match_all('/'.$rule.'/ism', $this->html, $out_matches); 
$this->TransTag($out_matches, 'trunk'); unset($out_matches); 
} 
private function TransTag($result, $type) { 
if (!empty($result[0])) { 
switch ($type) { 
case 'panel' : { 
for ($i = 0; $i < count($result[0]); $i ++) { 
$strTag = explode(' ', $result[1][$i], 2); 
if (strpos($strTag[0], '.')) { 
$itemArg = explode('.', $result[1][$i], 2); 
$callFunc = Tags::$prefix.ucfirst($itemArg[0]); 
if (method_exists('Tags', $callFunc)) { 
$html = Tags::$callFunc(chop($itemArg[1])); 
if ($html !== false) { 
$this->html = str_ireplace($result[0][$i], $html, $this->html); 
} 
} 
} else { 
$rule = '^([^\s]+)\s*([\S\s]+)$'; 
preg_match_all('/'.$rule.'/is', trim($result[1][$i]), $tmp_matches); 
$callFunc = Tags::$prefix.ucfirst($tmp_matches[1][0]); 
if (method_exists('Tags', $callFunc)) { 
$html = Tags::$callFunc($tmp_matches[2][0]); 
if ($html !== false) { 
$this->html = str_ireplace($result[0][$i], $html, $this->html); 
} 
} unset($tmp_matches); 
} 
} break; 
} 
case 'trunk' : { 
for ($i = 0; $i < count($result[0]); $i ++) { 
$callFunc = Tags::$prefix.ucfirst($result[1][$i]); 
if (method_exists('Tags', $callFunc)) { 
$html = Tags::$callFunc($result[2][$i], $result[3][$i]); 
$this->html = str_ireplace($result[0][$i], $html, $this->html); 
} 
} break; 
} 
default: break; 
} 
} else { 
return false; 
} 
} 
private function RegHatchVars() { 
$this->SetPanelTags(); 
} 
function __destruct() {} 
} 
?>

标签解析类:(目前暂时提供data、list两种标签的解析,说明思路)
<?php 
/* 
* class: 标签解析类 
* author: 51JS.COM-ZMM 
* date: 2011.3.2 
* email: 304924248@qq.com 
* blog: http://www.cnblogs.com/cnzmm/ 
*/ 
class Tags { 
static private $attrs=null; 
static public $file, $vars, $rule, $prefix='TAG_'; 
static public function TAG_Syntax($html, $that) { 
$rule = $that->bTag.'if\s+([^'.$that->eFlag.']+)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php if (\\1) { ?>', $html); 
$rule = $that->bTag.'elseif\s+([^'.$that->eFlag.']+)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php } elseif (\\1) { ?>', $html); 
$rule = $that->bTag.'else\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php } else { ?>', $html); 
$rule = $that->bTag.'loop\s+(\S+)\s+(\S+)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php foreach (\\1 as \\2) { ?>', $html); 
$rule = $that->bTag.'loop\s+(\S+)\s+(\S+)\s+(\S+)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php foreach (\\1 as \\2 => \\3) { ?>', $html); 
$rule = $that->eTag.'(if|loop)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php } ?>', $html); 
$rule = $that->bTag.'php\s*'.$that->eFlag.'((?:(?!'. 
$that->bTag.')[\S\s]*?|(?R))*)'.$that->eTag.'php\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php \\1 ?>', $html); 
return self::TAG_Execute($html); 
} 
static public function TAG_List($attr, $html) { 
if (!empty($html)) { 
if (self::TAG_HaveTag($html)) { 
return self::TAG_DealTag($attr, $html, true); 
} else { 
return self::TAG_GetData($attr, $html, true); 
} 
} else { 
exit('标签{list}的内容为空!'); 
} 
} 
static public function TAG_Data($attr, $html) { 
if (!empty($html)) { 
if (self::TAG_HaveTag($html)) { 
return self::TAG_DealTag($attr, $html, false); 
} else { 
return self::TAG_GetData($attr, $html, false); 
} 
} else { 
exit('标签{data}的内容为空!'); 
} 
} 
static public function TAG_Execute($html) { 
ob_clean(); ob_start(); 
if (!empty(self::$vars)) { 
is_array(self::$vars) && 
extract(self::$vars, EXTR_OVERWRITE); 
} 
$file_inc = WEBSITE_DIRINC.'/buffer/'. 
md5(uniqid(rand(), true)).'.php'; 
if ($fp = fopen($file_inc, 'xb')) { 
fwrite($fp, $html); 
if (fclose($fp)) { 
include($file_inc); 
$html = ob_get_contents(); 
} unset($fp); 
} else { 
exit('模板解析文件生成失败!'); 
} ob_end_clean(); @unlink($file_inc); 
return $html; 
} 
static private function TAG_HaveTag($html) { 
$bool_has = false; 
$tpl_ins = new Template(); 
self::$rule = $tpl_ins->bTag.'([^'.$tpl_ins->eFlag.']+)\/'.$tpl_ins->eFlag; 
$bool_has = $bool_has || preg_match('/'.self::$rule.'/ism', $html); 
self::$rule = $tpl_ins->bTag.'(\w+)\s*([^'.$tpl_ins->eFlag.']*?)'.$tpl_ins->eFlag. 
'((?:(?!'.$tpl_ins->bTag.')[\S\s]*?|(?R))*)'.$tpl_ins->eTag.'\\1\s*'.$tpl_ins->eFlag; 
$bool_has = $bool_has || preg_match('/'.self::$rule.'/ism', $html); 
unset($tpl_ins); 
return $bool_has; 
} 
static private function TAG_DealTag($attr, $html, $list) { 
preg_match_all('/'.self::$rule.'/ism', $html, $out_matches); 
if (!empty($out_matches[0])) { 
$child_node = array(); 
for ($i = 0; $i < count($out_matches[0]); $i ++) { 
$child_node[] = $out_matches[3][$i]; 
$html = str_ireplace($out_matches[3][$i], '{-->>child_node_'.$i.'<<--}', $html); 
} 
$html = self::TAG_GetData($attr, $html, $list); 
for ($i = 0; $i < count($out_matches[0]); $i ++) { 
$html = str_ireplace('{-->>child_node_'.$i.'<<--}', $child_node[$i], $html); 
} 
preg_match_all('/'.self::$rule.'/ism', $html, $tmp_matches); 
if (!empty($tmp_matches[0])) { 
for ($i = 0; $i < count($tmp_matches[0]); $i ++) { 
$callFunc = self::$prefix.ucfirst($tmp_matches[1][$i]); 
if (method_exists('Tags', $callFunc)) { 
$temp = self::$callFunc($tmp_matches[2][$i], $tmp_matches[3][$i]); 
$html = str_ireplace($tmp_matches[0][$i], $temp, $html); 
} 
} 
} 
unset($tmp_matches); 
} 
unset($out_matches); return $html; 
} 
static private function TAG_GetData($attr, $html, $list=false) { 
if (!empty($attr)) { 
$attr_ins = new Attbt($attr); 
$attr_arr = $attr_ins->attrs; 
if (is_array($attr_arr)) { 
extract($attr_arr, EXTR_OVERWRITE); 
$source = table_name($source, $column); 
$rule = '\[field:\s*(\w+)\s*([^\]]*?)\s*\/?]'; 
preg_match_all('/'.$rule.'/is', $html, $out_matches); 
$data_str = ''; 
$data_ins = new DataSql(); 
$attr_where = $attr_order = ''; 
if (!empty($where)) { 
$where = str_replace(',', ' and ', $where); 
$attr_where = ' where '. $where; 
} 
if (!empty($order)) { 
$attr_order = ' order by '.$order; 
} else { 
$fed_name = ''; 
$fed_ins = $data_ins->GetFedNeedle($source); 
$fed_cnt = $data_ins->GetFedCount($fed_ins); 
for ($i = 0; $i < $fed_cnt; $i ++) { 
$fed_flag = $data_ins->GetFedFlag($fed_ins, $i); 
if (preg_match('/auto_increment/ism', $fed_flag)) { 
$fed_name = $data_ins->GetFedName($fed_ins, $i); 
break; 
} 
} 
if (!empty($fed_name)) 
$attr_order = ' order by '.$fed_name.' desc'; 
} 
if ($list == true) { 
if (empty($source) && empty($sql)) { 
exit('标签{list}必须指定source属性!'); 
} 
$attr_rows = $attr_page = ''; 
if ($rows > 0) { 
$attr_rows = ' limit 0,'.$rows; 
} 
if (!empty($sql)) { 
$data_sql = $sql; 
} else { 
$data_sql = 'select * from `'.$source.'`'. 
$attr_where.$attr_order.$attr_rows; 
} 
if ($pages=='true' && !empty($size)) { 
$data_num = $data_ins->GetRecNum($data_sql); 
$page_cnt = ceil($data_num / $size); 
global $page; 
if (!isset($page) || $page < 1) $page = 1; 
if ($page > $page_cnt) $page = $page_cnt; 
$data_sql = 'select * from `'.$source.'`'.$attr_where. 
$attr_order.' limit '.($page-1) * $size.','.$size; 
$GLOBALS['cfg_page_curr'] = $page; 
$GLOBALS['cfg_page_prev'] = $page - 1; 
$GLOBALS['cfg_page_next'] = $page + 1; 
$GLOBALS['cfg_page_nums'] = $page_cnt; 
if (function_exists('list_pagelink')) { 
$GLOBALS['cfg_page_list'] = list_pagelink($page, $page_cnt, 2); 
} 
} 
$data_idx = 0; 
$data_ret = $data_ins->SqlCmdExec($data_sql); 
while ($row = $data_ins->GetRecArr($data_ret)) { 
if ($skip > 0 && !empty($flag)) { 
$data_idx != 0 && 
$data_idx % $skip == 0 && 
$data_str .= $flag; 
} 
$data_tmp = $html; 
$data_tmp = str_ireplace('@idx', $data_idx, $data_tmp); 
for ($i = 0; $i < count($out_matches[0]); $i ++) { 
$data_tmp = str_ireplace($out_matches[0][$i], 
$row[$out_matches[1][$i]], $data_tmp); 
} 
$data_str .= $data_tmp; $data_idx ++; 
} 
} else { 
if (empty($source)) { 
exit('标签{data}必须指定source属性!'); 
} 
$data_sql = 'select * from `'.$source. 
'`'.$attr_where.$attr_order; 
$row = $data_ins->GetOneRec($data_sql); 
if (is_array($row)) { 
$data_tmp = $html; 
for ($i = 0; $i < count($out_matches[0]); $i ++) { 
$data_val = $row[$out_matches[1][$i]]; 
if (empty($out_matches[2][$i])) { 
$data_tmp = str_ireplace($out_matches[0][$i], $data_val, $data_tmp); 
} else { 
$attr_str = $out_matches[2][$i]; 
$attr_ins = new Attbt($attr_str); 
$func_txt = $attr_ins->attrs['function']; 
if (!empty($func_txt)) { 
$func_tmp = explode('(', $func_txt); 
if (function_exists($func_tmp[0])) { 
eval('$func_ret ='.str_ireplace('@me', 
'\''.$data_val.'\'', $func_txt)); 
$data_tmp = str_ireplace($out_matches[0][$i], $func_ret, $data_tmp); 
} else { 
exit('调用了不存在的函数!'); 
} 
} else { 
exit('标签设置属性无效!'); 
} 
} 
} 
$data_str .= $data_tmp; 
} 
} 
unset($data_ins); 
return $data_str; 
} else { 
exit('标签设置属性无效!'); 
} 
} else { 
exit('没有设置标签属性!'); 
} 
} 
static public function __callStatic($name, $args) { 
exit('标签{'.$name.'}不存在!'); 
} 
} 
?>
PHP 相关文章推荐
php生成随机密码的几种方法
Jan 17 PHP
mac下安装nginx和php
Nov 04 PHP
php实现数组筛选奇数和偶数示例
Apr 11 PHP
ThinkPHP使用心得分享-ThinkPHP + Ajax 实现2级联动下拉菜单
May 15 PHP
PHP获取POST数据的几种方法汇总
Mar 03 PHP
PHP获取数组最大值下标的方法
May 12 PHP
php函数连续调用实例分析
Jul 30 PHP
PHP生成随机密码方法汇总
Aug 27 PHP
PhpStorm terminal无法输入命令的解决方法
Oct 09 PHP
phpcms中的评论样式修改方法
Oct 21 PHP
解决PHP 7编译安装错误:cannot stat ‘phar.phar’: No such file or directory
Feb 25 PHP
因str_replace导致的注入问题总结
Aug 08 PHP
PHP面向接口编程 耦合设计模式 简单范例
Mar 23 #PHP
PHP中用接口、抽象类、普通基类实现“面向接口编程”与“耦合方法”简述
Mar 23 #PHP
php中取得URL的根域名的代码
Mar 23 #PHP
PHP+JS+rsa数据加密传输实现代码
Mar 23 #PHP
PHP 事件机制(2)
Mar 23 #PHP
php函数之子字符串替换&amp;#65279; str_replace
Mar 23 #PHP
php expects parameter 1 to be resource, array given 错误
Mar 23 #PHP
You might like
全国中波电台频率表
2020/03/11 无线电
PHP树-不需要递归的实现方法
2016/06/21 PHP
教你在header中隐藏php的版本信息
2016/08/10 PHP
总结PHP内存释放以及垃圾回收
2018/03/29 PHP
浅谈php常用的7大框架的优缺点
2020/07/20 PHP
小型js框架veryide.librar源代码
2009/03/05 Javascript
js函数调用常用方法详解
2012/12/03 Javascript
固定表格行列(expression)在IE下适用
2013/07/25 Javascript
Jquery Uploadify上传带进度条的简单实例
2014/02/12 Javascript
javascript实现客户端兼容各浏览器创建csv并下载的方法
2015/03/23 Javascript
JavaScript动态设置div的样式的方法
2015/12/26 Javascript
解决vue.js在编写过程中出现空格不规范报错的问题
2017/09/20 Javascript
js最简单的双向绑定实例讲解
2018/01/02 Javascript
vue拖拽组件使用方法详解
2018/12/01 Javascript
Python splitlines使用技巧
2008/09/06 Python
Python装饰器基础详解
2016/03/09 Python
Python运算符重载详解及实例代码
2017/03/07 Python
python+matplotlib绘制3D条形图实例代码
2018/01/17 Python
Python3 单行多行万能正则匹配方法
2019/01/07 Python
利用Python校准本地时间的方法教程
2019/10/31 Python
Python如何实现大型数组运算(使用NumPy)
2020/07/24 Python
全球最大的房车租赁市场:Outdoorsy
2018/09/19 全球购物
HomeAway英国:全球领先的度假租赁在线市场
2020/02/03 全球购物
写出SQL四条最基本的数据操作语句(DML)
2012/12/12 面试题
如何判断计算机可能已经中马
2013/03/22 面试题
财务会计实习报告体会
2013/12/20 职场文书
简历上的自我评价怎么写
2014/01/28 职场文书
道路交通安全实施方案
2014/03/12 职场文书
分层教学实施方案
2014/03/19 职场文书
开学寄语大全
2014/04/08 职场文书
文秘个人求职信范文
2014/04/22 职场文书
党员反对四风问题思想汇报
2014/09/12 职场文书
政府会议通知范文
2015/04/15 职场文书
Python 内置函数速查表一览
2021/06/02 Python
java中重写父类方法加不加@Override详解
2021/06/21 Java/Android
FP-growth算法发现频繁项集——发现频繁项集
2021/06/24 Python