成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Laravel學(xué)習(xí)筆記之Container源碼解析

huayeluoliuhen / 3353人閱讀

摘要:實(shí)際上的綁定主要有三種方式且只是一種的,這些已經(jīng)在學(xué)習(xí)筆記之實(shí)例化源碼解析聊過,其實(shí)現(xiàn)方法并不復(fù)雜。從以上源碼發(fā)現(xiàn)的反射是個(gè)很好用的技術(shù),這里給出個(gè),看下能干些啥打印結(jié)果太長了,就不粘貼了。

說明:本文主要學(xué)習(xí)Laravel中Container的源碼,主要學(xué)習(xí)Container的綁定和解析過程,和解析過程中的依賴解決。分享自己的研究心得,希望對(duì)別人有所幫助。實(shí)際上Container的綁定主要有三種方式:bind(),singleton(),instance(),且singleton()只是一種"shared" = true的bind(),這些已經(jīng)在Laravel學(xué)習(xí)筆記之IoC Container實(shí)例化源碼解析聊過,其實(shí)現(xiàn)方法并不復(fù)雜。當(dāng)Service通過Service Provider綁定到Container中后,當(dāng)需要該Service時(shí),是需要Container幫助自動(dòng)解析make()。OK,下面聊聊自動(dòng)解析過程,研究下Container是如何在自動(dòng)解析Service時(shí)解決該Service的依賴問題的。

開發(fā)環(huán)境: Laravel5.3 + PHP7 + OS X 10.11

PHPUnit測試下綁定

在聊解析過程前,先測試下IlluminateContainerContainer中綁定的源碼,這里測試下bind(),singleton(),instance()三個(gè)綁定方式:

container = new Container();
    }

    public function testBindClosure()
    {
        // Arrange
        $expected = "Laravel is a PHP Framework.";
        $this->container->bind("PHP", function () use ($expected) {
            return $expected;
        });

        // Actual
        $actual = $this->container->make("PHP");

        // Assert
        $this->assertEquals($expected, $actual);
    }

    public function testBindInterfaceToImplement()
    {
        // Arrange
        $this->container->bind(IContainerStub::class, ContainerImplementationStub::class);

        // Actual
        $actual = $this->container->make(IContainerStub::class);

        // Assert
        $this->assertInstanceOf(IContainerStub::class, $actual);
    }

    public function testBindDependencyResolution()
    {
        // Arrange
        $this->container->bind(IContainerStub::class, ContainerImplementationStub::class);

        // Actual
        $actual = $this->container->make(ContainerNestedDependentStub::class);

        // Assert
        $this->assertInstanceOf(ContainerDependentStub::class, $actual->containerDependentStub);
        $this->assertInstanceOf(ContainerImplementationStub::class, $actual->containerDependentStub->containerStub);
    }

    public function testSingleton()
    {
        // Arrange
        $this->container->singleton(ContainerConcreteStub::class);
        $expected = $this->container->make(ContainerConcreteStub::class);

        // Actual
        $actual = $this->container->make(ContainerConcreteStub::class);

        // Assert
        $this->assertSame($expected, $actual);
    }

    public function testInstanceExistingObject()
    {
        // Arrange
        $expected = new ContainerImplementationStub();
        $this->container->instance(IContainerStub::class, $expected);

        // Actual
        $actual = $this->container->make(IContainerStub::class);

        // Assert
        $this->assertSame($expected, $actual);
    }
}

class ContainerConcreteStub
{

}

interface IContainerStub
{

}

class ContainerImplementationStub implements IContainerStub
{

}

class ContainerDependentStub
{
    /**
     * @var MyRightCapitalContainerTestsIContainerStub
     */
    public $containerStub;

    public function __construct(IContainerStub $containerStub)
    {
        $this->containerStub = $containerStub;
    }
}

class ContainerNestedDependentStub
{
    /**
     * @var MyRightCapitalContainerTestsContainerDependentStub
     */
    public $containerDependentStub;

    public function __construct(ContainerDependentStub $containerDependentStub)
    {
        $this->containerDependentStub = $containerDependentStub;
    }
}

這里測試了bind()綁定閉包,綁定接口和對(duì)應(yīng)實(shí)現(xiàn),依賴解析這三個(gè)feature,singleton()測試了是否為單例綁定一個(gè)feature,instance()測試了已存在對(duì)象綁定這個(gè)feature,測試結(jié)果5個(gè)tests都通過:

關(guān)于在PHPStorm中配置PHPUnit可參考這篇:Laravel學(xué)習(xí)筆記之基于PHPStorm編輯器的Laravel開發(fā)

