溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

ThinkPHP中模板引擎是什么

發(fā)布時(shí)間:2021-01-16 10:06:40 來(lái)源:億速云 閱讀:321 作者:小新 欄目:編程語(yǔ)言

這篇文章主要介紹ThinkPHP中模板引擎是什么,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

模板引擎由來(lái)

早期做PHP開(kāi)發(fā)WEB應(yīng)用都是把PHP代碼和HTML模板混在一起,模板引擎的誕生主要就是為了解決后端與前端的完全分離(現(xiàn)在來(lái)看其實(shí)是屬于不完全分離)的問(wèn)題,讓開(kāi)發(fā)與美工可以分工合作(雖然實(shí)際上最終模板工作大多仍然是由后端開(kāi)發(fā)人員完成),從而提高開(kāi)發(fā)效率和便于維護(hù)。

伴隨著PHP的快速成長(zhǎng),模板引擎也越來(lái)越多,但大致分為解釋型和編譯型兩種,目前主流的模板引擎大多數(shù)是編譯型的,也就是會(huì)先把模板編譯成PHP文件執(zhí)行,只要模板文件本身不變化,就不需要重新編譯,例如老牌的Smarty。解釋型的模板引擎每次執(zhí)行的時(shí)候都會(huì)進(jìn)行模板解析流程,例如小強(qiáng)(tinybutstrong)。

ThinkPHP從一開(kāi)始就內(nèi)置了一個(gè)基于XML標(biāo)簽庫(kù)技術(shù)的編譯型模板引擎,早期參考自Struts,并且不斷在汲取新的思想不斷進(jìn)化。

如何選擇模板引擎

目前主流框架都帶有模板引擎組件或者封裝了模板引擎的實(shí)現(xiàn),因此選擇內(nèi)置的解決方案是最佳之選,功能和穩(wěn)定性都有保證。目前最流行的模板引擎當(dāng)屬Laravel自帶的Blade模板引擎,以及Symfony自帶的Twig模板引擎。

通過(guò)安裝模板引擎擴(kuò)展,你可以在ThinkPHP中輕松使用包括Angular、Twig和Blade在內(nèi)的模板引擎,甚至完全不使用模板引擎而是直接用PHP文件作為模板。

因?yàn)榻鼛啄耆笄岸丝蚣埽≧eact/Vue/Angular)的流行,前后端分離開(kāi)發(fā)逐漸成為主流,因此從ThinkPHP5.0開(kāi)始定位為API開(kāi)發(fā)而設(shè)計(jì),導(dǎo)致模板引擎的概念已經(jīng)被弱化了。ThinkPHP5.1版本的模板引擎進(jìn)行過(guò)一次內(nèi)部的重構(gòu),使得模板標(biāo)簽更加易用和接近PHP語(yǔ)法。

至少對(duì)于大部分新的應(yīng)用來(lái)說(shuō),應(yīng)該選擇更主流的前后端分離設(shè)計(jì),盡量減輕服務(wù)端的壓力,也更方便前后端單獨(dú)測(cè)試。你會(huì)在市面上不經(jīng)意的看到采用Vue和ThinkPHP的產(chǎn)品(之前幾期的ThinkPHP開(kāi)發(fā)者周刊曾經(jīng)報(bào)道過(guò)幾個(gè))。如果是維護(hù)一些老項(xiàng)目尤其是內(nèi)容管理產(chǎn)品的時(shí)候,仍然可能會(huì)用到模板引擎。

鑒于這種情況,下一個(gè)版本的ThinkPHP框架將不會(huì)內(nèi)置模板引擎,但有需要使用模板引擎的開(kāi)發(fā)者仍然可以使用官方獨(dú)立出來(lái)的think-template類(lèi)庫(kù),具體使用可以參考這篇文章。

后面的篇幅,我們主要來(lái)總結(jié)下ThinkPHP內(nèi)置的模板引擎的使用和技巧。

模板執(zhí)行流程

系統(tǒng)內(nèi)部的模板引擎調(diào)用關(guān)系如下:

視圖(View) <=> 模板驅(qū)動(dòng)(Driver) <=> 模板引擎(Template)

