溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ThinkPHP容器的示例分析

發(fā)布時間:2021-09-18 14:03:50 來源:億速云 閱讀:149 作者:小新 欄目:編程語言

這篇文章主要為大家展示了“ThinkPHP容器的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“ThinkPHP容器的示例分析”這篇文章吧。

一、單例模式

在學(xué)習(xí)容器以及門面之前需要必須了解的倆個設(shè)計模式,單例模式、注冊樹模式。

先對單例模式做一個簡單的說明。

  • 擁有一個構(gòu)造函數(shù),并且屬性為private
  • 擁有一個靜態(tài)成員變量來保存類的實例
  • 擁有一個靜態(tài)方法來訪問這個實例

以下就是實現(xiàn)的一個簡單的單例模式,對照一下上面的三大特性看是否一致。

靜態(tài)變量為instance

擁有構(gòu)造并且還是私有的

最后一個就是有一個getInstance這個靜態(tài)方法

ThinkPHP容器的示例分析接下來進(jìn)行一下簡單的測試

還是在index控制器中做測試,為了證實其類只被實例化過一次,調(diào)用了其四次

ThinkPHP容器的示例分析訪問這個方法來看一下

new-class只執(zhí)行了一次,就直接證明了創(chuàng)建的類只實例化了一次。ThinkPHP容器的示例分析在這里咔咔之前有過一個疑問就是,這里的構(gòu)造函數(shù)為什么要使用私有的屬性。

你之前有過這個疑問嗎?咔咔帶你一起來解答一下

在本類定義私有屬性的構(gòu)造方法是為了防止其類在外部被實例化。

當(dāng)在外部實例化這個類就會報下圖的錯。

ThinkPHP容器的示例分析那么為什么會在這里提一嘴單例模式呢!是因為在接下來的學(xué)習(xí)容器的源碼中會使用到

例如下圖thinkphp/library/think/Container.php類中就存在一個獲取當(dāng)前容器的實例。

ThinkPHP容器的示例分析截止到這里單例模式就簡單的了解完了,了解單例模式也是為了更好的理解容器。

 

二、注冊樹模式

為什么在這里說這個注冊樹模式,因為在框架中注冊樹模式就是一個主導(dǎo)位置,所以必須去了解它!

那什么是注冊樹模呢!

  • 注冊樹模式就是將對象實例注冊到一顆樹上(這里的樹可不是真的樹啊!就是注冊到一個全局的屬性里邊)
  • 然后可以通過內(nèi)部方法從全局的樹上獲取對應(yīng)的對象實例。

這樣說的話肯定也不能更好的理解,接下來咔咔帶大家看一個簡單的案例來簡單的了解一下。

一個注冊樹模式需要的東西就是四個,注冊樹的池子,將對象掛載到注冊池里,從注冊池里獲取對象,從注冊池里卸載對象。

如下圖是咔咔寫的一個簡單的注冊樹模式。

代碼如果看不懂的就需要去補(bǔ)補(bǔ)基礎(chǔ)了哈!

ThinkPHP容器的示例分析ThinkPHP容器的示例分析接下來在到同一目錄創(chuàng)建一個TestTree文件

ThinkPHP容器的示例分析來到控制器測試寫的注冊樹模式是否有問題

在做測試的時候一定要注意命名空間問題哈!這里的kaka目錄是之前在類的自動加載那里配置的,如有不會的可以去第一期文章查看。

這里就相當(dāng)于先把TestTree這個類實例化出來

然后使用注冊樹模式把這個實例注冊到object樹池子中

最后使用get方式將這個類獲取出來就可以直接調(diào)用TestTree中的方法了。

ThinkPHP容器的示例分析最后看一下最終打印結(jié)果,結(jié)果就是TestTree類中g(shù)etTreeContent方法的返回值。

ThinkPHP容器的示例分析注冊樹模式就是以上咔咔說明的這些內(nèi)容,就是不去針對源碼學(xué)習(xí),這些內(nèi)容也是我們必須要去學(xué)會使用的。

 

三、如何理解控制反轉(zhuǎn)和依賴注入

