首頁  >  文章  >  php框架  >  ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別

咔咔
咔咔原創
2020-12-04 12:49:35138瀏覽

本文會對實例化控制器為引子然後解析關於ArrayAccess和直接執行魔術存取返回實例的區別

前言

在上文中對路由進行了特別的詳解,也從應用初始化開始解析一直到路由調度返回給路由偵測這一環節。

路由偵測所獲得的值如下圖,也就是路由調度最終回傳的值。

所使用的路由規則為Route::get('hello/:name', 'index/index/:name');

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
列印的地方
ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
路由偵測列印的結果

#從上圖可以看出重要資料都是在dispatc中存放的,接下來就會對控制器進行詳解。

最先說明的就是的當路由偵測完畢之後執行的實例化控制器操作。

一、實例化控制器

#先來看看是怎麼執行到實例化控制器吧!

毫無疑問程式碼肯定是先從入口檔案開始執行的,這裡使用容器回傳一個App的實例,然後去呼叫App類別中的run方法。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
入口檔

下來就會來到執行應用程序,在這個方法中也是在上文剛剛解析的路由。

所以偵測路由執行完就會去執行實例化控制器。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
執行應用程式

在路由偵測執行完之後傳回的是think\route\dispatch\Module Object這個類,而這個類別賦值給了變數$dispatch

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
#路由偵測

接著看一下本方法的這塊程式碼,這裡使用的是中間件,在這快代碼中還是用了閉包,對閉包的概念不清晰的就需要回頭啃基礎了。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
中間件

在上圖中喀喀圈出來的地方就是$dispatch->run()這塊程式碼,接下來就要對這塊程式碼進行解析了。

在偵測路由最終的回傳值可以知道其實這個方法是在think\route\dispatch\Module這個類別中。

接著就需要對這個類別中的run方法進行解析了,而這個方法也就是執行路由調度。

在這個方法中不管是取得路由參數或是偵測路由、資料自動驗證都不會執行(是依照喀喀爾上文給的路由位址為案例)。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
執行路由調度

所以根據上圖程式碼就會執行到$data = $this->exec();這裡。

追蹤這個方法會到下圖地方存在一個抽象類,這裡要知道的是抽象類別。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
抽象類別

對抽象類別做出解釋

  • 抽象類別不能被實例化
  • 有抽象方法的類別一定是抽象類別;類別必須要abstract修飾
  • 抽象方法不能有函數體;即abstract function fun();
  • #抽象類別中的非抽象方法,可以被子類別呼叫
  • ##非抽象子類別繼承抽象類,子類別必須實作父類別的所有抽象方法
  • #抽象子類別繼承抽象類,而無需繼承父類別的抽象方法
根據上圖的原則可以看到

Dispatch這個類別是抽象類別。

所以就會有兩個種情況, 一種是抽象類別繼承抽象類,無需繼承父類別的抽象方法。

另一種是非抽象子類別繼承抽象類,子類別必須實作父類別的所有抽象方法。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別抽象類別

怎麼去找誰繼承了Dispatch

這個時候是不是有一個疑問就是怎麼去找Dispatch的子類別。

在這個圖中可以看到本類Dispatch,但還有一個dispatch這個目錄。

根據路由偵測回傳的資料可以輕易的就知道是

thinkphp/library/think/route/dispatch/Module.php這個類別。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
尋找子類別

來到thinkphp/library/think/route/dispatch/Module.php查看exec方法。

那麼接下來的任務就是對這個方法進行深入的解讀了。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
實例化控制器

先看第一行程式碼$this->app['hook']->listen( 'module_init');,在這裡使用了容器ArrayAccess用數組的形式訪問對象,然後執行的魔術方法__get,當訪問不存在的屬性時會去執行make方法。

使用編輯器追蹤這個app會到thinkphp/library/think/route/Dispatch.php這個類別裡邊,在這個類別的建構子中可以看到對於app這個屬性是賦值了一個App實例。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
尋找app類別的實例化

接著來到App類別可以看到繼承的是Container類別。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
繼承類別

在容器這塊已經不只一次的說過這塊的知識點了,訪問不存在的屬性回去執行容器的__get魔術方法。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
魔術方法存取不存在的屬性會執行

