“
本文會對實例化控制器為引子然后解析關于ArrayAccess和直接執行魔術訪問返回實例的區別
”
前言
在上文中對路由進行了特別的詳解,也從應用初始化開始解析一直到路由調度返回給路由檢測這一環節。
路由檢測獲取到的值如下圖,也就是路由調度最終返回的值。
使用的路由規則為Route::get(‘hello/:name’, ‘index/index/:name’);
從上圖可以看出重要數據都是在dispatc中存放的,接下來就會對控制器進行詳解。
最先說明的就是的當路由檢測完畢之后執行的實例化控制器操作。
一、實例化控制器
先來看一下是怎么執行到實例化控制器吧!
毫無疑問代碼肯定是先從入口文件開始執行的,這里使用容器返回一個App的實例,然后去調用App類中的run方法。
下來就會來到執行應用程序,在這個方法中也是在上文剛剛解析的路由。
所以檢測路由執行完就會去執行實例化控制器。
在路由檢測執行完之后返回的是thinkroutedispatchModule Object這個類,并且這個類賦值給了變量$dispatch
立即學習“PHP免費學習筆記(深入)”;
接著看一下本方法的這塊代碼,這里使用的是中間件,在這快代碼中還是用了閉包,對閉包的概念不清晰的就需要回頭啃基礎了。
在上圖中咔咔圈出來的一個地方就是$dispatch->run()這塊代碼,接下來就要對這塊代碼進行解析了。
在檢測路由最終的返回值可以知道其實這個方法是在thinkroutedispatchModule這個類中。
接著就需要對這個類中的run方法進行解析了,這個方法也就是執行路由調度。
在這個方法中不管是獲取路由參數還是檢測路由、數據自動驗證都不會執行(是按照咔咔上文給的路由地址為案例)。
所以根據上圖代碼就會執行到$data = $this->exec();這里。
跟蹤這個方法會到下圖地方存在一個抽象類,這里需要知道的是抽象類。
對抽象類做出解釋
-
抽象類不能被實例化 -
有抽象方法的類一定是抽象類;類必須要abstract修飾 -
抽象方法不能有函數體;即abstract function fun(); -
抽象類中的非抽象方法,可以被子類調用 -
非抽象子類繼承抽象類,子類必須實現父類的所有抽象方法 -
抽象子類繼承抽象類,無需繼承父類的抽象方法
根據上圖的原則可以看到Dispatch這個類是抽象類。
所以就會有倆種情況, 一種是抽象類繼承抽象類,無需繼承父類的抽象方法。
另一種是非抽象子類繼承抽象類,子類必須實現父類的所有抽象方法。
怎么去找誰繼承了Dispatch
這個時候是不是有一個疑問就是怎么去找Dispatch的子類。
在這個圖中可以看到本類Dispatch,但是還有一個dispatch這個目錄。
根據路由檢測返回的數據可以輕而易舉的就知道是thinkphp/library/think/route/dispatch/Module.php這個類。
來到thinkphp/library/think/route/dispatch/Module.php查看exec方法。
那么接下來的任務就是對這個方法進行深入的解讀了。
先看第一行代碼$this->app[‘hook’]->listen(‘module_init’);,在這里使用了容器ArrayAccess用數組的形式訪問對象,然后執行的魔術方法__get,當訪問不存在的屬性時會去執行make方法。
使用編輯器追蹤這個app會到thinkphp/library/think/route/Dispatch.php這個類里邊,在這個類的構造函數中可以看到對于app這個屬性是賦值了一個App實例。
接著來到App類可以看到繼承的是Container類。
在容器這塊已經不止一次的說過這塊的知識點了,訪問不存在的屬性回去執行容器的__get魔術方法。
所以說這塊的參數會傳入hook,并且會返回hook的實例,關于這個實例是怎么返回的在容器那一節中說的很是詳細,可以去看一下哈!
接下來就會去執行hook的listen方法,監聽標簽的行為。
此時可以來到應用行為擴展定義文件,可以看到這個參數為模塊初始化,但是因為這個值是空的。
所以在上圖不會去執行,那么就把應用初始化的值給放到這個參數里邊進行簡單的測試。
這個類就是執行的鉤子,對門面類的優化操作。
那么代碼就會執行到$results[$key] = $this->execTag($name, $tag, $params);這里來。
參數說明
-
$name = String(22) “behaviorLoadBehavior” -
$tag = module_init
接著通過正則對傳過來的參數進行處理,最終返回moduleInit
然后通過$obj = Container::get($class);返回behaviorLoadBehavior的實例
最終通過is_callable這個函數進行驗證,檢測類里邊的方法是否可以被調用,方法數組格式,這個方法后期咔咔單獨寫一篇文章作為對象來解析,這里只需要知道會返回false即可。
然后會把本類的$portal這個值賦值給$method,這個值就是run。
最后通過$result = $this->app->invoke($call, [$params]);這行代碼,這行代碼的底部執行就是通過反射機制實現的。
最后這段代碼會返回NULL。
實例化控制器
接下來就是進行實例化控制器,調用的方法是$this->app->controller()
這里需要注意的是list這個函數,這個函數的后邊會返回一個數組,然后list中的倆個變量會分別為索引0和1。
判斷也會去執行第一個,同樣會執行到容器類的make方法,這個方法會直接返回appindexcontrollerIndex這個類的實例。
二、關于ArrayAccess和直接執行魔術訪問返回實例的區別
有一部分小伙伴都已經學會了ArrayAccess和魔術方法__get的使用了。
估計也有一部分在這倆個地方處于模糊地段,咔咔將這倆個放在一起在解析一次。
先聊ArrayAccess的使用
這個案例在之前也給大家演示過,主要就是實現ArrayAccess的這個類。
然后在來到控制器進行使用,先進行實例化,之前實現的案例如下。
但是這次需要實現的案例并不是下圖所實現的。
接下來使用下圖的方式進行訪問,直接使用數組訪問對象屬性。
在上圖中可以看到設置了一個屬性title為kaka,在這個案例中直接用數組形式直接獲取。
看到返回結果為kaka,也就是說直接使用數組形式訪問對象的屬性。
總結
在第一次案例的實現過程中,忽略了一步,就是使用對象直接以數組形式直接訪問對象的屬性。
可以看到的是可以直接獲取到的,那么接下來將這個思想套到框架中在來看一下。
框架實戰案例
在上一期文章中解析的路由中存在以下代碼,接下來進行簡單的解析一下。
先來看一下這個app的值打印出來就是thinkApp Object對象。
當thinkApp Object這個對象去訪問request時,因為app屬性就沒有這個request,又因為app類是繼承著container類,所以會去容器類執行下圖方法。
然后就會去執行__get方法,執行make方法返回對應的實例。
此時你要是還有疑問就是,怎么就咔咔說會執行就會執行呢!
接下來咔咔帶著大家做一個簡單的測試就知道了。
在這個位置中隨機打印一個數值。
然后來到容器類的ArrayAccess的offsetGet方法中打印一下傳過來的值。
看一下打印結果,就很明確了。
關于ArrayAccess的使用就到這里就結束了,這也是在之前的基礎上詳細的進行了一次說明,接下來對容器中的__get方法進行詳解,看在什么情況會執行__get方法。
__get方法使用詳解
這個案例請看下圖中的這個$this->hook。
同樣的道理先來調試一下這個$this是什么值。
打印這個值都沒什么必要,因為就是在本類中。
在類中屬性的訪問應該都會,就是直接使用$this->?即可。
所以說當系統訪問$this->hook這個的時候,由于App類是不存在hook這個屬性的,所以就會去執行容器類的魔術方法。
然后在去執行make方法,創建類的實例。
總結
所以說是用ArrayAccess和__get魔術方法,最終都是執行的make方法返回類的實例。
當遇到this->config就是執行的容器的__get方法。
當遇到app[‘request’]就是執行的ArrayAccess然后執行offsetGet
-
__get是針對類的屬性,當類的屬性不存在時會執行 -
ArrayAccess當是用實例化好的類以數組形式訪問時,如果不存在就會執行offsetGet這個方法。 推薦教程:《thinkphp》