其實這倆個就是指的一個東西,就是一種編程思想而已,不要想的那么難以理解和高大上。

那么什么是容器,容器直面理解就是裝東西的東西。在編程中,我們常見的變量、對象屬性都是一個容器。一個容器里邊能夠裝什么,完全取決于對該容器的定義。

然而現(xiàn)在我們討論的是另外一種容器,它存儲的既不是文本、數(shù)值,而是對象、類、接口通過這種容器,得以實現(xiàn)很多高級功能,最常用的就是代碼之間的解耦、依賴注入。

那么為什么會存在倆種概念,為什么要說控制反轉(zhuǎn)和依賴注入呢!在上文也提到過,它們其實指的就是一種東西,只是描述的角度不同而已。

就跟你是爸爸的兒子,你還是你爺爺?shù)膶O子,不管兒子還是孫子都指的是一個人。只是站在不同的角度看待問題而已。

控制反轉(zhuǎn)

是站在容器的角度看待問題,容器控制著應(yīng)用程序,由容器反向的向應(yīng)用程序注入應(yīng)用程序需要的外部資源。

依賴注入

是站在應(yīng)用程序的角度看待問題,應(yīng)用程序依賴容器創(chuàng)建并注入它所需要的外部資源。

作用

主要用來減少代碼之間的耦合程度。

有效的分離對象和應(yīng)用程序所需要的外部資源。

下面?zhèn)z幅圖就可以很清晰的說明問題

ThinkPHP容器的示例分析ThinkPHP容器的示例分析

給大家整一個簡單的案例

定義倆個類分別為Person、Car,在Person中實例并調(diào)用Car中的pay方法。

ThinkPHP容器的示例分析然后在控制器中調(diào)用,并且打印結(jié)果肯定就是Car返回的123,這個就不去打印了。

ThinkPHP容器的示例分析  
在這里插入圖片描述

那這個時候我們把代碼修改一下,把Car類直接傳給Person類,在Person類中直接用傳過來的對象去調(diào)用對應(yīng)的方法。

ThinkPHP容器的示例分析這只是一個簡單的實現(xiàn)過程,為了給閱讀框架容器代碼做一個鋪墊,在后文中會詳細(xì)說明框架中的容器注入。

 

四、必會反射機(jī)制

不知道大家有沒有了解過GO的反射機(jī)制,咔咔在當(dāng)時看了go的反射機(jī)制后說實話有點暈乎乎的。

但是在后來看了PHP的反射之后,不僅對go的反射有了一定的深入了解,并且對于PHP的反射也是更好的理解。

反射這一概念是在PHP5.0被引出來的,在目前使用的框架中咔咔知道的就有thinkphp和laravel都使用了反射來實現(xiàn)依賴注入。

對于反射的理解:其實就是從根獲取根以外的東西,放在編程中講就是只要知道一個類就可以知道這個類所有的屬性和方法。

案例

這只是一個簡單的實現(xiàn)案例,獲取類的全部方法和屬性。可以看下圖中的打印結(jié)果跟TestReflection是否一致。

ThinkPHP容器的示例分析ThinkPHP容器的示例分析

這個也從側(cè)面表現(xiàn)出現(xiàn)一個問題,就是會暴露出來一些本不應(yīng)該暴露出來的信息。

關(guān)于反射提供的接口還有很多,這里就介紹幾個常用的,其余的在框架源碼中解析。

使用反射執(zhí)行一個類的方法

打印出來的結(jié)果就是咔咔

ThinkPHP容器的示例分析使用反射執(zhí)行一個類中帶參數(shù)的方法

ThinkPHP容器的示例分析  
在這里插入圖片描述

使用反射執(zhí)行一個類中不帶參數(shù)的方法

ThinkPHP容器的示例分析其它的方法你們自己可以嘗試嘗試,因為這個反射的接口在平時基礎(chǔ)開發(fā)是不怎么用的,這咔咔給大家介紹的都是后邊在閱讀源碼都是可以用的到的。

既然了解到了反射,那么反射可以做什么事情呢!其中有一個功能點自動生成文檔。