make()源碼解析

從以上testcase知道,make()是負(fù)責(zé)從Container中解析出service的,而且在testBindDependencyResolution()這個(gè)test中,還能發(fā)現(xiàn)當(dāng)ContainerNestedDependentStub::class有構(gòu)造依賴時(shí),Container也會(huì)自動(dòng)去解析這個(gè)依賴并注入ContainerNestedDependentStub::class的構(gòu)造函數(shù)中,這個(gè)依賴是ContainerDependentStub::class,而這個(gè)依賴又有自己的依賴IContainerStub::class,從斷言語句$this->assertInstanceOf(ContainerImplementationStub::class, $actual->containerDependentStub->containerStub);知道,Container又自動(dòng)解析了這個(gè)依賴,所有這一切都不需要我們手動(dòng)去解析,全都是Container自動(dòng)化解析的。

這一切Container是怎么做到的?
實(shí)際上并不復(fù)雜,解決依賴只是用了PHP的Reflector反射機(jī)制來實(shí)現(xiàn)的。先看下make()源碼:

    /**
     * Resolve the given type from the container.
     *
     * @param  string  $abstract
     * @param  array   $parameters
     * @return mixed
     */
    public function make($abstract, array $parameters = [])
    {
        $abstract = $this->getAlias($this->normalize($abstract));

        // 如果是instance()綁定的方式,就直接解析返回綁定的service
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        // 獲取$abstract對(duì)應(yīng)綁定的$concrete
        $concrete = $this->getConcrete($abstract);

        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete, $parameters);
        } else {
            $object = $this->make($concrete, $parameters);
        }

        foreach ($this->getExtenders($abstract) as $extender) {
            $object = $extender($object, $this);
        }

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

        $this->fireResolvingCallbacks($abstract, $object);

        $this->resolved[$abstract] = true;

        return $object;
    }
    
    protected function getConcrete($abstract)
    {
        if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
            return $concrete;
        }

        // 如果是$this->container->singleton(ContainerConcreteStub::class);這種方式綁定,即$concrete = null
        // 則 $abstract = $concrete,可看以上PHPUnit的testSingleton()這個(gè)test
        // 這種方式稱為"自動(dòng)補(bǔ)全"綁定
        if (! isset($this->bindings[$abstract])) {
            return $abstract;
        }

        return $this->bindings[$abstract]["concrete"];
    }
    
    protected function isBuildable($concrete, $abstract)
    {
        return $concrete === $abstract || $concrete instanceof Closure;
    }

從以上源碼可知道如果綁定的是閉包或者"自動(dòng)補(bǔ)全"綁定($concrete = null),則需要build()這個(gè)閉包或類名,轉(zhuǎn)換成對(duì)應(yīng)的實(shí)例。如果是"接口實(shí)現(xiàn)"這種方式綁定,則需要再一次調(diào)用make()并經(jīng)過getConcrete后$abstract = $concrete,然后符合isBuildable()的條件,進(jìn)入build()函數(shù)內(nèi)。所以以上的PHPUnit的測試用例中不管什么方式的綁定,都要進(jìn)入build()函數(shù)內(nèi)編譯出相應(yīng)對(duì)象實(shí)例。當(dāng)編譯出對(duì)象后,檢查是否是共享的,以及是否要觸發(fā)回調(diào),以及標(biāo)記該對(duì)象已經(jīng)被解析。OK,看下build()的源碼:

    /**
     * Instantiate a concrete instance of the given type.
     *
     * @param  string  $concrete
     * @param  array   $parameters
     * @return mixed
     *
     * @throws IlluminateContractsContainerBindingResolutionException
     */
    public function build($concrete, array $parameters = [])
    {
        // 如果是閉包直接執(zhí)行閉包并返回,e.g. PHPUnit的這個(gè)test:testBindClosure()
        if ($concrete instanceof Closure) {
            return $concrete($this, $parameters);
        }
        
        // 如這個(gè)test:testBindInterfaceToImplement(),這里的$concrete = ContainerImplementationStub::class類名稱,
        // 則使用反射ReflectionClass來探測ContainerImplementationStub這個(gè)類的構(gòu)造函數(shù)和構(gòu)造函數(shù)的依賴
        $reflector = new ReflectionClass($concrete);

        // 如果ContainerImplementationStub不能實(shí)例化,這應(yīng)該是接口或抽象類,再或者就是ContainerImplementationStub的構(gòu)造函數(shù)是private的
        if (! $reflector->isInstantiable()) {
            if (! empty($this->buildStack)) {
                $previous = implode(", ", $this->buildStack);

                $message = "Target [$concrete] is not instantiable while building [$previous].";
            } else {
                $message = "Target [$concrete] is not instantiable.";
            }

            throw new BindingResolutionException($message);
        }

        $this->buildStack[] = $concrete;

        // 獲取構(gòu)造函數(shù)的反射
        $constructor = $reflector->getConstructor();

        // 如果構(gòu)造函數(shù)是空,說明沒有任何依賴,直接new返回
        if (is_null($constructor)) {
            array_pop($this->buildStack);

            return new $concrete;
        }
        
        // 獲取構(gòu)造函數(shù)的依賴,返回ReflectionParameter[]
        $dependencies = $constructor->getParameters();

        $parameters = $this->keyParametersByArgument(
            $dependencies, $parameters
        );

        // 然后就是獲取相關(guān)依賴,如testBindDependencyResolution()這個(gè)test中,
        // ContainerNestedDependentStub::class是依賴于ContainerDependentStub::class的
        $instances = $this->getDependencies(
            $dependencies, $parameters
        );

        array_pop($this->buildStack);

        return $reflector->newInstanceArgs($instances);
    }

