上次提到过,模板引擎一般是要做三件事情:
变量值的输出(echo)
条件判断和循环(if ... else、for、foreach、while)
引入或继承其他文件
现在就来看看 Laravel 的模板引擎是如何来处理这三件事情的。我是在 Laravel 5.1 的实现上来写这篇文章的。
1. 视图解析流程
Laravel 的 View 部分是内置了两套输出系统:直接输出和使用 Blade 引擎“编译”后输出,默认情况下它们通过文件名后缀来选择:.blade.php 后缀的认为是模板视图文件,其他的 .php 文件按照 PHP 本身的方式执行。虽然 Blade 模板文件中也可以随意嵌入 PHP 代码,但如果并没有使用,系统还去进行语法解析和替换也是没有必要的,这样可以提高效率。
在使用 View 组件输出时,不管是调用 helpers 中提供的 view 函数还是使用 Facades 提供静态接口 View::make(),实际上执行的都是 Illuminate\View\Factory 中的 make 方法。以此为入口,很容易就能知道视图解析输出的流程:
查找视图文件;
根据文件名后缀从 Container 中取出响应的引擎;
加载视图文件或编译后加载编译后的文件执行,同时将需要解析的数据暴露在视图文件环境中。
Factory 中的一些方法完成了以上第一步的过程,文件查找是调用的 FileViewFinder,其中使用了一些 Illuminate\Filesystem\Filesystem 中的方法,这个类中还有一些方法是跟 events 相关的,这里就忽略不表了。
在以上步骤中,如果中获取到的视图文件是需要“编译”的,引擎会调用 “Blade 编译器”将原视图进行“编译”并保存在 cache 目录中然后加载输出。下次调用时如果发现源文件并没有被修改过就不再重新编译而是直接获取缓存文件并输出。
CompilerEngine 调用的编译器是 CompilerInterface 接口的实现,默认情况下也就只有 BladeCompiler(如果不知道解析器是如何注入的,你需要去了解 Laravel 的服务容器,这里就不细表)。
2. Blade 引擎
接下来就是本文的重点:Blade 是如何“编译”的。我一直给“编译”两个字加引号,因为这显然不是真正意义上的代码编译的过程,只是一些正则替换的过程。
我们知道 Laravel 的模板引擎是很简洁的,使用时并不需要掌握太多东西,基本上只需要知道以下两点:
{{ 与 }} 之间是要输出的内容,也有扩展的两个方法 {{{ ... }}} 和 {!! .. !!} 分别用于转义输出和不转义输出,5.0 以后的版本中 {{ ... }} 之间的默认情况下也是转义处处的;
@ 符号开头的都是指令,包括 PHP 本身有的 if else foreach 以及扩展的 include yield stop 等等;
而 Blade 对于解析的处理实际上是分了四种情况:
Extensions -> 扩展部分
Statements -> 语句块(就是 @ 开头的指令)
Comments -> 注释部分({{-- ... --}} 的写法,解析之后是 PHP 的注释而不是 HTML的注释)
Echos -> 输出
在解析(解析是在 cache 不存在的情况下)过程中,Blade 会先使用 token_get_all 函数获取到视图文件中的被 PHP 解释器认为是 HTML(T_INLINE_HTML)的部分,然后依次进行以上四种情况的解析。
扩展部分是调用用户自定义的编译器解析字符串。BladeCompiler 中提供了的 extend 方法来添加可扩展。
注释部分也很简单,就是将 {{-- ... --}} 替换成 。
输出部分提供了三个方法,分别对应上文提到的三种情况:
compileRawEchos -> 输出未经转义的内容 ({!! ... !!})
compileEscapedEchos -> 输出转义之后的内容 ({{{ ... }}})
compileRegularEchos -> 正常输出 ({{ ... }})
默认情况下经过字符替换之后 compileEscapedEchos 和 compileRegularEchos 的函数体其实是完全一样的,在输出的时候都是调用一个 e() 的辅助函数来输出:
<?php function e($value) { if ($value instanceof Htmlable) { return $value->toHtml(); } return htmlentities($value, ENT_QUOTES, 'UTF-8', false); }
这貌似是 5.0 之后的版本才改的,之前的版本里 compileRegularEchos 执行的是 compileRawEchos 的行为。不过两个函数还是有一个区别:compileRegularEchos 的转义函数是可以通过 setEchoFormat 自定义的(只是默认是 e()),但是 compileEscapedEchos 不允许自定义。
echo 后的内容也是经过正则替换的:
<?php public function compileEchoDefaults($value) { return preg_replace('/^(?=\$)(.+?)(?:\s+or\s+)(.+?)$/s', 'isset($1) ? $1 : $2', $value); }
从正则表达式中可以看出来输出提供了一个 or 的关键字,$a or $b 的写法会被替换成 isset($a) ? $a : $b。
语句块部分可以分成三种情况:
和 PHP 本身一样的 if else foreach 以及扩展的 unless 等流程和循环控制的关键字;
include yield 等模板文件引入、内容替换的部分;
lang choice can 等涉及到 Laravel 其他组件的功能性关键字。
第一种情况是很简单的替换过程,本身 PHP 为了在 HMTL 和 PHP 混合书写方便就提供了 if foreach 等几个关键字使用冒号和 endif 等关键字代替大括号来控制流程的方法。
第二种情况稍微复杂一点,比如下面的函数:
<?php protected function compileYield($expression) { return "<?php echo \$__env->yieldContent{$expression}; ?>"; }
解析之后的语句是调用了一个名为 $_env 的实例中的方法。这个实例其实就是 Illuminate\View\Factory 的实例:
Factory 的构造函数:
<?php public function __construct(EngineResolver $engines, ViewFinderInterface $finder, Dispatcher $events) { ... $this->share('__env', $this); }
Illuminate\View\View 中:
<?php protected function getContents() { return $this->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 等指令的实现也是在 Factory 中,分别对应的是 renderEach yieldContent 等。
所以文件引入等指令的实现方式就是:在主视图输出的时候,通过注入的 $__env 来重复调用 Factory 中的 make 方法来输出引入的文件。
至于 lang 等关键字,替换后就是使用 app() 函数来调用 Laravel 的其他组件。此外 Blade 还提供了 inject 关键字来调用任何你想使用的组件。
除了以上这些,你还可以通过 directive 方法来增加一些自定义指令。
compileStatements 方法中最后进行正则替换的正则表达式看起来比较复杂:
/\B@(\w+)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x
这是因为正则后面的一部分实现了递归模式来匹配语句块中括号的数量。
3. 后话
通过以上的分析可以看出来 Laravel 的视图组件还是十分简洁的,同时也不失灵活性和可扩展性。如果有兴趣的话,也可以实现一个自己的模板解析引擎。
如果你想在其他项目中使用 Blade 引擎,通过 Composer 安装下来之后会发现还有 Container、Events 等部分,这和 Laravel 本身有关。
为了能够在任何地方使用 Blade,我把它核心的部分提取了出来,去掉了其他组件的依赖,也不再依赖文件扩展名来选择引擎:
项目地址:https://github.com/XiaoLer/blade
此外也通过这个提取之后的版本做了一个 yii2 能够使用的版本:https://github.com/XiaoLer/yii2-blade。在之前尝试的版本中直接使用 Laravel 的 View 组件并不灵活,现在感觉好多了。
个人博客原文:http://0x1.im/blog/laravel/laravel-blade-engine.html

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP不是在消亡,而是在不斷適應和進化。 1)PHP從1994年起經歷多次版本迭代,適應新技術趨勢。 2)目前廣泛應用於電子商務、內容管理系統等領域。 3)PHP8引入JIT編譯器等功能,提升性能和現代化。 4)使用OPcache和遵循PSR-12標準可優化性能和代碼質量。