反射到這里就簡單的了解一下,至于還想了解更多的接口使用可以去官方查看對應(yīng)的接口信息。

ThinkPHP容器的示例分析  
在這里插入圖片描述

在了解完反射之后就要開始進(jìn)入正題了,就需要正式進(jìn)入我們的容器環(huán)節(jié)了。只有上邊的基礎(chǔ)打好接下來的容器才能更好的理解。

 

五、玩轉(zhuǎn)自己的容器類

經(jīng)歷了九九八十一難終于來到了容器這一環(huán)節(jié),在這一環(huán)節(jié)我們先來實現(xiàn)一個自己的容器,將之前講解的單例模式、注冊樹模式、反射進(jìn)行一個串聯(lián),從而進(jìn)行加深印象和更好的理解。

還記得之前在依賴注入里邊說過這樣一個方法dependency,這個方法就是進(jìn)行了依賴注入,從而對代碼進(jìn)行解耦。

ThinkPHP容器的示例分析但是這次呢!會使用容器來解決這一問題。

首先先把需要的類定義好,這一個類就使用了單例模式和注冊樹模式,之前的文章沒有好好看的,一定要仔細(xì)看一下,否則后文會很難理解的。

ThinkPHP容器的示例分析  
在這里插入圖片描述

ThinkPHP容器的示例分析ThinkPHP容器的示例分析

<?php
/**
 * Created by PhpStorm.
 * User: 咔咔
 * Date: 2020/9/21
 * Time: 19:04
 */

namespace container;


class Container
{
    /**
     * 存放容器
     * @var array
     */
    public $instances = [];

    /**
     * 容器的對象實例
     * @var array
     */
    protected static $instance;

    /**
     * 定義一個私有的構(gòu)造函數(shù)防止外部類實例化
     * Container constructor.
     */
    private function __construct() {

    }