視圖和模板引擎之間增加了一個(gè)驅(qū)動(dòng)層,所以可以很方便的替換其它的模板引擎。通常我們?cè)诳刂破髦姓{(diào)用的assign/fetch等方法其實(shí)都是調(diào)用的think\View類(lèi)的方法。當(dāng)然,如果有必要,你也完全可以直接在控制器中操作模板引擎類(lèi),只是不方便切換其它模板引擎。

以fetch方法為例,我們看下最終的調(diào)用過(guò)程:

think\Controller->fetch();
think\View->fetch();
think\view\driver\Think->fetch();
think\Template->fetch();

如果你調(diào)用fetch方法的時(shí)候沒(méi)有傳入要渲染的完整模板文件名,則會(huì)在第三步的時(shí)候自動(dòng)識(shí)別要渲染的模板文件。

很顯然,最關(guān)鍵是最后一步,模板編譯和執(zhí)行的流程則全部由

think\Template->fetch();

方法完成,這個(gè)環(huán)節(jié)大體又可以分成幾個(gè)流程。

1、判斷和讀取頁(yè)面渲染緩存

如果當(dāng)前模板設(shè)置了頁(yè)面輸出緩存并且已經(jīng)渲染輸出過(guò),如果是則會(huì)讀取緩存中的輸出內(nèi)容直接輸出。

if (!empty($this->config['cache_id']) && $this->config['display_cache']) {
    // 讀取渲染緩存
    $cacheContent = $cache->get($this->config['cache_id']);
    if (false !== $cacheContent) {
        echo $cacheContent;
        return;
    }
}

2、定位模板文件

定位實(shí)際的模板文件操作由模板引擎類(lèi)的parseTemplateFile方法實(shí)現(xiàn),這個(gè)方法的邏輯其實(shí)和視圖驅(qū)動(dòng)類(lèi)的parseTemplate方法是類(lèi)似的,如果最終的模板文件不存在則會(huì)拋出一個(gè)模板文件不存在的異常。

$template = $this->parseTemplateFile($template);

3、判斷編譯緩存

如果當(dāng)前的模板文件已經(jīng)編譯過(guò),會(huì)判斷緩存是否還有效,有效的話(huà)就不用重復(fù)解析直接讀取緩存的解析內(nèi)容。由checkCache方法負(fù)責(zé)完成。

if (!$this->checkCache($cacheFile)) {
    // 緩存無(wú)效 重新模板編譯
    $content = file_get_contents($template);
    $this->compiler($content, $cacheFile);
}

4、模板編譯并緩存

這一步驟是模板引擎最核心的環(huán)節(jié),也是功能最復(fù)雜的地方,由compiler方法負(fù)責(zé)完成,主要是解析當(dāng)前模板文件中的模板標(biāo)簽語(yǔ)法為PHP可執(zhí)行代碼,然后生成一個(gè)模板解析緩存文件,也就是所謂的模板“編譯”,其中使用了大量的正則表達(dá)式替換技術(shù),雖然正則解析有一定的性能開(kāi)銷(xiāo),但得益于一次解析多次調(diào)用的緩存原理,基本上模板解析的性能開(kāi)銷(xiāo)不會(huì)影響實(shí)際使用的性能。

模板編譯方法的關(guān)鍵代碼是parse方法,parse方法負(fù)責(zé)對(duì)模板文件中的標(biāo)簽進(jìn)行解析,然后寫(xiě)入編譯緩存文件,編譯緩存默認(rèn)使用的是文件緩存,支持?jǐn)U展。

5、讀取編譯緩存

模板編譯的過(guò)程只是生成了模板編譯緩存文件,并沒(méi)有真正載入模板,這一步驟就是載入模板編譯緩存,然后導(dǎo)入模板變量。實(shí)現(xiàn)方法可以參考think\template\driver\File類(lèi)的read方法。

public function read($cacheFile, $vars = [])
{
    $this->cacheFile = $cacheFile;
    if (!empty($vars) && is_array($vars)) {
        // 模板陣列變量分解成為獨(dú)立變量
        extract($vars, EXTR_OVERWRITE);
    }
    //載入模版緩存文件
    include $this->cacheFile;
}

6、緩存頁(yè)面輸出

如果當(dāng)前模板渲染的時(shí)候開(kāi)啟了頁(yè)面輸出緩存,就會(huì)這一步生成頁(yè)面渲染后的輸出緩存。

模板編譯原理

我們來(lái)了解下ThinkPHP的模板引擎的實(shí)現(xiàn)原理。前面提到過(guò),ThinkPHP的模板引擎最早源于Struts的設(shè)計(jì)理念,基于XML和標(biāo)簽庫(kù)的技術(shù)實(shí)現(xiàn)。在設(shè)計(jì)模板語(yǔ)言的時(shí)候使用系統(tǒng)固定的標(biāo)簽來(lái)實(shí)現(xiàn)普通的變量輸出功能(所以稱(chēng)之為普通標(biāo)簽),而利用XML標(biāo)簽庫(kù)技術(shù)實(shí)現(xiàn)的動(dòng)態(tài)標(biāo)簽用于變量的控制或者條件判斷輸出。

