前回述べたように、テンプレートエンジンは一般的に次の 3 つのことを行います。
変数値の出力 (エコー)
条件判断ループ (if ... else、for、foreach、while)
他のファイルの導入または継承
Laravel を見てみましょう。テンプレート エンジンはこれら 3 つのことを処理します。この記事はLaravel 5.1の実装をベースに書いています。
Laravel の View 部分には、直接出力と Blade エンジンを使用した「コンパイル」後の出力という 2 つの組み込み出力システムがあります。デフォルトでは、これらはファイル名を介して出力されます。選択: .blade.php 接尾辞はテンプレート ビュー ファイルとみなされ、他の .php ファイルは PHP 自体に従って実行されます。 PHP コードは Blade テンプレート ファイルに自由に埋め込むことができますが、それを使用しない場合は、システムが構文解析や置換を実行する必要がないため、効率が向上します。
View コンポーネントの出力を使用する場合、ヘルパーで提供される view 関数を呼び出すか、Facades が提供する静的インターフェイス View::make() を使用するかにかかわらず、IlluminateViewFactory の make メソッドが実際に実行されます。これをエントリ ポイントとして使用すると、ビューの解析出力のプロセスを簡単に知ることができます。
ビュー ファイルを見つけます。
からの応答を取得します。ファイル名のサフィックスに基づくコンテナ。エンジン
は、解析する必要があるデータをビュー ファイルに公開しながら、ビュー ファイルをロードするか、コンパイル後にコンパイルされたファイルをロードします。環境。
Factory の一部のメソッドは、上記の最初のステップを完了します。ファイル検索は、IlluminateFilesystemFilesystem のメソッドを使用します。このクラスには、イベントに関連するメソッドもいくつかあります。ここで言及されています。
上記の手順で、取得したビュー ファイルを「コンパイル」する必要がある場合、エンジンは「ブレード コンパイラ」を呼び出して元のビューを「コンパイル」し、それをキャッシュ ディレクトリに保存してから、出力。次回呼び出し時にソースファイルが変更されていないことが判明した場合は、再コンパイルせずに直接キャッシュファイルを取得して出力します。
CompilerEngine によって呼び出されるコンパイラは、CompilerInterface インターフェイスの実装です。デフォルトでは、これは BladeCompiler のみです (パーサーがどのように挿入されるかを知らない場合は、Laravel のサービス コンテナを理解する必要があります。詳細はこちら)。
この記事の次のステップは、Blade がどのように「コンパイル」されるかです。私は常に「コンパイル」という単語を引用符で囲みます。これは明らかに、本当の意味でのコードのコンパイルのプロセスではなく、単なる定期的な置換のプロセスだからです。
Laravel のテンプレート エンジンは非常にシンプルであることがわかりますが、基本的に次の 2 つの点を理解するだけで済みます。 {{ と }} の間には、出力されるコンテンツが含まれます。5.0 以降のバージョンでは、それぞれエスケープ出力用とエスケープなし出力用の 2 つの拡張メソッド {{{ ... }}} と {!! .. !!} があります。 { { ... }} も、デフォルトではどこでもエスケープされます。
ステートメント ブロック (コマンドの開始) @付き)
コメント -> コメント ({{-- ... --}} メソッドの記述、解析後は HTML コメントではなく PHP コメントになります)
Echos -> 出力
拡張部分は、ユーザー定義のコンパイラを呼び出して文字列を解析します。拡張性を追加するために、BladeCompiler には extend メソッドが提供されています。
コメント部分も非常に簡単で、{{-- ... --}} を に置き換えるだけです。
出力セクションには、上記の 3 つの状況に対応する 3 つのメソッドが用意されています。
compileRawEchos -> エスケープされていないコンテンツを出力します ({!! . .. !!})compileEscapedEchos -> エスケープされた内容を出力します ({{{ ... }}})
compile RegularEchos -> 通常の出力 ({{ ... }})
これは 5.0 以降のバージョンで変更されたようです。以前のバージョンでは、compile RegularEchos は、compileRawEchos の動作を実行していました。ただし、この 2 つの関数にはまだ 1 つの違いがあります。compile RegularEchos のエスケープ関数は setEchoFormat (ただし、デフォルトは e()) を通じてカスタマイズできますが、compileEscapedEchos ではカスタマイズができません。
エコーの後の内容も正規表現に置き換えられています:
<?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
这是因为正则后面的一部分实现了递归模式来匹配语句块中括号的数量。
通过以上的分析可以看出来 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