從源碼可知道,比較麻煩的是當(dāng)ContainerNestedDependentStub::class的構(gòu)造函數(shù)有依賴ContainerDependentStub::class時(shí),通過getDependencies()來解決的,看下getDependencies()的源碼:

    // 這里$parameters = ReflectionParameter[]
    protected function getDependencies(array $parameters, array $primitives = [])
    {
        $dependencies = [];

        foreach ($parameters as $parameter) {
            $dependency = $parameter->getClass();

            // 如果某一依賴值已給,就賦值
            if (array_key_exists($parameter->name, $primitives)) {
                $dependencies[] = $primitives[$parameter->name];
            } 
            // 如果類名為null,說明是基本類型,如"int","string" and so on.
            elseif (is_null($dependency)) {
                $dependencies[] = $this->resolveNonClass($parameter);
            } 
            // 如果是類名,如ContainerDependentStub::class,則resolveClass去解析成對(duì)象
            else {
                $dependencies[] = $this->resolveClass($parameter);
            }
        }

        return $dependencies;
    }

通過上面注釋,看下resolveClass()的源碼:

    protected function resolveClass(ReflectionParameter $parameter)
    {
        try {
            // $parameter->getClass()->name返回的是類名,如ContainerNestedDependentStub依賴于$containerDependentStub
            // $containerDependentStub的typehint是ContainerDependentStub,所以類名是"ContainerDependentStub"
            // 然后遞歸繼續(xù)make(ContainerDependentStub::class)
            // 又和PHPUnit中這個(gè)測試$this->container->make(ContainerNestedDependentStub::class)相類似了
            // ContainerNestedDependentStub又依賴于IContainerStub::class,
            // IContainerStub::class是綁定于ContainerImplementationStub::class
            // 直到ContainerImplementationStub沒有依賴或者是構(gòu)造函數(shù)是基本屬性,
            // 最后build()結(jié)束
            return $this->make($parameter->getClass()->name);
        } catch (BindingResolutionException $e) {
            if ($parameter->isOptional()) {
                return $parameter->getDefaultValue();
            }

            throw $e;
        }
    }

從以上代碼注釋直到build()是個(gè)遞歸過程,A類依賴于B類,B類依賴于C類和D類,那就從A類開始build,發(fā)現(xiàn)依賴于B類,再從Container中解析make()即再build()出B類,發(fā)現(xiàn)依賴于C類,再make() and build(),發(fā)現(xiàn)B類又同時(shí)依賴于D類,再make() and build(),以此類推直到?jīng)]有依賴或依賴基本屬性,解析結(jié)束。這樣一步步解析完后,發(fā)現(xiàn)Container的解析make()并不是很神秘很復(fù)雜中的過程。

從以上源碼發(fā)現(xiàn)PHP的反射Reflector是個(gè)很好用的技術(shù),這里給出個(gè)test,看下Reflector能干些啥:

request = $request;
    }

    private function reflectorMethod1()
    {
    }

    protected function reflectorMethod2()
    {
    }

    public function reflectorMethod3()
    {
    }
}

$reflector_class        = new ReflectionClass(ReflectorTest::class);
$methods                = $reflector_class->getMethods();
$properties             = $reflector_class->getProperties();
$constructor            = $reflector_class->getConstructor();
$constructor_parameters = $constructor->getParameters();