普通標(biāo)簽的解析是由think\Template類(lèi)的parseTag方法完成的,主要實(shí)現(xiàn)了下面幾個(gè)模板功能:

·變量輸出(包括系統(tǒng)變量);

·函數(shù)過(guò)濾;

·變量運(yùn)算;

·三元運(yùn)算;

·執(zhí)行函數(shù)以及輸出結(jié)果;

·模板注釋。

標(biāo)簽庫(kù)采用的是動(dòng)態(tài)擴(kuò)展的設(shè)計(jì)方案,采用了類(lèi)似XML的閉合/開(kāi)放定義方式(這個(gè)其實(shí)也是目前模板引擎的一個(gè)局限所在),例如下面的這個(gè):

// 閉合類(lèi)型標(biāo)簽
<tagLib:tagName name="value" >
...
</tagLib:tagName>
// 開(kāi)放類(lèi)型標(biāo)簽
<tagLib:tagName name="value" />

tagLib就代表了一個(gè)標(biāo)簽庫(kù)(類(lèi)),后面的tagName標(biāo)簽就表示該標(biāo)簽庫(kù)下面的某個(gè)標(biāo)簽(通常對(duì)應(yīng)了標(biāo)簽庫(kù)類(lèi)的某個(gè)方法),后面的屬性就是該標(biāo)簽支持的屬性定義。具體該標(biāo)簽的屬性和功能則完全由標(biāo)簽庫(kù)類(lèi)的這個(gè)方法來(lái)決定。

可以在模板開(kāi)頭明確指出,當(dāng)前模板使用了哪些標(biāo)簽庫(kù)

{taglib name="html,article" /}

所以要擴(kuò)展模板引擎的功能只需要通過(guò)擴(kuò)展一個(gè)標(biāo)簽庫(kù)類(lèi)就可以了。大多數(shù)的內(nèi)容管理系統(tǒng)都會(huì)定義一套自己的模板二次開(kāi)發(fā)標(biāo)簽,利用標(biāo)簽庫(kù)功能就可以很方便的定義一套屬于自己的標(biāo)簽功能。

系統(tǒng)內(nèi)置了一套標(biāo)簽庫(kù)Cx,主要用于文件包含、條件控制、循環(huán)輸出等功能。內(nèi)置標(biāo)簽庫(kù)在使用的時(shí)候無(wú)需引入,而且在使用的時(shí)候可以省略標(biāo)簽庫(kù)前綴,例如:

{foreach $list as $key=>$vo } 
    {$vo.id}:{$vo.name}
{/foreach}

這個(gè)模板語(yǔ)法相信PHP開(kāi)發(fā)的很容易上手,上面的標(biāo)簽解析由think\template\taglib\Cx類(lèi)的tagForeach方法完成,該方法的返回值是一個(gè)字符串,其實(shí)就是最終會(huì)解析成的一段包含變量的PHP可執(zhí)行代碼。

到這里,模板引擎的執(zhí)行過(guò)程和原理現(xiàn)在基本就明白了,剩下的就是模板標(biāo)簽的解析細(xì)節(jié),考驗(yàn)的就是正則表達(dá)式的掌握程度了。本文就不做深入了,有興趣的朋友可以去看一些正則表達(dá)式的相關(guān)資料(例如這本《正則指引》,開(kāi)發(fā)者周刊第14期也提供了一些在線(xiàn)的正則工具)。

遵循的原則

使用模板引擎,要盡量遵循幾個(gè)重要的原則。

不要在模板文件中添加任何的業(yè)務(wù)邏輯

模板的作用主要是進(jìn)行模板變量的控制和輸出,不要在模板文件中添加業(yè)務(wù)邏輯代碼。

明確指定渲染模板

養(yǎng)成明確指定渲染模板的好習(xí)慣,避免當(dāng)方法名發(fā)生變化,或者被其它方法調(diào)用的時(shí)候發(fā)生錯(cuò)誤。也不易受模板命名規(guī)范的影響。

變量統(tǒng)一賦值

使用assign方法或者在view助手函數(shù)的時(shí)候,統(tǒng)一一次傳入模板變量。不要多次賦值,以免混亂。

系統(tǒng)變量無(wú)需賦值到模板

對(duì)于系統(tǒng)變量(包括請(qǐng)求變量、$_SESSION和$_SERVER等系統(tǒng)變量)無(wú)需進(jìn)行模板變量賦值,可以直接在模板中輸出。

常見(jiàn)問(wèn)題

這里總結(jié)一下經(jīng)常會(huì)遇到的一些常見(jiàn)問(wèn)題。

修改定界符

可以通過(guò)模板配置文件修改模板標(biāo)簽的定界符。

例如,修改普通標(biāo)簽定界符

