亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Laravel核心——服務(wù)容器的細(xì)節(jié)特性

AprilJ / 3399人閱讀

摘要:前言首先歡迎關(guān)注我的博客在前面幾個(gè)博客中,我詳細(xì)講了容器各個(gè)功能的使用綁定的源碼解析的源碼,今天這篇博客會(huì)詳細(xì)介紹容器的一些細(xì)節(jié),一些特性,以便更好地掌握容器的功能。

前言

首先歡迎關(guān)注我的博客: www.leoyang90.cn

在前面幾個(gè)博客中,我詳細(xì)講了 Ioc 容器各個(gè)功能的使用、綁定的源碼、解析的源碼,今天這篇博客會(huì)詳細(xì)介紹 Ioc 容器的一些細(xì)節(jié),一些特性,以便更好地掌握容器的功能。

注:本文使用的測(cè)試類與測(cè)試對(duì)象都取自 laravel 的單元測(cè)試文件src/illuminate/tests/Container/ContainerTest.php

rebind綁定特性 rebind 在綁定之前

instance 和 普通 bind 綁定一樣,當(dāng)重新綁定的時(shí)候都會(huì)調(diào)用 rebind 回調(diào)函數(shù),但是有趣的是,對(duì)于普通 bind 綁定來(lái)說(shuō),rebind 回調(diào)函數(shù)被調(diào)用的條件是當(dāng)前接口被解析過(guò):

public function testReboundListeners()
{
    unset($_SERVER["__test.rebind"]);

    $container = new Container;
    $container->rebinding("foo", function () {
        $_SERVER["__test.rebind"] = true;
    });
    $container->bind("foo", function () {
    });
    $container->make("foo");
    $container->bind("foo", function () {
    });

    $this->assertTrue($_SERVER["__test.rebind"]);
}

所以遇到下面這樣的情況,rebinding 的回調(diào)函數(shù)是不會(huì)調(diào)用的:

public function testReboundListeners()
{
    unset($_SERVER["__test.rebind"]);

    $container = new Container;
    $container->rebinding("foo", function () {
        $_SERVER["__test.rebind"] = true;
    });
    $container->bind("foo", function () {
    });
    $container->bind("foo", function () {
    });

    $this->assertFalse(isset($_SERVER["__test.rebind"]));
}

有趣的是對(duì)于 instance 綁定:

public function testReboundListeners()
{
    unset($_SERVER["__test.rebind"]);

    $container = new Container;
    $container->rebinding("foo", function () {
        $_SERVER["__test.rebind"] = true;
    });
    $container->bind("foo", function () {
    });
    $container->instance("foo", function () {
    });

    $this->assertTrue(isset($_SERVER["__test.rebind"]));
}

rebinding 回調(diào)函數(shù)卻是可以被調(diào)用的。其實(shí)原因就是 instance 源碼中 rebinding 回調(diào)函數(shù)調(diào)用的條件是 rebound 為真,而普通 bind 函數(shù)調(diào)用 rebinding 回調(diào)函數(shù)的條件是 resolved 為真. 目前筆者不是很清楚為什么要對(duì) instance 和 bind 區(qū)別對(duì)待,希望有大牛指導(dǎo)。

rebind 在綁定之后

為了使得 rebind 回調(diào)函數(shù)在下一次的綁定中被激活,在 rebind 函數(shù)的源碼中,如果判斷當(dāng)前對(duì)象已經(jīng)綁定過(guò),那么將會(huì)立即解析:

public function rebinding($abstract, Closure $callback)
{
    $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
    
    if ($this->bound($abstract)) {
        return $this->make($abstract);
    }
}

單元測(cè)試代碼:

public function testReboundListeners1()
{
    unset($_SERVER["__test.rebind"]);

    $container = new Container;
    $container->bind("foo", function () {
        return "foo";
    });

    $container->resolving("foo", function () {
        $_SERVER["__test.rebind"] = true;
    });

    $container->rebinding("foo", function ($container,$object) {//會(huì)立即解析
        $container["foobar"] = $object."bar";
    });

    $this->assertTrue($_SERVER["__test.rebind"]);

    $container->bind("foo", function () {
    });

    $this->assertEquals("bar", $container["foobar"]);
}
resolving 特性 resolving 回調(diào)的類型

resolving 不僅可以針對(duì)接口執(zhí)行回調(diào)函數(shù),還可以針對(duì)接口實(shí)現(xiàn)的類型進(jìn)行回調(diào)函數(shù)。

public function testResolvingCallbacksAreCalledForType()
{
    $container = new Container;
    $container->resolving("StdClass", function ($object) {
        return $object->name = "taylor";
    });
    $container->bind("foo", function () {
          return new StdClass;
    });
    $instance = $container->make("foo");

    $this->assertEquals("taylor", $instance->name);
}
public function testResolvingCallbacksShouldBeFiredWhenCalledWithAliases()
{
    $container = new Container;
    $container->alias("StdClass", "std");
    $container->resolving("std", function ($object) {
        return $object->name = "taylor";
    });
    $container->bind("foo", function () {
        return new StdClass;
    });
    $instance = $container->make("foo");

    $this->assertEquals("taylor", $instance->name);
}
resolving 回調(diào)與 instance

前面講過(guò),對(duì)于 singleton 綁定來(lái)說(shuō),resolving 回調(diào)函數(shù)僅僅運(yùn)行一次,只在 singleton 第一次解析的時(shí)候才會(huì)調(diào)用。如果我們利用 instance 直接綁定類的對(duì)象,不需要解析,那么 resolving 回調(diào)函數(shù)將不會(huì)被調(diào)用:

public function testResolvingCallbacksAreCalledForSpecificAbstracts()
{
    $container = new Container;
    $container->resolving("foo", function ($object) {
        return $object->name = "taylor";
    });
    $obj = new StdClass;
    $container->instance("foo", $obj);
    $instance = $container->make("foo");

    $this->assertFalse(isset($instance->name));
}
extend 擴(kuò)展特性

extend 用于擴(kuò)展綁定對(duì)象的功能,對(duì)于普通綁定來(lái)說(shuō),這個(gè)函數(shù)的位置很靈活:

在綁定前擴(kuò)展
public function testExtendIsLazyInitialized()
{
    ContainerLazyExtendStub::$initialized = false;
    
    $container = new Container;      
    $container->extend("IlluminateTestsContainerContainerLazyExtendStub", function ($obj, $container) {
        $obj->init();
        return $obj;   
    });    
    $container->bind("IlluminateTestsContainerContainerLazyExtendStub"); 

    $this->assertFalse(ContainerLazyExtendStub::$initialized);   
    $container->make("IlluminateTestsContainerContainerLazyExtendStub");   
    $this->assertTrue(ContainerLazyExtendStub::$initialized);
}
在綁定后解析前擴(kuò)展
public function testExtendIsLazyInitialized()
{
    ContainerLazyExtendStub::$initialized = false;
    
    $container = new Container;   
    $container->bind("IlluminateTestsContainerContainerLazyExtendStub");    
    $container->extend("IlluminateTestsContainerContainerLazyExtendStub", function ($obj, $container) {
        $obj->init();
        return $obj;   
    });    

    $this->assertFalse(ContainerLazyExtendStub::$initialized);   
    $container->make("IlluminateTestsContainerContainerLazyExtendStub");   
    $this->assertTrue(ContainerLazyExtendStub::$initialized);
}
在解析后擴(kuò)展
public function testExtendIsLazyInitialized()
{
    ContainerLazyExtendStub::$initialized = false;
    
    $container = new Container;   
    $container->bind("IlluminateTestsContainerContainerLazyExtendStub");         
    
    $container->make("IlluminateTestsContainerContainerLazyExtendStub"); 
    $this->assertFalse(ContainerLazyExtendStub::$initialized);
    
    $container->extend("IlluminateTestsContainerContainerLazyExtendStub", function ($obj, $container) {
        $obj->init();
        return $obj;   
    });
    $this->assertFalse(ContainerLazyExtendStub::$initialized);  
      
    $container->make("IlluminateTestsContainerContainerLazyExtendStub"); 
    $this->assertTrue(ContainerLazyExtendStub::$initialized);
}

可以看出,無(wú)論在哪個(gè)位置,extend 擴(kuò)展都有 lazy 初始化的特點(diǎn),也就是使用 extend 函數(shù)并不會(huì)立即起作用,而是要等到 make 解析才會(huì)激活。

extend 與 instance 綁定