    /**
     * 獲取當(dāng)前容器的實例(單例模式)
     * @return array|Container
     */
    public static function getInstance ()
    {
        if(is_null(self::$instance)){
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function set ($key,$value)
    {
        return $this->instances[$key] = $value;
    }

    public function get ($key)
    {
        return $this->instances[$key];
    }
}
 

為了方便以后查看方便,這里把每節(jié)的案例演示都放在對應(yīng)的控制器中

這里把之前的依賴注入的代碼移植過來,并且配置上注解路由進(jìn)行訪問,看最終結(jié)果是否為Car方法返回的123

ThinkPHP容器的示例分析測試一下打印結(jié)果,一切ok

ThinkPHP容器的示例分析使用單例模式和注冊樹模式配合后修改的這份代碼

修改后打印出其結(jié)果,同樣也是car返回的值123。

在這里需要注意一下就是在同一個方法中set和get方法是不會共存的,這里只是為了給大家做一個演示寫到一起的。

后邊在看容器源碼時就知道set和get方法到底是怎么使用的,這里只是讓大家體驗一下單例模式和注冊樹模式。

ThinkPHP容器的示例分析這里做一個小修改,修改上文中最后倆行代碼

ThinkPHP容器的示例分析  
在這里插入圖片描述

ThinkPHP容器的示例分析場景二

此時我們把Person 的文件修改一下

添加一個構(gòu)造函數(shù),把參數(shù)使用構(gòu)造函數(shù)進(jìn)行賦值,在buy方法中就不需要在進(jìn)行傳遞參數(shù),只需要使用this->obj即可。

ThinkPHP容器的示例分析此時如果還是直接運行dependency路由就會報下邊一個錯,那是因為在Person中構(gòu)造函數(shù)有個參數(shù),的但是我們沒有傳。

ThinkPHP容器的示例分析此時就需要在修改一處,就是在實例化Person時把Car的實例當(dāng)參數(shù)給傳進(jìn)去就沒有任何問題了。

ThinkPHP容器的示例分析  
在這里插入圖片描述

但是你會發(fā)現(xiàn)上邊這都是什么代碼,本來簡簡單單的幾行代碼被復(fù)雜成這個樣子,這個時候就已經(jīng)弊大于利了,不管設(shè)計模式在好,盲目的使用對項目來說也是一種負(fù)擔(dān)。

所以這個時候反射就來了,反射在上文中也進(jìn)行簡單的介紹過,一定要看哈!文章都是一環(huán)套著一環(huán)的。

反射之戰(zhàn)優(yōu)化代碼

最終優(yōu)化完成的代碼就是這樣的,接下來對這段代碼進(jìn)行簡單的解析。

  • 在之前代碼的基礎(chǔ)上只修改了     kaka/container/Container.php這個類里邊的get方法
  • 判斷這個名person是否在容器中
  • 使用反射接口,然后獲取傳進(jìn)去person類的構(gòu)造方法
  • 如果person沒有構(gòu)造方法就直接返回person這個實例即可
  • 如存person在構(gòu)造函數(shù),則獲取person構(gòu)造函數(shù)的方法
  • 由于person類里邊的構(gòu)造函數(shù)的參數(shù)不會僅限于一個
  • 所以需要循環(huán)來獲取每個參數(shù)的對象
  • 最后使用反射的 newInstanceArgs接口創(chuàng)建對應(yīng)的實例
<?php
/**
 * Created by PhpStorm.
 * User: 咔咔
 * Date: 2020/9/21
 * Time: 19:04
 */

namespace container;


class Container
{
    /**
     * 存放容器
     * @var array
     */
    public $instances = [];

    /**
     * 容器的對象實例
     * @var array
     */
    protected static $instance;

    /**
     * 定義一個私有的構(gòu)造函數(shù)防止外部類實例化
     * Container constructor.
     */
    private function __construct() {

    }

    /**
     * 獲取當(dāng)前容器的實例(單例模式)
     * @return array|Container
     */
    public static function getInstance ()
    {
        if(is_null(self::$instance)){
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function set ($key,$value)
    {
        return $this->instances[$key] = $value;
    }

    /**
     * User : 咔咔
     * Notes: 獲取容器里邊的實例  使用反射
     * Time :2020/9/21 22:04
     * @param $key
     * @return mixed
     */
    public function get ($key)
    {
        if(!empty($this->instances[$key])){
            $key = $this->instances[$key];
        }

        $reflect = new \ReflectionClass($key);
        // 獲取類的構(gòu)造函數(shù)
        $c = $reflect->getConstructor();
        if(!$c){
            return new $key;
        }

        // 獲取構(gòu)造函數(shù)的參數(shù)
        $params = $c->getParameters();
        foreach ($params as $param) {
       /**
             ReflectionClass Object
            (
                [name] => container\dependency\Car
            )
             */
            $class = $param->getClass();
            if(!$class){

            }else{
                // container\dependency\Car
                $args[] = $this->get($class->name);
            }
        }
        // 從給出的參數(shù)創(chuàng)建一個新的類實例
        return $reflect->newInstanceArgs($args);
    }
}
 

ThinkPHP容器的示例分析文件application/index/controller/Container.php這里就是修改之后的變動

ThinkPHP容器的示例分析問題一:kaka/container/dependency/Person.php里邊的參數(shù)Car是什么意思

這個問題其實很簡單,你可以看到這個Car就是同目錄的Car.php文件。你就可以直接理解為同命名空間下的文件。

ThinkPHP容器的示例分析問題二:文件application/index/controller/Container.php為什么可以直接調(diào)用buy方法

首先看一下obj的值,返回的這個對象里邊就已經(jīng)把Car的類實例化好了,所以無需在實例化,可直接調(diào)用buy方法,因為參數(shù)會直接傳遞過去

ThinkPHP容器的示例分析  
在這里插入圖片描述

ThinkPHP容器的示例分析以上就是咔咔實現(xiàn)的一個簡單的容器,如有不明白或者問題可以直接評論區(qū)回復(fù)即可。

接下來就是針對框架里邊的容器進(jìn)行剖析,一步一步的追溯到根源。

 

六、Container容器類剖析之Countable巧用

關(guān)于Countable這塊內(nèi)容一直沒想好是否是文章的形式寫出展現(xiàn)給大家,但是在后期閱讀源碼時大量的出現(xiàn)了Countable的應(yīng)用。

為了大家能看懂每一個技術(shù)點,咔咔還是寫了出來。

在文件thinkphp/library/think/Container.php中,就可以直接看到使用了Countable接口,并且實現(xiàn)了它!

ThinkPHP容器的示例分析來到Countable這接口中,我們只能看到一個方法就是count().

根據(jù)代碼中Count elements of an object這行注釋可以了解到,這個接口是計算對象的元素

ThinkPHP容器的示例分析根據(jù)PHP文檔的說明在深入了解一下。

文檔說明當(dāng)你執(zhí)行count()方法時就相當(dāng)于在執(zhí)行上邊的abstract public Countable::count ( void ) : int抽象方法。

ThinkPHP容器的示例分析實戰(zhàn)案例

光說不干,事事落空;又說又干,馬到成功。直接開干

新建文件kaka/container/countableTest.php,并且添加以下內(nèi)容

ThinkPHP容器的示例分析接著在文件application/index/controller/Container.php中學(xué)會使用Countable。

這里注意一下用法,是直接使用count();

ThinkPHP容器的示例分析ThinkPHP容器的示例分析

Countable中的count()跟平時使用count()方法有什么區(qū)別

順便看一下PHP源碼中的解釋

可以看到第一個參數(shù)可以是數(shù)組也可是是countable

咔咔的理解是Countable只是重寫了SPL中的count方法,為了就是方便定制自己需要的統(tǒng)計規(guī)則而已。

int count ( mixed $array_or_countable [, int $mode = COUNT_NORMAL ] )
 

count你不知道的用法

既然說到了這里,咔咔給大家在普及一個count不是很常用的一個用法。

在平時開發(fā)的過程中,這樣的用法是最普遍的,也是大家最經(jīng)常見到的一個使用案例。

ThinkPHP容器的示例分析但是如果這時給你一個多維數(shù)組,例如下圖這樣,讓你統(tǒng)計這個多維數(shù)組,你該怎么統(tǒng)計呢!

這個時候估計大多數(shù)小伙伴的想法就是循環(huán)然后定義一個計數(shù)器累計。

其實count()函數(shù)在這一塊就已經(jīng)解決了這個需求。

下方打印結(jié)果就是"4----6"

直接使用count()函數(shù)一個數(shù)組得到的就是第一層數(shù)組的長度。

但是count()函數(shù)還有第二個參數(shù),設(shè)置為1就是遞歸地計數(shù)數(shù)組中元素的數(shù)目(計算多維數(shù)組中的所有元素)

所以你這時在去看文檔就會發(fā)現(xiàn),count()函數(shù)本身就有倆個參數(shù)

第一個參數(shù)是必須的,選擇是數(shù)組

第二個參數(shù)默認(rèn)是0就是不對多維數(shù)組中的所有元素進(jìn)行計數(shù)

當(dāng)?shù)诙€參數(shù)為1時就是遞歸的計算多維數(shù)組中的所有元素。

ThinkPHP容器的示例分析  
在這里插入圖片描述
 

七、Container容器類剖析

上文中實現(xiàn)了一個自己創(chuàng)建的容器,接下來看看源碼中的容器,經(jīng)過了上文容器中出現(xiàn)的技術(shù)點都已經(jīng)囊括完了。

在接下里閱讀容器源碼就不會很吃力,如果之前的文章沒看,一定要大概過一遍哈!

大家無數(shù)次打開的一個文件public/index.php。

曾有多少次打開這個文件想對源碼進(jìn)行一探究竟,但是看著看著就放棄了。

ThinkPHP容器的示例分析經(jīng)過之前的注冊樹模式之后,你肯定就會明白這行代碼會返回什么Container::get('app')

這行代碼返回就是app的實例,可以進(jìn)行簡單的斷點一下。

可以看到返回就是app類里邊的眾多屬性。

所以說注冊樹模式不會的在繼續(xù)返回去看之前寫的,要不越看越迷糊。

ThinkPHP容器的示例分析那么框架中的容器是怎么定義的呢!它到底是怎么實現(xiàn)的呢!

也就是只需要去關(guān)注這個get()方法做的事情就可以了。

ThinkPHP容器的示例分析代碼就會追蹤到文件thinkphp/library/think/Container.php中的get()方法

這里的getInstance()方法不陌生了吧!這就是上文說過的單例模式。

ThinkPHP容器的示例分析可以進(jìn)行代碼追蹤getInstance()這個方法,你就會在同文件中看到這個單例模式的方法,返回Container實例。

ThinkPHP容器的示例分析Container實例調(diào)用make方法

代碼static::getInstance()返回了Container的實例后,就會去調(diào)用本類的make方法,接下來就是對make方法進(jìn)行詳解了。

ThinkPHP容器的示例分析在開始閱讀make方法里邊的源碼之前,我們需要先對幾個屬性進(jìn)行簡單的梳理一下。

這四個屬性一定要有點印象,并且一定要區(qū)別instance和instances。

這倆個屬性一個是單例模式返回當(dāng)前類的實例,一個是容器中的所有的實例。

ThinkPHP容器的示例分析第一次執(zhí)行結(jié)果

   /**
     * 創(chuàng)建類的實例
     * @access public
     * @param  string        $abstract       類名或者標(biāo)識
     * @param  array|true    $vars           變量
     * @param  bool          $newInstance    是否每次創(chuàng)建新的實例
     * @return object
     */
    public function make($abstract, $vars = [], $newInstance = false)
    {
        // 判斷$vars這個變量是否為true
        if (true === $vars) {
            // 總是創(chuàng)建新的實例化對象
            $newInstance = true;
            $vars        = [];
        }

        // app  這里就是在容器別名里獲取傳遞過來的app    如果沒有則就是app
        $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
        
        // 從容器實例中獲取  如果存在則直接返回對應(yīng)的實例  也就是使用注冊樹模式
        if (isset($this->instances[$abstract]) && !$newInstance) {
            return $this->instances[$abstract];
        }

        // think\App 從容器標(biāo)識中獲取
        if (isset($this->bind[$abstract])) {
            // 將think\App 復(fù)制給$concrete變量
            $concrete = $this->bind[$abstract];
            // 用于代表匿名函數(shù)的類  判斷是不是閉包
            if ($concrete instanceof Closure) {
                $object = $this->invokeFunction($concrete, $vars);
            } else {
                // $this->name['app'] = think\App
                $this->name[$abstract] = $concrete;
                // 在執(zhí)行一次本類的make方法,也就是本方法
                return $this->make($concrete, $vars, $newInstance);
            }
        } else {
            $object = $this->invokeClass($abstract, $vars);
        }

        if (!$newInstance) {
            $this->instances[$abstract] = $object;
        }

        return $object;
    }
 

這是第二次執(zhí)行流程

    public function make($abstract, $vars = [], $newInstance = false)
    {
        // 判斷$vars這個變量是否為true
        if (true === $vars) {
            // 總是創(chuàng)建新的實例化對象
            $newInstance = true;
            $vars        = [];
        }

        // app  這里就是在容器別名里獲取傳遞過來的app    如果沒有則就是app
        // 第二次執(zhí)行時 $abstract = think\App
        $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;

        // 從容器實例中獲取  如果存在則直接返回對應(yīng)的實例  也就是使用注冊樹模式
        if (isset($this->instances[$abstract]) && !$newInstance) {
            return $this->instances[$abstract];
        }

        // think\App 從容器標(biāo)識中獲取
        // 第二次執(zhí)行$this->bind['think\App']不存在走else
        if (isset($this->bind[$abstract])) {
            // 將think\App 復(fù)制給$concrete變量
            $concrete = $this->bind[$abstract];
            // 用于代表匿名函數(shù)的類  判斷是不是閉包
            if ($concrete instanceof Closure) {
                $object = $this->invokeFunction($concrete, $vars);
            } else {
                // $this->name['app'] = think\App
                $this->name[$abstract] = $concrete;
                // 在執(zhí)行一次本類的make方法,也就是本方法
                // think\App
                return $this->make($concrete, $vars, $newInstance);
            }
        } else {
            // think\App
            $object = $this->invokeClass($abstract, $vars);
        }

        if (!$newInstance) {
            // 把創(chuàng)建的容器存起來
            //$this->instances['think\App'] = $object;
            $this->instances[$abstract] = $object;
        }

        return $object;
    }
 
public function invokeClass($class, $vars = [])
    {
        try {

            /**
             * ReflectionClass Object
                (
                [name] => think\App
                )
             */
            // 這里就是之前文章提到的反射
            $reflect = new ReflectionClass($class);


            if ($reflect->hasMethod('__make')) {
                $method = new ReflectionMethod($class, '__make');

                if ($method->isPublic() && $method->isStatic()) {
                    $args = $this->bindParams($method, $vars);
                    return $method->invokeArgs(null, $args);
                }
            }
            // 通過反射獲取think\App的構(gòu)造函數(shù)
            $constructor = $reflect->getConstructor();

            $args = $constructor ? $this->bindParams($constructor, $vars) : [];
            // 從給出的參數(shù)創(chuàng)建一個新的類實例
            return $reflect->newInstanceArgs($args);

        } catch (ReflectionException $e) {
            throw new ClassNotFoundException('class not exists: ' . $class, $class);
        }
    }
 

執(zhí)行流程圖

既然把代碼都理清楚了,這時來理一下執(zhí)行的流程圖可以看的更清晰。

ThinkPHP容器的示例分析invokeClass方法詳細(xì)解析

不管是閱讀完上邊的代碼流程,還是上圖的流程圖,肯定都知道了最終代碼會走向一個方法invokeClass,就是這個方法。

這個方法中全部都是利用反射的知識點,不會的在去看上文或者之前的文章吧!

invokeClass方法中,最重要的就是綁定參數(shù)的這個方法bindParams,這個方法里邊也全部運用的是反射。

所以在容器中反射起到的作用有多大就不用在去做過多的說明了。

在這之前需要把這塊說明一下,看到這個__make方法,咔咔是記憶尤深哈!

這個方法在之前學(xué)習(xí)config源碼配置那一篇文章中咔咔說暫時略過,因為當(dāng)時所儲備的知識點和框架代碼執(zhí)行流程還沒到說明__make這個方法的階段。

為了就是在容器這里詳細(xì)的說明__make這個方法的作用。

ThinkPHP容器的示例分析  
在這里插入圖片描述

當(dāng)你打印reflect這個變量的值時會返回倆個反射類的對象,如下圖。

ThinkPHP容器的示例分析代碼$reflect->hasMethod('__make')就是判斷此反射類里邊是否存在__make函數(shù)

代碼$method = new ReflectionMethod($class, '__make');就是執(zhí)行反射類的一個方法  這里就指的是__make方法

當(dāng)斷點這個method就會返回倆個存在__make反射類,這里是因為斷點了只有顯示了倆個反射類。

這里主要談?wù)搕hink\Config.

ThinkPHP容器的示例分析最后一行代碼$method->isPublic() && $method->isStatic()就是判斷方法是不是公公共的    判斷方法是不是靜態(tài)的

直到運行到$args = $this->bindParams($method, $vars);這行才會進(jìn)入到bindParams方法,這個方法也會在下文給出詳細(xì)的解析。

解析bindParams方法

接下來就解析一下bindParams這個方法。

關(guān)于參數(shù)傳遞的就是一個反射類   第二個參數(shù)暫時不做說明,目前還沒有遇到響應(yīng)的場景。

第一個參數(shù)值$reflect

ThinkPHP容器的示例分析  
在這里插入圖片描述

ThinkPHP容器的示例分析使用反射方法$reflect->getNumberOfParameters()獲取反射類中對應(yīng)的方法中的參數(shù)數(shù)目。按照上文的就是__make方法。容器代碼中只獲取過倆個方法的參數(shù)數(shù)目,一個是__make方法,一個是就是反射類中的構(gòu)造函數(shù)。

由于目前還沒有傳遞vars變量的場景,所以這塊的內(nèi)容暫時不去研究它直接略過。

代碼$params = $reflect->getParameters();也是使用反射獲取方法的參數(shù)。

打印出來可以看到的結(jié)果是倆組數(shù)據(jù)。

ThinkPHP容器的示例分析那么這這組數(shù)據(jù)是從哪里來的呢!往上翻一下,看一下$reflect這個參數(shù)是什么就明白了。

think\App這個反射類是沒有__make方法的,所以會獲取構(gòu)造函數(shù)中的參數(shù)。

ThinkPHP容器的示例分析然后think\Log反射類中存在__make方法,于是就會返回__make的參數(shù),如下圖。

ThinkPHP容器的示例分析  
在這里插入圖片描述

就像類似于think\Log這樣的類,既有__make方法,也存在構(gòu)造函數(shù),就會走倆次bindParams方法,這個應(yīng)該都明白,正是下圖邏輯。

ThinkPHP容器的示例分析在接下來就是循環(huán)反射類中獲取的參數(shù)。

獲取參數(shù)名、和獲取對應(yīng)的反射類

最后將獲取出來的反射類傳遞給getObjectParam方法。

ThinkPHP容器的示例分析在這個getObjectParam方法中并沒有多少內(nèi)容。

由于$vars從頭到尾都是空數(shù)組所以去除數(shù)組第一個的操作和判斷是否為閉包都不會執(zhí)行。

最終會在返回去執(zhí)行make方法

ThinkPHP容器的示例分析然后make方法會直接從容器中返回這個實例

ThinkPHP容器的示例分析當(dāng)一個反射類存在__make方法時,最終就會執(zhí)行return $method->invokeArgs(null, $args);,帶參數(shù)執(zhí)行反射類方法

ThinkPHP容器的示例分析使用容器來調(diào)用配置類

既然已經(jīng)把容器源碼讀了一次了,可不可以使用容器來實現(xiàn)呢!

那當(dāng)然是可以的了,這里需要注意一下咔咔的命名空間,這里由于為了以后回顧方便把類名也起成了Container了,所以給加了一個別名,你們在使用的時候是不需要的哈!

ThinkPHP容器的示例分析  
在這里插入圖片描述

截止到這里容器的源碼就講解的差不多了,后邊咔咔會做一個完整的流程圖,提供改大家查看。

 

八、容器源碼閱讀后總結(jié)

注冊模式

本文先從倆個設(shè)計模式開頭,分別為單例模式和注冊樹模式。

單例模式簡單理解就是在應(yīng)用程序聲明周期內(nèi)只會返回一個實例對象,不會再去創(chuàng)建新的對象。

注冊樹模式理解就是會把程序中使用的對象都會存放在一顆樹上,使用的時候直接從樹上獲取對象直接使用即可。

控制反轉(zhuǎn)依賴注入

控制反轉(zhuǎn)和依賴注入千萬不要讓名字把人虎住了,倆個看待一個事件的問題不同,一個是站在容器角度,一個是站在應(yīng)用程序角度。

從容器角度來看,容器控制著應(yīng)用程序,由容器反向的向應(yīng)用程序注入外部資源

從應(yīng)用程序的角度來看,應(yīng)用程序依賴容器創(chuàng)建并注入它所需的外部資源。

反射

反射沒有什么需要總結(jié)的,打開文檔看一下就明白了,重要的要學(xué)會使用并且知道各自什么意思學(xué)會靈活運用即可。

容器源碼解析

容器的源碼看完后你會發(fā)現(xiàn)用的東西就是上邊說的三個知識點形成的,運用注冊模式來對容器中的對象管理。

對于這個圖需要牢牢記住,在源碼中就使用的這四個屬性走來走去的。

ThinkPHP容器的示例分析  
在這里插入圖片描述

在一個就是代碼的執(zhí)行流程

ThinkPHP容器的示例分析  
在這里插入圖片描述

在容器中最重要的方法就是invokeClass和bindParams這倆個方法跟這咔咔的思路走就沒有什么問題,跟這斷點的流程一點一點執(zhí)行。

這塊看的時候估計有點繞,但是仔細(xì)看完之后你會發(fā)現(xiàn)可以學(xué)到很多東西。

以上是“ThinkPHP容器的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI