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字符串截取问题
Nov 28 PHP
PHP 面向对象 PHP5 中的常量
May 05 PHP
延长phpmyadmin登录时间的方法
Feb 06 PHP
php教程之魔术方法的使用示例(php魔术函数)
Feb 12 PHP
php实例分享之二维数组排序
May 15 PHP
php中smarty实现多模版网站的方法
Jun 11 PHP
PHP生成和获取XML格式数据的方法
Mar 04 PHP
PHP之将POST数据转化为字符串的实现代码
Nov 03 PHP
mac os快速切换多个PHP版本的方法
Mar 07 PHP
PHP使用GD库制作验证码的方法(点击验证码或看不清会刷新验证码)
Aug 15 PHP
PHP后期静态绑定之self::限制实例分析
Dec 21 PHP
在Laravel的Model层做数据缓存的实现
Sep 26 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
php中神奇的fastcgi_finish_request
2011/05/02 PHP
php+iframe实现隐藏无刷新上传文件
2012/02/10 PHP
thinkphp视图模型查询提示ERR: 1146:Table 'db.pr_order_view' doesn't exist的解决方法
2014/10/30 PHP
PHP 5.6.11 访问SQL Server2008R2的几种情况详解
2016/08/08 PHP
THINKPHP5分页数据对象处理过程解析
2020/10/28 PHP
javascript引用对象的方法
2007/01/11 Javascript
javascript同步Import,同步调用外部js的方法
2008/07/08 Javascript
JS文本框不能输入空格验证方法
2013/03/19 Javascript
Javascript 中 null、NaN和undefined的区别总结
2013/04/10 Javascript
jQuery制作可自定义大小的拼图游戏
2015/03/30 Javascript
Node.js操作Firebird数据库教程
2016/03/04 Javascript
jqGrid 学习笔记整理——进阶篇(一 )
2016/04/17 Javascript
js 自带的sort() 方法全面了解
2016/08/16 Javascript
微信小程序 教程之小程序配置
2016/10/17 Javascript
ASP.NET jquery ajax传递参数的实例
2016/11/02 Javascript
对layui中table组件工具栏的使用详解
2019/09/19 Javascript
Vue 刷新当前路由的实现代码
2019/09/26 Javascript
javascript设计模式 ? 代理模式原理与用法实例分析
2020/04/16 Javascript
vue-iview动态新增和删除的方法
2020/06/17 Javascript
Python读写文件方法总结
2015/06/09 Python
解决Django模板无法使用perms变量问题的方法
2017/09/10 Python
python 通过 socket 发送文件的实例代码
2018/08/14 Python
利用Python将数值型特征进行离散化操作的方法
2018/11/06 Python
使用OpenCV实现仿射变换—平移功能
2019/08/29 Python
python使用gdal对shp读取,新建和更新的实例
2020/03/10 Python
Python使用re模块验证危险字符
2020/05/21 Python
通过css3的filter滤镜改变png图片的颜色的示例代码
2020/05/06 HTML / CSS
使用phonegap检测网络状态的方法
2017/03/30 HTML / CSS
canvas实现二维码和图片合成的示例代码
2018/08/01 HTML / CSS
StubHub墨西哥:购买和出售您的门票
2016/09/17 全球购物
Sephora丝芙兰菲律宾官方网站:购买化妆品和护肤品
2017/04/05 全球购物
借款协议书
2014/04/12 职场文书
年度安全生产目标责任书
2014/07/23 职场文书
消防志愿者活动方案
2014/08/23 职场文书
青春雷锋观后感
2015/06/10 职场文书
js实现上传图片到服务器
2021/04/11 Javascript