「本文會對實例化控制器為引子然後解析關於ArrayAccess和直接執行魔術存取返回實例的區別
」
在上文中對路由進行了特別的詳解,也從應用初始化開始解析一直到路由調度返回給路由檢測這一環節。
路由偵測所獲得的值如下圖,也就是路由調度最終回傳的值。
所使用的路由規則為Route::get('hello/:name', 'index/index/:name');
從上圖可以看出重要資料都是在dispatc中存放的,接下來就會對控制器進行詳解。
最先說明的就是的當路由偵測完畢之後執行的實例化控制器操作。
先來看看是怎麼執行到實例化控制器吧!
毫無疑問程式碼肯定是先從入口檔案開始執行的,這裡使用容器回傳一個App的實例,然後去呼叫App類別中的run方法。
下來就會來到執行應用程序,在這個方法中也是在上文剛剛解析的路由。
所以偵測路由執行完就會去執行實例化控制器。
在路由偵測執行完後傳回的是think\route\dispatch\Module Object
這個類,而這個類別賦值給了變數$dispatch
接著看一下本方法的這塊程式碼,這裡使用的是中間件,在這快程式碼中還是用了閉包,對閉包的概念不清晰的就需要回頭啃基礎了。
在上圖喀喀圈出來的地方就是$dispatch->run()
這塊程式碼,接下來就要對這塊程式碼進行解析了。
在偵測路由最終的回傳值可以知道其實這個方法是在think\route\dispatch\Module
這個類別中。
接著就需要對這個類別中的run方法進行解析了,而這個方法也就是執行路由調度。
在這個方法中不管是取得路由參數或是偵測路由、資料自動驗證都不會執行(是依照喀喀爾上文給的路由位址為案例)。
所以根據上圖程式碼就會執行到$data = $this-> exec();
這裡。
追蹤這個方法會到下圖地方存在一個抽象類,這裡需要知道的是抽象類別。
#對抽象類別做出解釋
怎麼去找誰繼承了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);
這裡來。
參數說明
接著透過正規對傳過來的參數進行處理,最後傳回moduleInit
然後透過$obj = Container::get($class);
傳回behavior\LoadBehavior的實例
最後透過is_callable
這個函數進行驗證,檢測類別裡邊的方法是否可以被調用,方法數組格式,這個方法後期咔咔單獨寫一篇文章作為物件來解析,這裡只需要知道會返回false即可。
然後會把本類別的$portal
這個值賦值給$method
,這個值就是run。
最後通過$result = $this->app->invoke($call, [$params]);
這行程式碼,這行程式碼的底部執行就是透過反射機制實現的。
最後這段程式碼會回傳NULL。
實例化控制器
接下來就是進行實例化控制器,呼叫的方法是$this->app->controller()
這裡要注意的是list這個函數,這個函數的後邊會回傳一個數組,然後list中的兩個變數會分別為索引0和1。
判斷也會去執行第一個,同樣會執行到容器類別的make方法,這個方法會直接傳回app\index\controller\Index
這個類別的實例。
有一部分小夥伴都已經學會了ArrayAccess和魔術方法__get的使用了。
估計也有一部分在這兩個地方處於模糊地段,咔咔將這兩個放在一起在解析一次。
先聊聊ArrayAccess的使用
這個案例在之前也給大家示範過,主要就是實作ArrayAccess的這個類別。
#然後來到控制器使用,先進行實例化,之前實作的案例如下。
但這次需要實現的案例並不是下圖所實現的。
接下來使用下圖的方式進行訪問,直接使用陣列存取物件屬性。
在上圖中可以看到設定了一個屬性title為kaka,在這個案例中直接用陣列形式直接取得。
看到回傳結果為kaka,也就是說直接使用陣列形式存取物件的屬性。
#總結
在第一次案例的實作過程中,忽略了一步,就是使用物件直接以數組形式直接存取物件的屬性。
可以看到的是可以直接取得到的,那麼接下來將這個想法套到框架中在來看一下。
框架實戰案例
在上一期文章中解析的路由中存在以下程式碼,接下來進行簡單的解析一下。
先來看看這個app的值印出來就是think\App Object
對象。
當think\App 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
offsetGet
這個方法。 以上是ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別的詳細內容。更多資訊請關注PHP中文網其他相關文章!