PHP的未來將通過適應新技術趨勢和引入創新特性來實現:1)適應云計算、容器化和微服務架構,支持Docker和Kubernetes;2)引入JIT編譯器和枚舉類型,提升性能和數據處理效率;3)持續優化性能和推廣最佳實踐。

在PHP中,trait適用於需要方法復用但不適合使用繼承的情況。 1)trait允許在類中復用方法,避免多重繼承複雜性。 2)使用trait時需注意方法衝突,可通過insteadof和as關鍵字解決。 3)應避免過度使用trait,保持其單一職責,以優化性能和提高代碼可維護性。

依賴注入容器(DIC)是一種管理和提供對象依賴關係的工具,用於PHP項目中。 DIC的主要好處包括:1.解耦,使組件獨立,代碼易維護和測試;2.靈活性,易替換或修改依賴關係;3.可測試性,方便注入mock對象進行單元測試。

SplFixedArray在PHP中是一種固定大小的數組,適用於需要高性能和低內存使用量的場景。 1)它在創建時需指定大小,避免動態調整帶來的開銷。 2)基於C語言數組,直接操作內存,訪問速度快。 3)適合大規模數據處理和內存敏感環境,但需謹慎使用,因其大小固定。

PHP通過$\_FILES變量處理文件上傳,確保安全性的方法包括:1.檢查上傳錯誤,2.驗證文件類型和大小,3.防止文件覆蓋,4.移動文件到永久存儲位置。

JavaScript中處理空值可以使用NullCoalescingOperator(??)和NullCoalescingAssignmentOperator(??=)。 1.??返回第一個非null或非undefined的操作數。 2.??=將變量賦值為右操作數的值,但前提是該變量為null或undefined。這些操作符簡化了代碼邏輯,提高了可讀性和性能。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

記事本++7.3.1
好用且免費的程式碼編輯器

Dreamweaver CS6
視覺化網頁開發工具

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具