所以說這塊的參數會傳入hook,並且會傳回hook的實例,關於這個實例是怎麼回傳的在容器那一節中說的很是詳細,可以去看一下哈!

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
傳回hook的實例

接下來就會去執行hook的listen方法,監聽標籤的行為。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
監聽標籤的行為

此時可以來到應用行為擴充定義文件,可以看到這個參數為模組初始化,但因為這個值是空的。

所以在上圖不會去執行,那麼就把應用初始化的值給放到這個參數裡邊進行簡單的測試。

這個類別就是執行的鉤子,對門面類別的最佳化操作。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
應用程式行為擴充定義檔

那麼程式碼就會執行到$results[$key] = $this->execTag($name, $ tag, $params);這裡來。

參數說明

  • $name = string(22) "behavior\LoadBehavior"
  • $tag = module_init

接著透過正規對傳過來的參數處理,最後傳回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和直接執行魔術存取返回實例的差異

有一部分小夥伴都已經學會了ArrayAccess和魔術方法__get的使用了。

估計也有一部分在這兩個地方處於模糊地段,咔咔將這兩個放在一起在解析一次。

先聊聊ArrayAccess的使用

這個案例在之前也給大家示範過,主要就是實作ArrayAccess的這個類別。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
這是先前寫的測試方法

然後來到控制器進行使用,先進行實例化,之前實作的案例如下。

但這次需要實現的案例並不是下圖所實現的。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
控制器進行使用

接下來使用下圖的方式進行訪問,直接使用陣列存取物件屬性。

在上圖中可以看到設定了一個屬性title為kaka,在這個案例中直接用陣列形式直接取得。

看到回傳結果為kaka,也就是說直接使用陣列形式存取物件的屬性。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
新的訪問訪問
ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
列印結果

總結

#在第一次案例的實作過程中,忽略了一步,就是使用物件直接以陣列形式直接存取物件的屬性。

可以看到的是可以直接取得到的,那麼接下來將這個想法套到框架中在來看一下。

框架實戰案例

在上一期文章中解析的路由中存在以下程式碼,接下來進行簡單的解析一下。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
路由類別

先來看看這個app的值印出來就是think\App Object物件。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
列印的app值

think\App Object這個物件去存取request#時,因為app屬性就沒有這個request,又因為app類別是繼承container類,所以會去容器類別執行下圖方法。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
執行此方法

然後就會去執行__get方法,執行make方法傳回對應的實例。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別返回实例对象
執行此方法傳回實例物件

此時你要是還有疑問就是,怎麼就咔咔說會執行就會執行呢!

接下來咔咔帶著大家做一個簡單的測試就知道了。

在這個位置中隨機列印一個數值。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
測試案例

然後來到容器類別的ArrayAccess的offsetGet方法中列印一下傳過來的值。

看一下列印結果,就很明確了。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
在這裡插入圖片描述
ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
#列印結果

關於ArrayAccess的使用就到這裡就結束了,這也是在先前的基礎上詳細的進行了一次說明,接下來對容器中的__get方法進行詳解,看在什麼情況會執行__get方法。

__get方法使用詳解

這個案例請看下圖中的這個$this->hook

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
初始化應用程式

同樣的道理先來調試這個$this是什麼值。

列印這個值都沒什麼必要,因為就是在這個類別中。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
列印結果

在類別中屬性的存取應該都會,就是直接使用$this-> 即可。

所以說當系統存取$this->hook這個的時候,由於App類別是不存在hook這個屬性的,所以就會去執行容器類別的魔術方法。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
魔術方法

然後在去執行make方法,建立類別的實例。

ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別
建立類別的實例

總結

#

所以說是用ArrayAccess和__get魔術方法,最後都是執行的make方法回傳類別的實例。

當遇到this->config就是執行的容器的__get方法。

當遇到app['request']就是執行的ArrayAccess然後執行offsetGet

  • #__get是針對類別的屬性,當類別的屬性不存在時會執行
  • ArrayAccess當是用實例化好的類別以陣列形式存取時,如果不存在就會執行offsetGet這個方法。

以上是ThinkPHP關於ArrayAccess和直接執行魔術存取傳回實例的區別的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn