簡(jiǎn)單的自定義php模板引擎
模板引擎的思想是來(lái)源于MVC(Model View Controller)模型,即模型層、視圖層、控制器層。
在Web端,模型層為數(shù)據(jù)庫(kù)的操作;視圖層就是模板,也就是Web前端;Controller就是PHP對(duì)數(shù)據(jù)和請(qǐng)求的各種操作。模板引擎就是為了將視圖層和其他層分離開(kāi)來(lái),使php代碼和html代碼不會(huì)混雜在一起。因?yàn)楫?dāng)php代碼和html代碼混雜在一起時(shí),將使代碼的可讀性變差,并且代碼后期的維護(hù)會(huì)變得很困難。
大部分的模板引擎原理都差不多,核心就是利用正則表達(dá)式解析模板,將約定好的特定的標(biāo)識(shí)語(yǔ)句編譯成php語(yǔ)句,然后調(diào)用時(shí)只需要include編譯后的文件,這樣就講php語(yǔ)句和html語(yǔ)句分離開(kāi)來(lái)了。甚至可以更進(jìn)一步將php的輸出輸出到緩沖區(qū),然后將模板編譯成靜態(tài)的html文件,這樣請(qǐng)求時(shí),就是直接打開(kāi)靜態(tài)的html文件,請(qǐng)求速度大大加快。
簡(jiǎn)單的自定義模板引擎就是兩個(gè)類,第一個(gè)是模板類、第二個(gè)是編譯類。
首先是編譯類:
class CompileClass {
private $template; // 待編譯文件
private $content; // 需要替換的文本
private $compile_file; // 編譯后的文件
private $left = '{'; // 左定界符
private $right = '}'; // 右定界符
private $include_file = array(); // 引入的文件
private $config; // 模板的配置文件
private $T_P = array(); // 需要替換的表達(dá)式
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() {}
// 處理各種賦值和基本語(yǔ)句
public function c_var() {}
// 對(duì)靜態(tài)的JavaScript進(jìn)行解析
public function c_staticFile() {}
}
編譯類的大致結(jié)構(gòu)就是上面那樣,編譯類的工作就是根據(jù)配置的文件,將寫(xiě)好的模板文件按照規(guī)則解析,替換然后輸出到文件中。這個(gè)文件的內(nèi)容是php和html混雜的,但在使用模板引擎進(jìn)行開(kāi)發(fā)時(shí)并不需要在意這個(gè)文件,因?yàn)槲覀円帉?xiě)的是模板文件,也就是html和我們自己定義的標(biāo)簽混合的一個(gè)文件。這樣View和其他兩層就分離開(kāi)來(lái)了。
在這個(gè)自定義模板引擎中,我的左右定界符就是大括號(hào),具體的解析規(guī)則就是放在__construct()中
// 需要替換的正則表達(dá)式
$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; ?>";
上面的解析規(guī)則包含了基本的輸出和一些常用的語(yǔ)法,if、foreach等。利用preg_replace函數(shù)就能對(duì)模板文件進(jìn)行替換。具體情況如下
<!--模板文件-->
{$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和對(duì)JavaScript的解析都和這個(gè)大同小異。
然后就是模板類
class Template {
// 配置數(shù)組
private $_arrayConfig = array(
'root' => '', // 文件根目錄
'suffix' => '.html', // 模板文件后綴
'template_dir' => 'templates', // 模板所在文件夾
'compile_dir' => 'templates_c', // 編譯后存放的文件夾
'cache_dir' => 'cache', // 靜態(tài)html存放地址
'cache_htm' => false, // 是否編譯為靜態(tài)html文件
'suffix_cache' => '.htm', // 設(shè)置編譯文件的后綴
'cache_time' => 7200, // 自動(dòng)更新間隔
'php_turn' => true, // 是否支持原生php代碼
'debug' => 'false',
);
private $_value = array();
private $_compileTool; // 編譯器
static private $_instance = null;
public $file; // 模板文件名
public $debug = array(); // 調(diào)試信息
public function __construct($array_config=array()) {}
// 單步設(shè)置配置文件
public function setConfig($key, $value=null) {}
// 注入單個(gè)變量
public function assign($key, $value) {}
// 注入數(shù)組變量
public function assignArray($array) {}
// 是否開(kāi)啟緩存
public function needCache() {}
// 如果需要重新編譯文件
public function reCache() {}
// 顯示模板
public function show($file) {}
}
整個(gè)模板類的工作流程就是先實(shí)例化模板類對(duì)象,然后利用assign和assignArray方法給模板中的變量賦值,然后調(diào)用show方法,將模板和配置文件傳入編譯類的實(shí)例化對(duì)象中然后直接include編譯后的php、html混編文件,顯示輸出。簡(jiǎn)單的流程就是這樣,詳細(xì)的代碼如下
public function show($file) {
$this->file = $file;
if(!is_file($this->path())) {
exit("找不到對(duì)應(yīng)的模板文件");
}
$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()) {
// 輸出到緩沖區(qū)
ob_start();
}
// 將賦值的變量導(dǎo)入當(dāng)前符號(hào)表
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);
}
// 如果需要編譯成靜態(tài)文件
if($this->needCache() === true) {
$message = ob_get_contents();
file_put_contents($cache_file, $message);
}
}
else {
readfile($cache_file);
}
}
在show方法中,我首先判斷模板文件存在,然后利用MD5編碼生成編譯文件和緩存文件的文件名。然后就是判斷是否需要進(jìn)行編譯,判斷的依據(jù)是看編譯文件是否存在和編譯文件的寫(xiě)入時(shí)間是否小于模板文件。如果需要編譯,就利用編譯類進(jìn)行編譯,生成一個(gè)php文件。然后只需要include這個(gè)編譯文件就好了。
為了加快模板的載入,可以將編譯后的文件輸出到緩沖區(qū)中,也就是ob_start()這個(gè)函數(shù),所有的輸出將不會(huì)輸出到瀏覽器,而是輸出到默認(rèn)的緩沖區(qū),在利用ob_get_contents()將輸出讀取出來(lái),保存成靜態(tài)的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>
一個(gè)簡(jiǎn)單的自定義模板引擎就完成了,雖然簡(jiǎn)陋但是能用,而且重點(diǎn)在于造輪子的樂(lè)趣和收獲。
完整代碼可見(jiàn)我的 github
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
php文字水印和php圖片水印實(shí)現(xiàn)代碼(二種加水印方法)
有時(shí)上傳圖片時(shí)需要給網(wǎng)站加上水印,水印可以分為文字水印和圖片水印,下面就實(shí)現(xiàn)這二種水印2013-12-12
PHP getallheaders無(wú)法獲取自定義頭(headers)的問(wèn)題
這篇文章主要介紹了PHP getallheaders無(wú)法獲取自定義頭(headers)的問(wèn)題的相關(guān)資料,需要的朋友可以參考下2016-03-03
PHP實(shí)現(xiàn)財(cái)務(wù)審核通過(guò)后返現(xiàn)金額到客戶的功能
有這么一個(gè)返現(xiàn)的系統(tǒng),當(dāng)前端客戶發(fā)起提現(xiàn)的時(shí)候,后端就要通過(guò)審核這筆返現(xiàn)訂單,才可以返現(xiàn)到客戶的賬號(hào)里。這篇文章主要介紹了PHP實(shí)現(xiàn)財(cái)務(wù)審核通過(guò)后返現(xiàn)金額到客戶 ,需要的朋友可以參考下2019-07-07
記錄Yii2框架開(kāi)發(fā)微信公眾號(hào)遇到的問(wèn)題及解決方法
微信公眾號(hào)開(kāi)發(fā),提示“該公眾號(hào)暫時(shí)無(wú)法提供服務(wù),請(qǐng)稍后再試”,如何解決?下面小編給大家?guī)?lái)了解決方法,一起看看吧2018-07-07
php兩點(diǎn)地理坐標(biāo)距離的計(jì)算方法
這篇文章主要為大家詳細(xì)介紹了php兩點(diǎn)地理坐標(biāo)距離的計(jì)算方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
php后臺(tái)多用戶權(quán)限組思路與實(shí)現(xiàn)程序代碼分享
很多時(shí)候我們?cè)匍_(kāi)發(fā)過(guò)程中需要考慮到多用戶權(quán)限問(wèn)題,這篇文章大家可以參考下2012-02-02
php中實(shí)現(xiàn)用數(shù)組嫵媚地生成要執(zhí)行的sql語(yǔ)句
這篇文章主要介紹了php中實(shí)現(xiàn)用數(shù)組嫵媚地生成要執(zhí)行的sql語(yǔ)句,本文直接給出代碼示例,需要的朋友可以參考下2015-07-07

