摘要:上次提到過,模板引擎一般是要做三件事情變量值的輸出條件判斷和循環(huán)引入或繼承其他文件現(xiàn)在就來看看的模板引擎是如何來處理這三件事情的。引擎接下來就是本文的重點(diǎn)是如何編譯的。如果有興趣的話,也可以實現(xiàn)一個自己的模板解析引擎。
上次提到過,模板引擎一般是要做三件事情:
變量值的輸出(echo)
條件判斷和循環(huán)(if ... else、for、foreach、while)
引入或繼承其他文件
現(xiàn)在就來看看 Laravel 的模板引擎是如何來處理這三件事情的。我是在 Laravel 5.1 的實現(xiàn)上來寫這篇文章的。
1. 視圖解析流程Laravel 的 View 部分是內(nèi)置了兩套輸出系統(tǒng):直接輸出和使用 Blade 引擎“編譯”后輸出,默認(rèn)情況下它們通過文件名后綴來選擇:.blade.php 后綴的認(rèn)為是模板視圖文件,其他的 .php 文件按照 PHP 本身的方式執(zhí)行。雖然 Blade 模板文件中也可以隨意嵌入 PHP 代碼,但如果并沒有使用,系統(tǒng)還去進(jìn)行語法解析和替換也是沒有必要的,這樣可以提高效率。
在使用 View 組件輸出時,不管是調(diào)用 helpers 中提供的 view 函數(shù)還是使用 Facades 提供靜態(tài)接口 View::make(),實際上執(zhí)行的都是 IlluminateViewFactory 中的 make 方法。以此為入口,很容易就能知道視圖解析輸出的流程:
查找視圖文件;
根據(jù)文件名后綴從 Container 中取出響應(yīng)的引擎;
加載視圖文件或編譯后加載編譯后的文件執(zhí)行,同時將需要解析的數(shù)據(jù)暴露在視圖文件環(huán)境中。
Factory 中的一些方法完成了以上第一步的過程,文件查找是調(diào)用的 FileViewFinder,其中使用了一些 IlluminateFilesystemFilesystem 中的方法,這個類中還有一些方法是跟 events 相關(guān)的,這里就忽略不表了。
在以上步驟中,如果中獲取到的視圖文件是需要“編譯”的,引擎會調(diào)用 “Blade 編譯器”將原視圖進(jìn)行“編譯”并保存在 cache 目錄中然后加載輸出。下次調(diào)用時如果發(fā)現(xiàn)源文件并沒有被修改過就不再重新編譯而是直接獲取緩存文件并輸出。
CompilerEngine 調(diào)用的編譯器是 CompilerInterface 接口的實現(xiàn),默認(rèn)情況下也就只有 BladeCompiler(如果不知道解析器是如何注入的,你需要去了解 Laravel 的服務(wù)容器,這里就不細(xì)表)。
2. Blade 引擎接下來就是本文的重點(diǎn):Blade 是如何“編譯”的。我一直給“編譯”兩個字加引號,因為這顯然不是真正意義上的代碼編譯的過程,只是一些正則替換的過程。
我們知道 Laravel 的模板引擎是很簡潔的,使用時并不需要掌握太多東西,基本上只需要知道以下兩點(diǎn):
{{ 與 }} 之間是要輸出的內(nèi)容,也有擴(kuò)展的兩個方法 {{{ ... }}} 和 {!! .. !!} 分別用于轉(zhuǎn)義輸出和不轉(zhuǎn)義輸出,5.0 以后的版本中 {{ ... }} 之間的默認(rèn)情況下也是轉(zhuǎn)義輸出的;
@ 符號開頭的都是指令,包括 PHP 本身有的 if else foreach 以及擴(kuò)展的 include yield stop 等等;
而 Blade 對于解析的處理實際上是分了四種情況:
Extensions -> 擴(kuò)展部分
Statements -> 語句塊(就是 @ 開頭的指令)
Comments -> 注釋部分({{-- ... --}} 的寫法,解析之后是 PHP 的注釋而不是 HTML的注釋)
Echos -> 輸出
在解析(解析是在 cache 不存在的情況下)過程中,Blade 會先使用 token_get_all 函數(shù)獲取到視圖文件中的被 PHP 解釋器認(rèn)為是 HTML(T_INLINE_HTML)的部分,然后依次進(jìn)行以上四種情況的解析。
擴(kuò)展部分是調(diào)用用戶自定義的編譯器解析字符串。BladeCompiler 中提供了的 extend 方法來添加可擴(kuò)展。
注釋部分也很簡單,就是將 {{-- ... --}} 替換成 。
輸出部分提供了三個方法,分別對應(yīng)上文提到的三種情況:
compileRawEchos -> 輸出未經(jīng)轉(zhuǎn)義的內(nèi)容 ({!! ... !!})
compileEscapedEchos -> 輸出轉(zhuǎn)義之后的內(nèi)容 ({{{ ... }}})
compileRegularEchos -> 正常輸出 ({{ ... }})
默認(rèn)情況下經(jīng)過字符替換之后 compileEscapedEchos 和 compileRegularEchos 的函數(shù)體其實是完全一樣的,在輸出的時候都是調(diào)用一個 e() 的輔助函數(shù)來輸出:
toHtml(); } return htmlentities($value, ENT_QUOTES, "UTF-8", false); }
這貌似是 5.0 之后的版本才改的,之前的版本里 compileRegularEchos 執(zhí)行的是 compileRawEchos 的行為。不過兩個函數(shù)還是有一個區(qū)別:compileRegularEchos 的轉(zhuǎn)義函數(shù)是可以通過 setEchoFormat 自定義的(只是默認(rèn)是 e()),但是 compileEscapedEchos 不允許自定義。
echo 后的內(nèi)容也是經(jīng)過正則替換的:
從正則表達(dá)式中可以看出來輸出提供了一個 or 的關(guān)鍵字,$a or $b 的寫法會被替換成 isset($a) ? $a : $b。
語句塊部分可以分成三種情況:
和 PHP 本身一樣的 if else foreach 以及擴(kuò)展的 unless 等流程和循環(huán)控制的關(guān)鍵字;
include yield 等模板文件引入、內(nèi)容替換的部分;
lang choice can 等涉及到 Laravel 其他組件的功能性關(guān)鍵字。
第一種情況是很簡單的替換過程,本身 PHP 為了在 HMTL 和 PHP 混合書寫方便就提供了 if foreach 等幾個關(guān)鍵字使用冒號和 endif 等關(guān)鍵字代替大括號來控制流程的方法。
第二種情況稍微復(fù)雜一點(diǎn),比如下面的函數(shù):
yieldContent{$expression}; ?>"; }解析之后的語句是調(diào)用了一個名為 $_env 的實例中的方法。這個實例其實就是 IlluminateViewFactory 的實例:
Factory 的構(gòu)造函數(shù):
share("__env", $this); }IlluminateViewView 中:
engine->get($this->path, $this->gatherData()); } /** * Get the data bound to the view instance. * * @return array */ protected function gatherData() { $data = array_merge($this->factory->getShared(), $this->data); ... return $data; }由此也可以看出 each yield 等指令的實現(xiàn)也是在 Factory 中,分別對應(yīng)的是 renderEach yieldContent 等。
所以文件引入等指令的實現(xiàn)方式就是:在主視圖輸出的時候,通過注入的 $__env 來重復(fù)調(diào)用 Factory 中的 make 方法來輸出引入的文件。
至于 lang 等關(guān)鍵字,替換后就是使用 app() 函數(shù)來調(diào)用 Laravel 的其他組件。此外 Blade 還提供了 inject 關(guān)鍵字來調(diào)用任何你想使用的組件。
除了以上這些,你還可以通過 directive 方法來增加一些自定義指令。
compileStatements 方法中最后進(jìn)行正則替換的正則表達(dá)式看起來比較復(fù)雜:
/B@(w+)([ ]*)(( ( (?>[^()]+) | (?3) )* ))?/x這是因為正則后面的一部分實現(xiàn)了遞歸模式來匹配語句塊中括號的數(shù)量。
3. 后話通過以上的分析可以看出來 Laravel 的視圖組件還是十分簡潔的,同時也不失靈活性和可擴(kuò)展性。如果有興趣的話,也可以實現(xiàn)一個自己的模板解析引擎。
如果你想在其他項目中使用 Blade 引擎,通過 Composer 安裝下來之后會發(fā)現(xiàn)還有 Container、Events 等部分,這和 Laravel 本身有關(guān)。
為了能夠在任何地方使用 Blade,我把它核心的部分提取了出來,去掉了其他組件的依賴,也不再依賴文件擴(kuò)展名來選擇引擎:
項目地址:https://github.com/XiaoLer/blade
此外也通過這個提取之后的版本做了一個 yii2 能夠使用的版本:https://github.com/XiaoLer/yii2-blade。在之前嘗試的版本中直接使用 Laravel 的 View 組件并不靈活,現(xiàn)在感覺好多了。
個人博客地址:http://0x1.im/
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/21199.html
摘要:官方地址是目前最流行的框架,發(fā)展勢頭迅猛,應(yīng)用非常廣泛,有豐富的擴(kuò)展包可以應(yīng)付你能想到的各種應(yīng)用場景,框架思想前衛(wèi),跟隨時代潮流,提倡優(yōu)雅代碼,自稱為工匠,其中的模板引擎容器以及擴(kuò)展包為業(yè)務(wù)的開發(fā)提供了極大的便利。 laravel5.5+ laravel官方地址 laravel是目前最流行的php框架,發(fā)展勢頭迅猛,應(yīng)用非常廣泛,有豐富的擴(kuò)展包可以應(yīng)付你能想到的各種應(yīng)用場景,lara...
摘要:之前我是使用的這個從中抽離出來的模板引擎,并且自定義為使用文件后綴。這下一切都簡單了,我們只需要在或者其他適當(dāng)?shù)姆?wù)提供者里通過調(diào)用就可以非常方便快捷地添加擴(kuò)展名了謹(jǐn)以此文記錄,愿能幫到后來人 原文地址:https://prinzeugen.net/add-ex... 因為一些原因,我準(zhǔn)備把 Blessing Skin 的框架換成 Laravel 了(之前是自己搭建的一個框架),但是在...
摘要:但是不用擔(dān)心,我們后續(xù)的教程會陸續(xù)講解相關(guān)的內(nèi)容。所以上面的路由注冊其實就是負(fù)責(zé)解決訪問的時候的響應(yīng)。 原文來自:https://jellybool.com/post/programming-with-laravel-5-routers-views-controllers-workflow 免費(fèi)視頻教程地址 https://laravist.com/series/laravel-5-b...
摘要:在中使用是一個很棒的單純的由和構(gòu)建的后臺模板,在這片文章中,我將講述如何將和優(yōu)雅的整合在一起,而且我們可以通過來及時的更新和管理。 showImg(https://segmentfault.com/img/bVqBce); 在Laravel5.* 中使用 AdminLTE AdminLTE是一個很棒的單純的由 HTML 和 CSS 構(gòu)建的后臺模板,在這片文章中,我將講述如何將 Admi...
摘要:接下來我將帶大家認(rèn)識下五個指令,這些指令將讓你在解決特定問題時如虎添翼。如果你是剛接觸的用戶,這些小技巧能帶你認(rèn)識到模板引擎的便捷與高效。 showImg(https://segmentfault.com/img/remote/1460000015076241); 接下來我將帶大家認(rèn)識下五個 Laravel Blade 指令,這些指令將讓你在解決特定問題時如虎添翼。如果你是剛接觸 La...
閱讀 921·2023-04-25 18:51
閱讀 1875·2021-09-09 11:39
閱讀 3285·2019-08-30 15:53
閱讀 2104·2019-08-30 13:03
閱讀 1314·2019-08-29 16:17
閱讀 587·2019-08-29 11:33
閱讀 1888·2019-08-26 14:00
閱讀 2126·2019-08-26 13:41