對(duì)于 instance 綁定來(lái)說(shuō),暫時(shí) extend 的位置需要位于 instance 之后才會(huì)起作用,并且會(huì)立即起作用,沒有 lazy 的特點(diǎn):

public function testExtendInstancesArePreserved()
{
    $container = new Container;

    $obj = new StdClass;
    $obj->foo = "foo";
    $container->instance("foo", $obj);
    $container->extend("foo", function ($obj, $container) {
        $obj->bar = "baz";

        return $obj;
    });

    $this->assertEquals("foo", $container->make("foo")->foo);
    $this->assertEquals("baz", $container->make("foo")->bar);
}
extend 綁定與 rebind 回調(diào)

無(wú)論擴(kuò)展對(duì)象是 instance 綁定還是 bind 綁定,extend 都會(huì)啟動(dòng) rebind 回調(diào)函數(shù):

public function testExtendReBindingInstance()
{
    $_SERVER["_test_rebind"] = false;

    $container = new Container;
    $container->rebinding("foo",function (){
        $_SERVER["_test_rebind"] = true;
    });

    $obj = new StdClass;
    $container->instance("foo",$obj);

    $container->make("foo");

    $container->extend("foo", function ($obj, $container) {
        return $obj;
    });

    this->assertTrue($_SERVER["_test_rebind"]);
}

public function testExtendReBinding()
{
    $_SERVER["_test_rebind"] = false;

    $container = new Container;
    $container->rebinding("foo",function (){
        $_SERVER["_test_rebind"] = true;
    });
    $container->bind("foo",function (){
        $obj = new StdClass;

        return $obj;
    });

    $container->make("foo");

    $container->extend("foo", function ($obj, $container) {
        return $obj;
    });

    this->assertFalse($_SERVER["_test_rebind"]);
}
contextual 綁定特性 contextual 在綁定前

contextual 綁定不僅可以與 bind 綁定合作,相互不干擾,還可以與 instance 綁定相互合作。而且 instance 的位置也很靈活,可以在 contextual 綁定前,也可以在contextual 綁定后:

public function testContextualBindingWorksForExistingInstancedBindings()
{
    $container = new Container;

    $container->instance("IlluminateTestsContainerIContainerContractStub", new ContainerImplementationStub);

    $container->when("IlluminateTestsContainerContainerTestContextInjectOne")->needs("IlluminateTestsContainerIContainerContractStub")->give("IlluminateTestsContainerContainerImplementationStubTwo");

    $this->assertInstanceOf(
             "IlluminateTestsContainerContainerImplementationStubTwo",
             $container->make("IlluminateTestsContainerContainerTestContextInjectOne")->impl
     );
}
contextual 在綁定后
public function testContextualBindingWorksForNewlyInstancedBindings()
{
    $container = new Container;

    $container->when("IlluminateTestsContainerContainerTestContextInjectOne")->needs("IlluminateTestsContainerIContainerContractStub")->give("IlluminateTestsContainerContainerImplementationStubTwo");

    $container->instance("IlluminateTestsContainerIContainerContractStub", new ContainerImplementationStub);

    $this->assertInstanceOf(
            "IlluminateTestsContainerContainerImplementationStubTwo",
        $container->make("IlluminateTestsContainerContainerTestContextInjectOne")->impl
    );
}
contextual 綁定與別名

contextual 綁定也可以在別名上進(jìn)行,無(wú)論賦予別名的位置是 contextual 的前面還是后面:

public function testContextualBindingDoesntOverrideNonContextualResolution()
{
    $container = new Container;

    $container->instance("stub", new ContainerImplementationStub);
    $container->alias("stub", "IlluminateTestsContainerIContainerContractStub");

    $container->when("IlluminateTestsContainerContainerTestContextInjectTwo")->needs("IlluminateTestsContainerIContainerContractStub")->give("IlluminateTestsContainerContainerImplementationStubTwo");

    $this->assertInstanceOf(
            "IlluminateTestsContainerContainerImplementationStubTwo",
            $container->make("IlluminateTestsContainerContainerTestContextInjectTwo")->impl
        );

    $this->assertInstanceOf(
            "IlluminateTestsContainerContainerImplementationStub",
            $container->make("IlluminateTestsContainerContainerTestContextInjectOne")->impl
    );
}