'tpl_begin' => '{{', // 模板引擎普通標(biāo)簽開(kāi)始標(biāo)記
'tpl_end' => '}}', // 模板引擎普通標(biāo)簽結(jié)束標(biāo)記

標(biāo)簽庫(kù)標(biāo)簽定界符

'taglib_begin' => '<{', // 標(biāo)簽庫(kù)標(biāo)簽開(kāi)始標(biāo)記
'taglib_end' => '}>', // 標(biāo)簽庫(kù)標(biāo)簽結(jié)束標(biāo)記

保持原樣輸出

如果擔(dān)心模板標(biāo)簽和JS代碼產(chǎn)生混淆,可以使用literal標(biāo)簽

{literal} Hello,{$name}! {/literal}

頁(yè)面最終會(huì)直接輸出

Hello,{$name}!

避免輸出轉(zhuǎn)義

5.1版本為了避免XSS攻擊,默認(rèn)對(duì)模板變量的輸出使用了安全轉(zhuǎn)義,默認(rèn)的轉(zhuǎn)義函數(shù)是htmlentities,你可以通過(guò)更改default_filter配置改變默認(rèn)的轉(zhuǎn)義函數(shù)。

如果你不需要對(duì)某個(gè)模板變量輸出進(jìn)行轉(zhuǎn)義(例如包含了HTML代碼),可以使用:

{$data.content|raw}

分頁(yè)輸出就是一個(gè)需要輸出HTML的典型例子,因此必須增加|raw。

關(guān)于模板主題

新版取消了原來(lái)的模板主題功能,因?yàn)槟0逯黝}對(duì)模板引擎來(lái)說(shuō),其實(shí)無(wú)非是一個(gè)模板目錄,完全可以根據(jù)自己的需求控制。

例如

$theme = 'blue';
$this->fetch('/' . $theme. '/user/index');

或者動(dòng)態(tài)設(shè)置模板引擎的view_path參數(shù)

$this->view->config('view_path', \think\facade\App::getModulePath(). 'view/'. $theme . '/');

如何關(guān)閉模板緩存

由于是編譯型模板引擎,模板標(biāo)簽不能被直接執(zhí)行,必須編譯成PHP語(yǔ)法后才能執(zhí)行,因此不能關(guān)閉模板編譯緩存,模板引擎每次執(zhí)行渲染的時(shí)候會(huì)檢測(cè)模板文件是否有變化,當(dāng)模板文件的修改時(shí)間超過(guò)模板編譯緩存的修改時(shí)間后,模板引擎會(huì)自動(dòng)更新編譯緩存。

但你可以強(qiáng)制模板引擎每次都重新編譯,只需要在配置文件中設(shè)置

'tpl_cache' => false, // 關(guān)閉模板緩存

使用PHP作為模板引擎

如果不希望使用內(nèi)置的模板引擎,直接使用PHP作為模板引擎,可以配置

'type' => 'php',

配置使用PHP作為模板引擎的話(huà),是不會(huì)生成模板編譯緩存的。

如何使用第三方模板引擎

系統(tǒng)支持?jǐn)U展其它的第三方模板引擎,你只需要開(kāi)發(fā)一個(gè)模板引擎驅(qū)動(dòng),目前已經(jīng)支持的第三方模板引擎包括Smarty、Twig和Blade。

如何跨模塊輸出模板

要渲染一個(gè)跨模塊的模板文件,你需要使用

// 渲染user模塊的模板文件
$this->fetch('User@order/index');

是否支持變量運(yùn)算

可以直接在模板文件中進(jìn)行變量運(yùn)算而不需要在控制器中進(jìn)行運(yùn)算后再賦值都模板變量輸出。

{$score1+$score2}
{$count++}

文件包含是否支持變量

include標(biāo)簽可以支持傳入變量,但只能使用

{include file="$file" /}

而不能使用

{include file="file_$name" /}

可以支持模板輸出替換么

支持兩個(gè)方式對(duì)模板進(jìn)行輸出替換,如果需要對(duì)模板文件的內(nèi)容進(jìn)行替換,可以配置:

'tpl_replace_string'  =>  [
    '__STATIC__'=>'/static',
'__JS__' => '/static/javascript',
]

如果是對(duì)模板渲染輸出的內(nèi)容進(jìn)行替換,可以在控制器中使用視圖過(guò)濾功能:

public function index()
{
    // 使用視圖輸出過(guò)濾
    return $this->filter(function($content){
    return str_replace("\r\n",'<br/>',$content);
    })->fetch();
}

模板繼承的block是否支持嵌套

目前模板繼承的block無(wú)法支持嵌套功能,你應(yīng)該使用其它方式解決。

以上是“ThinkPHP中模板引擎是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI