提示:
vendor\laravel\framework\src\Illuminate\View
是視圖模塊所在的文件夾,如未說明類所在文件位置則指此文件夾。
1. 服務提供者類注入相關類到容器
在使用視圖相關的功能之前是需要做些准備的,准備自然是在服務提供者類運行的(在app\config.php
的providers中定義了應用中的服務提供者類),從中我們可以看出Illuminate\View\ViewServiceProvider::class
指的就是ViewServiceProvider
類,因此在ViewServiceProvider
類中會注冊一些需要用到的依賴到容器,注入名稱和實例如下:
view.engine.resolver EngineResolver
php PhpEngine
blade CompilerEngine
blade.compiler BladeCompiler
view.finder FileViewFinder
view Factory

這個時候我們已經可以看出Factory
是視圖模塊重要的類了,因為Laravel會從容器中取出view.engine.resolver, view.finder, events(這個類是其他地方注入到容器中的)中的對象作為參數傳遞給Factory
,而php和blade雖然並沒有注入到容器,卻是作為EngineResolver
的屬性$resolvers
的值的。
2.開始使用視圖
上一步驟只是做了些視圖運行前的准備,而並沒有真的運行視圖的功能,一般運行是從view()
開始的,這是一個全局的方法,這個方法是定義在vendor\laravel\framework\src\Illuminate\Foundation\helpers.php
中的,這里其實調用了Factory
的make()
,這個方法其實主要是對傳入的參數做處理並用這些參數初始化View
類,再返回View
的實例。
$this->callCreator($view = new View($this, $this->getEngineFromPath($path), $view, $path, $data)); return $view;
對View
的處理還涉及到Router
和Response
的處理,不理會這個我們是知道視圖其實返回的應該是字符串的才對。對象轉字符串自然使用的是魔術方法__toString()
,這里並不直接返回字符串,而是通過一系列的調用最終是在getContents()
中返回$this->engine->get()
,這里的engine是CompilerEngine
,即上面View實例化的時候傳遞的第二個參數(也是EngineInterface
的實現),因為Laravel的視圖文件的名稱包含blade,所以實例化的是CompilerEngine
。事實上這和容器中名為view.engine.resolver的類實例有關,具體細節可以在getEngineFromPath()
中找。
3.模版的編譯
CompilerEngine
的$compiler
才是實際把視圖文件轉成字符串的主力。一般的$compiler
的實例是BladeCompiler
,這個就是把Blade語法(Blade是Laravel內置模版引擎的名字)轉換成PHP語法的核心。
不管上面的步驟如何的繞來繞去,把視圖文件編譯成字符串才是BladeCompiler
類的主場功能,這個類才是真正的苦力工作者。BladeCompiler
實現了CompilerInterface
接口,這個接口真正工作是compile()
,compile()
把視圖文件編譯成PHP的原生語法的字符串並寫入到緩存目錄的文件中,而getCompiledPath()
根據視圖文件的路徑來獲取編譯后的文件的路徑。
編譯使用token_get_all()
來進行語法的解析,當碰到PHP標記為T_INLINE_HTML
(T_INLINE_HTML
標記其實就是code無法直接解析成PHP而把這段code當作一段嵌套的HTML,這里一般就是指Blade語法的Code)的PHP標記時用對應的方法進行語法的替換,直到最終沒有PHP標記為T_INLINE_HTML
為止。
無法直接編譯的code分為4類,分別是擴展,語句,注釋,輸出,這個從$compilers
中可以看出,我們可以稱$compilers
為內部編譯器,每個內部編譯器對應一個相應的解析方式,如注釋編譯器對應compileComments()
。
/** * Compile Blade comments into valid PHP. * * @param string $value * @return string */ protected function compileComments($value) { $pattern = sprintf('/%s--((.|\s)*?)--%s/', $this->contentTags[0], $this->contentTags[1]); return preg_replace($pattern, '<?php /*$1*/ ?>', $value); }
這里就把Blade語法的code{{-- This comment will not be present in the rendered HTML --}}
變成原生PHP語法的code<?php /*This comment will not be present in the rendered HTML*/ ?>
了。 從中不難看出就是把Blade語法翻譯成PHP原生語法的實現。當然這是最簡單的內部編譯器,像語句,輸出內部編譯器的實現就復雜的多,但只是實現復雜,涉及到正則的解析和不同形式的語句的解析,原理還是一樣的。