public function testContextualBindingWorksOnNewAliasedBindings()
{
    $container = new Container;

    $container->when("IlluminateTestsContainerContainerTestContextInjectOne")->needs("IlluminateTestsContainerIContainerContractStub")->give("IlluminateTestsContainerContainerImplementationStubTwo");

    $container->bind("stub", ContainerImplementationStub::class);
    $container->alias("stub", "IlluminateTestsContainerIContainerContractStub");

    $this->assertInstanceOf(
          "IlluminateTestsContainerContainerImplementationStubTwo",
          $container->make("IlluminateTestsContainerContainerTestContextInjectOne")->impl
    );
}
爭(zhēng)議

目前比較有爭(zhēng)議的是下面的情況:

public function testContextualBindingWorksOnExistingAliasedInstances()
{
    $container = new Container;

    $container->alias("IlluminateTestsContainerIContainerContractStub", "stub");
    $container->instance("stub", new ContainerImplementationStub);

    $container->when("IlluminateTestsContainerContainerTestContextInjectOne")->needs("stub")->give("IlluminateTestsContainerContainerImplementationStubTwo");

    $this->assertInstanceOf(
        "IlluminateTestsContainerContainerImplementationStubTwo",
        $container->make("IlluminateTestsContainerContainerTestContextInjectOne")->impl
    ); 
}

由于instance的特性,當(dāng)別名被綁定到其他對(duì)象上時(shí),別名 stub 已經(jīng)失去了與 IlluminateTestsContainerIContainerContractStub 之間的關(guān)系,因此不能使用 stub 代替作上下文綁定。
但是另一方面:

public function testContextualBindingWorksOnBoundAlias()
{
    $container = new Container;

    $container->alias("IlluminateTestsContainerIContainerContractStub", "stub");
    $container->bind("stub", ContainerImplementationStub::class);

    $container->when("IlluminateTestsContainerContainerTestContextInjectOne")->needs("stub")->give("IlluminateTestsContainerContainerImplementationStubTwo");

    $this->assertInstanceOf(
        "IlluminateTestsContainerContainerImplementationStubTwo",
        $container->make("IlluminateTestsContainerContainerTestContextInjectOne")->impl
    ); 
}

代碼只是從 instance 綁定改為 bind 綁定,由于 bind 綁定只切斷了別名中的 alias 數(shù)組的聯(lián)系,并沒有斷絕abstractAlias數(shù)組的聯(lián)系,因此這段代碼卻可以通過(guò),很讓人難以理解。本人在給 Taylor Otwell 提出 PR 時(shí),作者原話為“I"m not making any of these changes to the container on a patch release.”。也許,在以后(5.5或以后)版本作者會(huì)更新這里的邏輯,我們就可以看看服務(wù)容器對(duì)別名綁定的態(tài)度了,大家也最好不要這樣用。

服務(wù)容器中的閉包函數(shù)參數(shù)

服務(wù)容器中很多函數(shù)都有閉包函數(shù),這些閉包函數(shù)可以放入特定的參數(shù),在綁定或者解析過(guò)程中,這些參數(shù)會(huì)被服務(wù)容器自動(dòng)帶入各種類對(duì)象或者服務(wù)容器實(shí)例。

bind 閉包參數(shù)
public function testAliasesWithArrayOfParameters()
{
    $container = new Container;    
    $container->bind("foo", function ($app, $config) {
        return $config;    
    });    

    $container->alias("foo", "baz");    
    $this->assertEquals([1, 2, 3], $container->makeWith("baz", [1, 2, 3]));
}
extend 閉包參數(shù)
public function testExtendedBindings()
{
    $container = new Container;    
    $container["foo"] = "foo’;    
    $container->extend("foo", function ($old, $container) {
        return $old."bar’;    
    });
   
    $this->assertEquals("foobar", $container->make("foo"));
    
    $container = new Container;
    
    $container->singleton("foo", function () {
        return (object) ["name" => "taylor"];    
    });    
    $container->extend("foo", function ($old, $container) {
        $old->age = 26;
        return $old;    
    });
    
    $result = $container->make("foo");
    $this->assertEquals("taylor", $result->name);    
    $this->assertEquals(26, $result->age);   
    $this->assertSame($result, $container->make("foo"));
}
bindmethod 閉包參數(shù)
public function testCallWithBoundMethod()
{
    $container = new Container;
    $container->bindMethod("IlluminateTestsContainerContainerTestCallStub@unresolvable", function ($stub,$container) {
        $container["foo"] = "foo";
        return $stub->unresolvable("foo", "bar");
    });
    $result = $container->call("IlluminateTestsContainerContainerTestCallStub@unresolvable");
    $this->assertEquals(["foo", "bar"], $result);
    $this->assertEquals("foo",$container["foo"]);
}
resolve 閉包參數(shù)
public function testResolvingCallbacksAreCalledForSpecificAbstracts()
{
     $container = new Container;
     $container->resolving("foo", function ($object,$container) {
         return $object->name = "taylor";
     });
 
     $container->bind("foo", function () {
        return new StdClass;
     });
     $instance = $container->make("foo");

     $this->assertEquals("taylor", $instance->name);
}
rebinding 閉包參數(shù)
public function testReboundListeners()
{
    $container = new Container;
    $container->bind("foo", function () {
        return "foo";
    });
  
    $container->rebinding("foo", function ($container,$object) {
         $container["bar"] = $object."bar";
    });
  
    $container->bind("foo", function () {
    });

    $this->assertEquals("bar",$container["foobar"]);
}

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

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