foreach ($constructor_parameters as $constructor_parameter) {
    $dependency = $constructor_parameter->getClass();
    var_dump($dependency);

    if ($constructor_parameter->isDefaultValueAvailable()) {
        var_dump($constructor_parameter->getDefaultValue());
    }
}

var_dump($methods);
var_dump($properties);
var_dump($constructor);
var_dump($constructor_parameters);

打印結(jié)果太長了,就不粘貼了。可以看下PHP官方文檔:Reflector

總結(jié):本文學(xué)習(xí)了下Container的核心功能:service resolve的過程,并學(xué)習(xí)了service的依賴是如何被自動(dòng)解析的。遇到好的心得再分享,到時(shí)見。

歡迎關(guān)注Laravel-China。

RightCapital招聘Laravel DevOps

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/30466.html

相關(guān)文章

  • Laravel學(xué)習(xí)筆記Middleware源碼解析

    摘要:學(xué)習(xí)筆記之已經(jīng)聊過使用了來設(shè)計(jì),看源碼發(fā)現(xiàn)其巧妙用了和的一些數(shù)組函數(shù)來設(shè)計(jì)。開發(fā)環(huán)境內(nèi)置函數(shù)和看源碼之前,先看下這幾個(gè)內(nèi)置函數(shù)的使用。學(xué)習(xí)筆記之實(shí)例化源碼解析已經(jīng)聊過的實(shí)例化,得到中的變量,即的實(shí)例化對(duì)象。后面再學(xué)習(xí)下的源碼,到時(shí)見。 說明:本文主要學(xué)習(xí)Laravel的Middleware的源碼設(shè)計(jì)思想,并將學(xué)習(xí)心得分享出來,希望對(duì)別人有所幫助。Laravel學(xué)習(xí)筆記之Decorato...

    _Dreams 評(píng)論0 收藏0
  • Laravel學(xué)習(xí)筆記Query Builder源碼解析(上)

    摘要:說明本文主要學(xué)習(xí)模塊的源碼。這里,就已經(jīng)得到了鏈接器實(shí)例了,該中還裝著一個(gè),下文在其使用時(shí)再聊下其具體連接邏輯。 說明:本文主要學(xué)習(xí)Laravel Database模塊的Query Builder源碼。實(shí)際上,Laravel通過Schema Builder來設(shè)計(jì)數(shù)據(jù)庫,通過Query Builder來CURD數(shù)據(jù)庫。Query Builder并不復(fù)雜或神秘,只是在PDO擴(kuò)展的基礎(chǔ)上又開...

    Steve_Wang_ 評(píng)論0 收藏0
  • Laravel學(xué)習(xí)筆記IoC Container實(shí)例化源碼解析

    摘要:說明本文主要學(xué)習(xí)容器的實(shí)例化過程,主要包括等四個(gè)過程??聪碌脑创a如果是數(shù)組,抽取別名并且注冊(cè)到中,上文已經(jīng)討論實(shí)際上就是的。 說明:本文主要學(xué)習(xí)Laravel容器的實(shí)例化過程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the ...

    ningwang 評(píng)論0 收藏0
  • Laravel學(xué)習(xí)筆記bootstrap源碼解析

    摘要:總結(jié)本文主要學(xué)習(xí)了啟動(dòng)時(shí)做的七步準(zhǔn)備工作環(huán)境檢測配置加載日志配置異常處理注冊(cè)注冊(cè)啟動(dòng)。 說明:Laravel在把Request通過管道Pipeline送入中間件Middleware和路由Router之前,還做了程序的啟動(dòng)Bootstrap工作,本文主要學(xué)習(xí)相關(guān)源碼,看看Laravel啟動(dòng)程序做了哪些具體工作,并將個(gè)人的研究心得分享出來,希望對(duì)別人有所幫助。Laravel在入口index...

    xiaoxiaozi 評(píng)論0 收藏0
  • Laravel學(xué)習(xí)筆記Filesystem源碼解析(上)

    摘要:說明本文主要學(xué)習(xí)的模塊的源碼邏輯,把自己的一點(diǎn)點(diǎn)研究心得分享出來,希望對(duì)別人有所幫助。實(shí)際上,使用了的重載學(xué)習(xí)筆記之重載,通過魔術(shù)方法調(diào)用里的,而這個(gè)實(shí)際上就是,該中有方法,可以調(diào)用。 說明:本文主要學(xué)習(xí)Laravel的Filesystem模塊的源碼邏輯,把自己的一點(diǎn)點(diǎn)研究心得分享出來,希望對(duì)別人有所幫助??偟膩碚f,F(xiàn)ilesystem模塊的源碼也比較簡單,Laravel的Illumi...

    AlphaGooo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<