《PHP Learning》模板引擎
- 模板處理
- 使用正則處理模板替換規(guī)則
- 保存模板編譯結(jié)果
- 模板使用
模板處理
使用正則處理模板替換規(guī)則
定界符
定義模板昨天定界符為<{
,右邊定界符為}>
岂津,對(duì)應(yīng)的變量為$left
和$right
虱黄,這兩個(gè)變量在后續(xù)的正則表達(dá)式會(huì)使用到
/* 將左右定界符號(hào)中,有影響正則的特殊符號(hào)轉(zhuǎn)義 例如吮成,<{ }>轉(zhuǎn)義\<\{ \}\> */
$left = preg_quote($this->left_delimiter, '/');
$right = preg_quote($this->right_delimiter, '/');
模板文件
模板文件定義了一個(gè)主入口文件以及主入口文件包含的多個(gè)從屬文件橱乱,這樣的處理方式也是為了能夠讓模板能夠做到模塊化,方便開發(fā)和維護(hù)
模板主入口文件main.html
<{ include "header.html" }>
<table border="1" align="center" width="90%" cellpadding="3" cellspacing="0">
<caption><h1> <{ $tableName }> <h1></caption>
<tr bgcolor="#cccccc">
<th>編號(hào)</th><th>姓名</th><th>性別</th><th>年齡</th><th>電子郵件</th>
</tr>
<{ loop $users $user }>
<tr>
<{ loop $user $colKey => $colValue }>
<{ if $colKey == "sex" }>
<{ if $colValue=="男" }>
<td bgColor="red"> <{ $colValue }> </td>
<{ elseif $colValue=="女" }>
<td bgColor="green"> <{ $colValue }> </td>
<{ else }>
<td bgColor="blue"> 未知 </td>
<{ /if }>
<{ else }>
<td> <{ $colValue }> </td>
<{ /if }>
<{ /loop }>
</tr>
<{ /loop }>
</table>
<center>共查找到<b> <{ $rowNum }> </b>條記錄</center>
<{ include 'footer.html' }>
從屬文件footer.html
<hr><center> ############### 作者:<{ $author }> ############## </center>
</body>
</html>
從屬文件header.html
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title> <{ $title }> </title>
</head>
<body>
替換變量
變量匹配規(guī)則的正則表達(dá)式
/* 1粱甫、匹配模板中變量 ,例如泳叠,"<{ $var }>" */
'/' . $left . '\s*\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*' . $right . '/i',
變量匹配規(guī)則的處理方法,該方法作為preg_replace_callback
正則替換的回調(diào)方法
/* 1茶宵、替換模板中的變量 <{ $var }> ==> <?php echo $this->tpl_vars["var"]; */
function ($matches) use ($parent) {
$result = '<?php echo $this->tpl_vars["' . $matches[1] . '"]; ?>';
return $result;
}
處理結(jié)果
// 原始的模板代碼
<{ $tableName }>
// 轉(zhuǎn)換后的php代碼
<?php echo $this->tpl_vars["tableName"]; ?>
模板文件的處理結(jié)果析二,左邊是原始模板的代碼,右邊是替換之后的PHP語(yǔ)法代碼
if語(yǔ)句替換
if語(yǔ)句匹配規(guī)則的正則表達(dá)式
/* 2、匹配模板中if標(biāo)識(shí)符叶摄,例如 "<{ if $col == "sex" }> statements <{ /if }>" */
'/' . $left . '\s*if\s*(.+?)\s*' . $right . '(.+)' . $left . '\s*\/if\s*' . $right . '/is',
if語(yǔ)句匹配規(guī)則的處理方法,該方法作為preg_replace_callback
正則替換的回調(diào)方法
/* 2安拟、E 替換模板中的if字符串 "<{ if $col == "sex" }> statements <{ /if }>" ==> <?php if($col == "sex") { ?> <?php } ?> */
function ($matches) use ($parent) {
$result = $parent->stripvtags('<?php if(' . $matches[1] . ') { ?>', $matches[2] . '<?php } ?>');
return $result;
}
/**
* 內(nèi)部使用的私有方法蛤吓,用來(lái)將條件語(yǔ)句中使用的變量替換為對(duì)應(yīng)的值
* @param string $expr 提供模板中條件語(yǔ)句的開始標(biāo)記
* @param string $statement 提供模板中條件語(yǔ)句的結(jié)束標(biāo)記
* @return strin 將處理后的條件語(yǔ)句相連后返回
*/
private function stripvtags($expr, $statement = '')
{
/* 匹配變量的正則 */
$var_pattern = '/\s*\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*/is';
/* 將變量替換為值 */
$expr = preg_replace($var_pattern, '$this->tpl_vars["${1}"]', $expr);
/* 將開始標(biāo)記中的引號(hào)轉(zhuǎn)義替換 */
$expr = str_replace("\\\"", "\"", $expr);
/* 替換語(yǔ)句體和結(jié)束標(biāo)記中的引號(hào) */
$statement = str_replace("\\\"", "\"", $statement);
/* 將處理后的條件語(yǔ)句相連后返回 */
return $expr . $statement;
}
替換的結(jié)果如下
// 原始的模板代碼
<?php if($colKey == "sex") { ?>
// 轉(zhuǎn)換后的php代碼
<?php if($this->tpl_vars["colKey"]== "sex") { ?>
<{ if $colValue=="男" }>
<td bgColor="red"> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<{ elseif $colValue=="女" }>
<td bgColor="green"> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<{ else }>
<td bgColor="blue"> 未知 </td>
<{ /if }>
<{ else }>
<td> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } ?>
模板文件的處理結(jié)果,左邊是原始模板的代碼糠赦,右邊是替換之后的PHP語(yǔ)法代碼
else if 語(yǔ)句處理
else if語(yǔ)句匹配規(guī)則的正則表達(dá)式
/* 3会傲、匹配elseif標(biāo)識(shí)符, 例如 "<{ elseif $col == "sex" }>" */
'/' . $left . '\s*else\s*if\s*(.+?)\s*' . $right . '/is',
else if語(yǔ)句匹配規(guī)則的處理方法,該方法作為preg_replace_callback
正則替換的回調(diào)方法
/* 3拙泽、E 替換elseif的字符串 "<{ elseif $col == "sex" }>" ==> <?php } elseif($col == "sex") { ?> */
function ($matches) use ($parent) {
$result = $parent->stripvtags('<?php } elseif(' . $matches[1] . ') { ?>', "");
return $result;
}
替換的結(jié)果如下
// 原始的模板代碼
<{ elseif $colValue=="女" }>
// 轉(zhuǎn)換后的php代碼
<?php } elseif($this->tpl_vars["colValue"]=="女") { ?>
模板文件的處理結(jié)果淌山,左邊是原始模板的代碼,右邊是替換之后的PHP語(yǔ)法代碼
else 語(yǔ)句處理
else 語(yǔ)句匹配規(guī)則的正則表達(dá)式
/* 4顾瞻、匹配else標(biāo)識(shí)符, 例如 "<{ else }>" */
'/' . $left . '\s*else\s*' . $right . '/is',
else 語(yǔ)句匹配規(guī)則的處理方法泼疑,該方法作為preg_replace_callback
正則替換的回調(diào)方法
/* 4、替換else的字符串 "<{ else }>" ==> <?php } else { ?> */
function ($matches) use ($parent) {
$result = '<?php } else { ?>';
return $result;
}
替換的結(jié)果如下
// 原始的模板代碼
<{ else }>
// 轉(zhuǎn)換后的php代碼
<?php } else { ?>
模板文件的處理結(jié)果荷荤,左邊是原始模板的代碼退渗,右邊是替換之后的PHP語(yǔ)法代碼
loop 語(yǔ)句處理
loop 語(yǔ)句匹配規(guī)則的正則表達(dá)式,loop語(yǔ)句也就是PHP中的foreach語(yǔ)句蕴纳,有兩種語(yǔ)法格式
/* 5会油、用來(lái)匹配模板中的loop標(biāo)識(shí)符,用來(lái)遍歷數(shù)組中的值, 例如 "<{ loop $arrs $value }> <{ /loop}>" */
'/' . $left . '\s*loop\s+\$(\S+)\s+\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*' . $right . '(.+?)' . $left . '\s*\/loop\s*' . $right . '/is',
/* 6古毛、用來(lái)遍歷數(shù)組中的鍵和值,例如 "<{ loop $arrs $key => $value }> <{ /loop}>" */
'/' . $left . '\s*loop\s+\$(\S+)\s+\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=>\s*\$(\S+)\s*' . $right . '(.+?)' . $left . '\s*\/loop \s*' . $right . '/is',
loop 語(yǔ)句匹配規(guī)則的處理方法翻翩,遍歷元素的key
/Value
值是以模板對(duì)象中數(shù)組屬性tpl_vars
中的某個(gè)值的方式進(jìn)行保存的,轉(zhuǎn)換規(guī)則如下表所示
模板代碼 | PHP語(yǔ)法 |
---|---|
$colKey | $this->tpl_vars["colKey"] |
$colKey | $this->tpl_vars["colKey"] |
$colValue | $this->tpl_vars["colValue"] |
/* 5稻薇、以下兩條用來(lái)替換模板中的loop標(biāo)識(shí)符為foreach格式 "<{ loop $arrs $value }> statement <{ /loop}>"
==> <?php foreach($this->tpl_vars["users"] as $this->tpl_vars["user"]) { ?> statement <?php } ?>*/
function ($matches) use ($parent) {
$result = '<?php foreach($this->tpl_vars["' . $matches[1] . '"] as $this->tpl_vars["' . $matches[2] . '"]) { ?>' . $matches[3] . '<?php } ?>';
return $result;
}
/* 6嫂冻、 "<{ loop $arrs $key => $value }> statement <{ /loop}>"
==> <?php foreach($this->tpl_vars["user"] as $this->tpl_vars["colKey"] => $this->tpl_vars["colValue"]) { ?> statement <?php } ?>*/
function ($matches) use ($parent) {
$result = '<?php foreach($this->tpl_vars["' . $matches[1] . '"] as $this->tpl_vars["' . $matches[2] . '"] => $this->tpl_vars["' . $matches[3] . '"]) { ?>' . $matches[4] . '<?php } ?>';
return $result;
}
替換的結(jié)果如下
// 原始的模板代碼
<{ loop $users $user }>
<tr>
<{ loop $user $colKey => $colValue }>
<?php if($this->tpl_vars["colKey"]== "sex") { ?>
<{ if $colValue=="男" }>
<td bgColor="red"> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } elseif($this->tpl_vars["colValue"]=="女") { ?>
<td bgColor="green"> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } else { ?>
<td bgColor="blue"> 未知 </td>
<{ /if }>
<?php } else { ?>
<td> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } ?>
<{ /loop }>
</tr>
<{ /loop }>
// 轉(zhuǎn)換后的php代碼
<?php foreach($this->tpl_vars["users"] as $this->tpl_vars["user"]) { ?>
<tr>
<?php foreach($this->tpl_vars["user"] as $this->tpl_vars["colKey"] => $this->tpl_vars["colValue"]) { ?>
<?php if($this->tpl_vars["colKey"]== "sex") { ?>
<{ if $colValue=="男" }>
<td bgColor="red"> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } elseif($this->tpl_vars["colValue"]=="女") { ?>
<td bgColor="green"> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } else { ?>
<td bgColor="blue"> 未知 </td>
<{ /if }>
<?php } else { ?>
<td> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } ?>
<?php } ?>
</tr>
<?php } ?>
模板文件的處理結(jié)果,左邊是原始模板的代碼颖低,右邊是替換之后的PHP語(yǔ)法代碼
- foreach 第一種語(yǔ)法格式絮吵,只有值的遍歷
- foreach 第二種語(yǔ)法格式,帶有鍵值對(duì)的遍歷
include 語(yǔ)句處理
include 語(yǔ)句匹配規(guī)則的正則表達(dá)式
/* 7忱屑、匹配include標(biāo)識(shí)符, 例如蹬敲,'<{ include "header.html" }>' */
'/' . $left . '\s*include\s+[\"\']?(.+?)[\"\']?\s*' . $right . '/i'
include 語(yǔ)句匹配規(guī)則的處理方法,該方法作為preg_replace_callback
正則替換的回調(diào)方法
/* 7莺戒、E 替換include的字符串*/
function ($matches) use ($parent) {
$result = file_get_contents($this->template_dir . "/" . $matches[1]);
return $result;
}
替換的結(jié)果如下
// 原始的模板代碼
<{ include "header.html" }>
// 轉(zhuǎn)換后的PHP代碼
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title> <{ $title }> </title>
</head>
<body>
// 原始的模板代碼
<{ include 'footer.html' }>
// 轉(zhuǎn)換后的PHP代碼
<hr><center> ############### 作者:<{ $author }> ############## </center>
</body>
</html>
模板文件的處理結(jié)果伴嗡,左邊是原始模板的代碼,右邊是替換之后的PHP語(yǔ)法代碼
最終的結(jié)果
上面的步驟之后从铲,通過(guò)正則匹配模板語(yǔ)法的定界符來(lái)做的判斷瘪校,如果還有存在模板語(yǔ)法,需要進(jìn)行遞歸處理,把所有的模板語(yǔ)法代碼轉(zhuǎn)換為PHP語(yǔ)法代碼
/* 使用正則替換函數(shù)處理 */
$repContent = $this->replaceContent($pattern, $replacementFunctions, $content);
/* 如果還有要替換的標(biāo)識(shí),遞歸調(diào)用自己再次替換 */
if (preg_match('/' . $left . '([^(' . $right . ')]{1,})' . $right . '/', $repContent)) {
$repContent = $this->tpl_replace($repContent);
}
以下是兩份最終的代碼的比較
原始的模板代碼
// header.html
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title> <{ $title }> </title>
</head>
<body>
// footer.html
<hr><center> ############### 作者:<{ $author }> ############## </center>
</body>
</html>
// main.html
<{ include "header.html" }>
<table border="1" align="center" width="90%" cellpadding="3" cellspacing="0">
<caption><h1> <{ $tableName }> <h1></caption>
<tr bgcolor="#cccccc">
<th>編號(hào)</th><th>姓名</th><th>性別</th><th>年齡</th><th>電子郵件</th>
</tr>
<{ loop $users $user }>
<tr>
<{ loop $user $colKey => $colValue }>
<{ if $colKey == "sex" }>
<{ if $colValue=="男" }>
<td bgColor="red"> <{ $colValue }> </td>
<{ elseif $colValue=="女" }>
<td bgColor="green"> <{ $colValue }> </td>
<{ else }>
<td bgColor="blue"> 未知 </td>
<{ /if }>
<{ else }>
<td> <{ $colValue }> </td>
<{ /if }>
<{ /loop }>
</tr>
<{ /loop }>
</table>
<center>共查找到<b> <{ $rowNum }> </b>條記錄</center>
<{ include 'footer.html' }>
替換之后的PHP代碼
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title> <?php echo $this->tpl_vars["title"]; ?> </title>
</head>
<body>
<table border="1" align="center" width="90%" cellpadding="3" cellspacing="0">
<caption><h1> <?php echo $this->tpl_vars["tableName"]; ?> <h1></caption>
<tr bgcolor="#cccccc">
<th>編號(hào)</th><th>姓名</th><th>性別</th><th>年齡</th><th>電子郵件</th>
</tr>
<?php foreach($this->tpl_vars["users"] as $this->tpl_vars["user"]) { ?>
<tr>
<?php foreach($this->tpl_vars["user"] as $this->tpl_vars["colKey"] => $this->tpl_vars["colValue"]) { ?>
<?php if($this->tpl_vars["colKey"]== "sex") { ?>
<?php if($this->tpl_vars["colValue"]=="男") { ?>
<td bgColor="red"> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } elseif($this->tpl_vars["colValue"]=="女") { ?>
<td bgColor="green"> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } else { ?>
<td bgColor="blue"> 未知 </td>
<?php } ?>
<?php } else { ?>
<td> <?php echo $this->tpl_vars["colValue"]; ?> </td>
<?php } ?>
<?php } ?>
</tr>
<?php } ?>
</table>
<center>共查找到<b> <?php echo $this->tpl_vars["rowNum"]; ?> </b>條記錄</center>
<hr><center> ############### 作者:<?php echo $this->tpl_vars["author"]; ?> ############## </center>
</body>
</html>
保存模板編譯結(jié)果
模板編譯是一個(gè)耗資源的操作阱扬,所以合適的處理方式是對(duì)結(jié)果進(jìn)行緩存泣懊,因?yàn)槭褂玫骄彺妫陨婕暗骄彺娴母绿鎿Q等問(wèn)題麻惶,這里的處理方式比較簡(jiǎn)單也就是比較模板編譯文件和主入口的模板文件的時(shí)間馍刮,如果編譯文件的時(shí)間小于主入口模板文件的時(shí)間,也就意味著模板文件存在更新窃蹋,需要更新編譯文件卡啰。然后使用include()
方法把模板文件的內(nèi)容輸出。相關(guān)的代碼如下
/* 獲取組合的模板文件警没,該文件中的內(nèi)容都是被替換過(guò)的 */
$comFileName = $this->compile_dir . "/com_" . $fileName . '.php';
/* 判斷替換后的文件是否存在或是存在但有改動(dòng)匈辱,都需要重新創(chuàng)建 */
if (!file_exists($comFileName) || filemtime($comFileName) < filemtime($tplFile)) {
if (DEBUG_CONTENT_CHANGE) {
$fileName = "./test/result.txt";
FileUtil::createFile($fileName);
file_put_contents($fileName, "");
}
/* 調(diào)用內(nèi)部替換模板方法 */
$repContent = $this->tpl_replace(file_get_contents($tplFile));
/* 保存由系統(tǒng)組合后的腳本文件 */
FileUtil::createFile($comFileName);
file_put_contents($comFileName, $repContent);
}
/* 包含處理后的模板文件輸出給客戶端 */
include($comFileName);
模板使用
使用模板有三個(gè)步驟:
- 1、指定使用的模板
- 2杀迹、給模板使用的數(shù)據(jù)賦值
- 3亡脸、讓模板引擎處理展示邏輯
對(duì)應(yīng)的使用的代碼如下:
$tpl=new MyTpl; //創(chuàng)建模板引擎類對(duì)象
$tpl->assign("title", "自定義模板引擎示例"); //分配標(biāo)題變量給頭部模板header.tpl
$tpl->assign("tableName", "用戶信息表"); //分配表名變量給主模板
$tpl->assign("author", "高洛峰"); //分配作者變量給尾部模板footer.tpl
$tpl->assign("users", $users); //分配存有表User的二維數(shù)組給主模板
$tpl->assign("rowNum", $stmt->rowCount()); //分配所取的數(shù)據(jù)行數(shù)變量給主模板
$tpl->display("main.html"); //包括替換模板中的變量輸出模板頁(yè)面