相關(guān)文章

  • 深入剖析 Laravel 服務(wù)容器

    摘要:劃下重點(diǎn),服務(wù)容器是用于管理類的依賴和執(zhí)行依賴注入的工具。類的實(shí)例化及其依賴的注入,完全由服務(wù)容器自動(dòng)的去完成。 本文首發(fā)于 深入剖析 Laravel 服務(wù)容器,轉(zhuǎn)載請(qǐng)注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請(qǐng)求,又是如何生成響應(yīng)并最終呈現(xiàn)給用戶的工作原理。 本章將帶領(lǐng)大...

    abson 評(píng)論0 收藏0
  • 為什么我們需要 Laravel IoC 容器?

    摘要:哲學(xué)的一個(gè)重要組成部分就是容器,也可以稱為服務(wù)容器。那我們要怎么做呢請(qǐng)看下面的例子數(shù)據(jù)庫(kù)連接通過(guò)上面的代碼,如果我們想把改成,根本不需要去修改類構(gòu)造函數(shù)里的依賴?,F(xiàn)在我要講下容器里到底發(fā)生了什么。 showImg(https://segmentfault.com/img/remote/1460000018868909); IOC 容器是一個(gè)實(shí)現(xiàn)依賴注入的便利機(jī)制 - Taylor?Ot...

    xiaokai 評(píng)論0 收藏0
  • Laravel 深入核心系列教程

    摘要:前言年底了不太忙,最近一段時(shí)間也一直在研究,就想寫篇關(guān)于比較深一點(diǎn)的教程系列啥的,于是就找到站長(zhǎng)給開了寫教程的渠道。優(yōu)點(diǎn)的就是為藝術(shù)家創(chuàng)造的框架,它也是工程化的趨勢(shì)。項(xiàng)目維護(hù)方便也是事實(shí)。如果有遇到問(wèn)題可以直接在教程下面留言。 前言 年底了不太忙,最近一段時(shí)間也一直在研究laravel,就想寫篇關(guān)于laravel比較深一點(diǎn)的教程系列啥的,于是就找到站長(zhǎng)給開了寫教程的渠道。由于第一次寫,...

    wemall 評(píng)論0 收藏0
  • PHPer、Laravel 面試可能會(huì)遇到問(wèn)題及答案

    摘要:如何實(shí)現(xiàn)持久化持久化,將在內(nèi)存中的的狀態(tài)保存到硬盤中,相當(dāng)于備份數(shù)據(jù)庫(kù)狀態(tài)。相當(dāng)于備份數(shù)據(jù)庫(kù)接收到的命令,所有被寫入的命令都是以的協(xié)議格式來(lái)保存的。 最近社區(qū)里面有一篇文章引起了最多程序猿的關(guān)注,Laravel、PHPer 面試可能會(huì)遇到的問(wèn)題,看評(píng)論區(qū)不少小伙伴們被難倒,對(duì)于一些問(wèn)題同樣難倒了我(其實(shí)有很多啦),趁著周末有空,又總結(jié)梳理了一遍,順便來(lái)答一波題。由于個(gè)人技術(shù)水平有限,答...

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

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

0條評(píng)論

閱讀需要支付1元查看
<