ThinkPHP6 核心分析之Http 類跟Request類的實例化

如下源碼分析,咱們能夠從 App,Http 類的實例化過程,瞭解類是如何實現自動實例化的,依賴注入是怎麼實現的。

從入口文件出發

當訪問一個 ThinkPHP 搭建的站點,框架最早是從入口文件開始的,而後纔是應用初始化、路由解析、控制器調用和響應輸出等操做。
 
入口文件主要代碼以下:
 

App 實例化

執行 new App() 實例化時,首先會調用它的構造函數。
 
 
構造函數實現了項目各類基礎路徑的初始化,並讀取了 provider.php 文件,將其類的綁定併入 $bind 成員變量,provider.php 文件默認內容以下:
 
 
合併後,$bind 成員變量的值以下:
 
 
$bind 的值是一組類的標識到類的映射。從這個實現也能夠看出,咱們不只能夠在 provider.php 文件中添加標識到類的映射,並且能夠覆蓋其原有的映射,也就是將某些核心類替換成本身定義的類。
 
static::setInstance($this) 實現的做用,如圖:
 
 
think\App 類的 $instance 成員變量指向 think\App 類的一個實例,也就是類本身保存本身的一個實例。
instance() 方法的實現:
 
 
其中的getAlias方法:
 
 
執行結果大概是這樣的:
 

Http 類的實例化以及依賴注入原理

這裏,$http = (new App())->http,前半部分好理解,後半部分乍一看有點讓人摸不着頭腦,App 類並不存在 http 成員變量,這裏何以大膽調用了一個不存在的東東呢?
 
原來,App 類繼承自 Container 類,而 Container 類實現了__get() 魔術方法,在 PHP 中,當訪問到的變量不存在,就會觸發__get() 魔術方法。該方法的實現以下:
 
 
其實是調用get()方法:
 
 
然而,實際上,主要是make()方法:
 
 
然而,make()方法主要靠invokeClass()來實現類的實例化。該方法具體分析:
 
 
以上代碼可看出,在一個類中,添加__make()方法,在類實例化時,會最早被調用。以上最值得一提的是bindParams()方法:
 
 
而這之中,又最值得一提的是getObjectParam()方法:
 
 
getObjectParam() 方法再一次光榮地調用 make() 方法,實例化一個類,而這個類,正是從 Http 的構造函數提取的參數,而這個參數又偏偏是一個類的實例 ——App 類的實例。到這裏,程序不只經過 PHP 的反射類實例化了 Http 類,並且實例化了 Http 類的依賴 App 類。假如 App 類又依賴 C 類,C 類又依賴 D類…… 無論多少層,整個依賴鏈條依賴的類均可以實現實例化。
 
總的來講,整個過程大概是這樣的:須要實例化 Http 類 ==> 提取構造函數發現其依賴 App 類 ==> 開始實例化 App 類(若是發現還有依賴,則一直提取下去,直到天荒地老)==> 將實例化好的依賴(App 類的實例)傳入 Http 類來實例化 Http 類。
 
這個過程,起個裝逼的名字就叫作「依賴注入」,起個摸不着頭腦的名字,就叫作「控制反轉」。
 
這個過程,若是退回遠古時代,要實例化 Http 類,大概是這樣實現的(假若有不少層依賴):
 
 
這得有多累人。而現代 PHP,交給「容器」就行了。
 
另外,須要提的一點是 make 方法的 $vars 參數,它的形式能夠是普通數組、關聯數組,並且數組中元素的值能夠是一個類的實例。$vars 參數的值最終將傳遞給要實例化的類的構造函數或者__make 方法中對應的參數。
 

Request 類的實例化

接上一面,獲得Http類的一個實例後,程序接下來執行$response = $http->run();。其中run()方法代碼以下:
 
 

從「request」標識找到要實例化的類

run() 方法的第一行經過容器類實例 app 調用 make() 方法並傳入 Request 類的標識來實例化 Request 類。具體過程以下分析。
 
經過 make() 方法首先解析獲得 request 標識對應的標識 think\Request, 進一步遞歸解析,又獲得 app\Request 類 —— 這個纔是最終要實例化的類。
 
app\Request 類對應的文件位於 app 目錄下,代碼以下:
 
 
實際上它啥事也沒幹,直接繼承系統的 \think\Request。固然咱們也能夠在這裏對系統的 Request 類進行改寫重構。

調用 invokeClass () 方法

從類的標識解析獲得最終須要實例化的類(單例模式下,且該類還不存在實例)以後,程序調用 invokeClass () 方法,經過 PHP 的反射類實現類的實例化。因爲 \think\Request 類存在__make() 方法,因此實例化以前首先調用該方法。__make() 方法代碼以下:
 
 
__make()方法首先實例化think\Request類自身。think\Request類構造函數以下:
 
 
構造函數讀取了 php://input 保存起來。接着,__make() 方法保存了一些請求相關的數據,最後返回一個 Request 類實例。最後的最後, make() 方法也成功獲得該實例,整個過程跟 Http 類的實例化相似。該 Request 類實例部分紅員變量如圖:
 

保存「Request」類的實例到「$instances」數組

獲得 Request 類的實例後,run() 方法接着將該實例保存到「$instance」數組,以便後面單例模式要用到時能夠直接獲取。$instances 數組的值如圖,Request 類的實例已保存在裏面:
 
相關文章
相關標籤/搜索