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

資訊專欄INFORMATION COLUMN

Laravel核心——Ioc服務(wù)容器

arashicage / 2735人閱讀

摘要:服務(wù)容器在說容器之前,我們需要了解什么是容器。服務(wù)容器是一個用于管理類依賴和執(zhí)行依賴注入的強(qiáng)大工具。幾乎所有的服務(wù)容器綁定都是在服務(wù)提供者中完成,也就是在服務(wù)提供者中綁定。

服務(wù)容器

在說 Ioc 容器之前,我們需要了解什么是 Ioc 容器。

Laravel 服務(wù)容器是一個用于管理類依賴和執(zhí)行依賴注入的強(qiáng)大工具。

在理解這句話之前,我們需要先了解一下服務(wù)容器的來龍去脈: laravel神奇的服務(wù)容器。這篇博客告訴我們,服務(wù)容器就是工廠模式的升級版,對于傳統(tǒng)的工廠模式來說,雖然解耦了對象和外部資源之間的關(guān)系,但是工廠和外部資源之間卻存在了耦和。而服務(wù)容器在為對象創(chuàng)建了外部資源的同時,又與外部資源沒有任何關(guān)系,這個就是 Ioc 容器。
?
所謂的依賴注入和控制反轉(zhuǎn): 依賴注入和控制反轉(zhuǎn),就是

只要不是由內(nèi)部生產(chǎn)(比如初始化、構(gòu)造函數(shù) __construct 中通過工廠方法、自行手動 new 的),而是由外部以參數(shù)或其他形式注入的,都屬于依賴注入(DI)

也就是說:

依賴注入是從應(yīng)用程序的角度在描述,可以把依賴注入描述完整點(diǎn):應(yīng)用程序依賴容器創(chuàng)建并注入它所需要的外部資源;

控制反轉(zhuǎn)是從容器的角度在描述,描述完整點(diǎn):容器控制應(yīng)用程序,由容器反向的向應(yīng)用程序注入應(yīng)用程序所需要的外部資源。

Laravel中的服務(wù)容器

Laravel服務(wù)容器主要承擔(dān)兩個作用:綁定與解析,服務(wù)容器的結(jié)構(gòu)如下:
?

綁定

所謂的綁定就是將接口與實(shí)現(xiàn)建立對應(yīng)關(guān)系。幾乎所有的服務(wù)容器綁定都是在服務(wù)提供者中完成,也就是在服務(wù)提供者中綁定。

如果一個類沒有基于任何接口那么就沒有必要將其綁定到容器。容器并不需要被告知如何構(gòu)建對象,因?yàn)樗鼤褂? PHP 的反射服務(wù)自動解析出具體的對象。

也就是說,如果需要依賴注入的外部資源如果沒有接口,那么就不需要綁定,直接利用服務(wù)容器進(jìn)行解析就可以了,服務(wù)容器會根據(jù)類名利用反射對其進(jìn)行自動構(gòu)造。

bind綁定

綁定有多種方法,首先最常用的是bind函數(shù)的綁定:

綁定自身

$this->app->bind("AppServicesRedisEventPusher", null);

綁定閉包

$this->app->bind("name", function () {
  return "Taylor";
});//閉包返回變量

$this->app->bind("HelpSpotAPI", function () {
  return HelpSpotAPI::class;
});//閉包直接提供類實(shí)現(xiàn)方式

public function testSharedClosureResolution()
{
  $container = new Container;
  $class = new stdClass;
  $container->bind("class", function () use ($class) {
      return $class;
  });

  $this->assertSame($class, $container->make("class"));
}//閉包返回類變量

$this->app->bind("HelpSpotAPI", function () {
  return new HelpSpotAPI();
});//閉包直接提供類實(shí)現(xiàn)方式

$this->app->bind("HelpSpotAPI", function ($app) {
  return new HelpSpotAPI($app->make("HttpClient"));
});//閉包返回需要依賴注入的類

綁定接口

public function testCanBuildWithoutParameterStackWithConstructors()
{
  $container = new Container;
  $container->bind("IlluminateTestsContainerIContainerContractStub", 
                   "IlluminateTestsContainerContainerImplementationStub");
  
  $this->assertInstanceOf(ContainerDependentStub::class, 
                          $container->build(ContainerDependentStub::class));
}

interface IContainerContractStub
{
}

class ContainerImplementationStub implements IContainerContractStub
{
}

class ContainerDependentStub
{
  public $impl;
  public function __construct(IContainerContractStub $impl)
  {
      $this->impl = $impl;
  }
}

這三種綁定方式中,第一種綁定自身一般用于綁定單例。

bindif綁定
public function testBindIfDoesntRegisterIfServiceAlreadyRegistered()
{
    $container = new Container;
    $container->bind("name", function ()
         return "Taylor";
     });

    $container->bindIf("name", function () {
         return "Dayle";
    });

    $this->assertEquals("Taylor", $container->make("name"));
}
singleton綁定

singleton 方法綁定一個只需要解析一次的類或接口到容器,然后接下來對容器的調(diào)用將會返回同一個實(shí)例:

$this->app->singleton("HelpSpotAPI", function ($app) {
    return new HelpSpotAPI($app->make("HttpClient"));
});

值得注意的是,singleton綁定在解析的時候若存在參數(shù)重載,那么就自動取消單例模式。

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

    $container->singleton("foo", function ($app, $config) {
        return $config;
    });

    $this->assertEquals(["name" => "taylor"], $container->makeWith("foo", ["name" => "taylor"]));
    $this->assertEquals(["name" => "abigail"], $container->makeWith("foo", ["name" => "abigail"]));
    }
instance綁定

我們還可以使用 instance 方法綁定一個已存在的對象實(shí)例到容器,隨后調(diào)用容器將總是返回給定的實(shí)例:

$api = new HelpSpotAPI(new HttpClient);
$this->app->instance("HelpSpotApi", $api);
Context綁定

有時侯我們可能有兩個類使用同一個接口,但我們希望在每個類中注入不同實(shí)現(xiàn),例如,兩個控制器依賴 IlluminateContractsFilesystemFilesystem 契約的不同實(shí)現(xiàn)。Laravel 為此定義了簡單、平滑的接口:

use IlluminateSupportFacadesStorage;
use AppHttpControllersVideoController;
use AppHttpControllersPhotoControllers;
use IlluminateContractsFilesystemFilesystem;

$this->app->when(StorageController::class)
          ->needs(Filesystem::class)
          ->give(function () {
            Storage::class
          });//提供類名

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
             return new Storage();
          });//提供實(shí)現(xiàn)方式

$this->app->when(VideoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
            return new Storage($app->make(Disk::class));
          });//需要依賴注入
原始值綁定

我們可能有一個接收注入類的類,同時需要注入一個原生的數(shù)值比如整型,可以結(jié)合上下文輕松注入這個類需要的任何值:

$this->app->when("AppHttpControllersUserController")
          ->needs("$variableName")
          ->give($value);
數(shù)組綁定

數(shù)組綁定一般用于綁定閉包和變量,但是不能綁定接口,否則只能返回接口的實(shí)現(xiàn)類名字符串,并不能返回實(shí)現(xiàn)類的對象。

public function testArrayAccess()
{
    $container = new Container;
    $container[IContainerContractStub::class] = ContainerImplementationStub::class;

    $this->assertTrue(isset($container[IContainerContractStub::class]));
    $this->assertEquals(ContainerImplementationStub::class, 
                        $container[IContainerContractStub::class]);

    unset($container["something"]);
    $this->assertFalse(isset($container["something"]));
}
標(biāo)簽綁定

少數(shù)情況下,我們需要解析特定分類下的所有綁定,例如,你正在構(gòu)建一個接收多個不同 Report 接口實(shí)現(xiàn)的報告聚合器,在注冊完 Report 實(shí)現(xiàn)之后,可以通過 tag 方法給它們分配一個標(biāo)簽:

$this->app->bind("SpeedReport", function () {
  //
});

$this->app->bind("MemoryReport", function () {
  //
});

$this->app->tag(["SpeedReport", "MemoryReport"], "reports");

這些服務(wù)被打上標(biāo)簽后,可以通過 tagged 方法來輕松解析它們:

$this->app->bind("ReportAggregator", function ($app) {
    return new ReportAggregator($app->tagged("reports"));
});
  public function testContainerTags()
  {
         $container = new Container;
         $container->tag("IlluminateTestsContainerContainerImplementationStub", "foo", "bar");
         $container->tag("IlluminateTestsContainerContainerImplementationStubTwo", ["foo"]);

         $this->assertCount(1, $container->tagged("bar"));
         $this->assertCount(2, $container->tagged("foo"));
         $this->assertInstanceOf("IlluminateTestsContainerContainerImplementationStub", $container->tagged("foo")[0]);
         $this->assertInstanceOf("IlluminateTestsContainerContainerImplementationStub", $container->tagged("bar")[0]);
         $this->assertInstanceOf("IlluminateTestsContainerContainerImplementationStubTwo", $container->tagged("foo")[1]);

         $container = new Container;
         $container->tag(["IlluminateTestsContainerContainerImplementationStub", "IlluminateTestsContainerContainerImplementationStubTwo"], ["foo"]);
         $this->assertCount(2, $container->tagged("foo"));
         $this->assertInstanceOf("IlluminateTestsContainerContainerImplementationStub", $container->tagged("foo")[0]);
         $this->assertInstanceOf("IlluminateTestsContainerContainerImplementationStubTwo", $container->tagged("foo")[1]);

          $this->assertEmpty($container->tagged("this_tag_does_not_exist"));
  }
extend擴(kuò)展

extend是在當(dāng)原來的類被注冊或者實(shí)例化出來后,可以對其進(jìn)行擴(kuò)展,而且可以支持多重?cái)U(kuò)展:

public function testExtendInstancesArePreserved()
{
    $container = new Container;
    $container->bind("foo", function () {
        $obj = new StdClass;
        $obj->foo = "bar";

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

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

    $this->assertEquals("foo", $container->make("foo")->foo);
    $this->assertEquals("baz", $container->make("foo")->bar);
    $this->assertEquals("foo", $container->make("foo")->baz);
}
Rebounds與Rebinding

綁定是針對接口的,是為接口提供實(shí)現(xiàn)方式的方法。我們可以對接口在不同的時間段里提供不同的實(shí)現(xiàn)方法,一般來說,對同一個接口提供新的實(shí)現(xiàn)方法后,不會對已經(jīng)實(shí)例化的對象產(chǎn)生任何影響。但是在一些場景下,在提供新的接口實(shí)現(xiàn)后,我們希望對已經(jīng)實(shí)例化的對象重新做一些改變,這個就是 rebinding 函數(shù)的用途。
下面就是一個例子:

abstract class Car
{
    public function __construct(Fuel $fuel)
    {
        $this->fuel = $fuel;
    }

    public function refuel($litres)
    {
        return $litres * $this->fuel->getPrice();
    }

    public function setFuel(Fuel $fuel)
    {
        $this->fuel = $fuel;
    }

}

class JeepWrangler extends Car
{
  //
}

interface Fuel
{
    public function getPrice();
}

class Petrol implements Fuel
{
    public function getPrice()
    {
        return 130.7;
    }
}

我們在服務(wù)容器中是這樣對car接口和fuel接口綁定的:

$this->app->bind("fuel", function ($app) {
    return new Petrol;
});

$this->app->bind("car", function ($app) {
    return new JeepWrangler($app["fuel"]);
});

$this->app->make("car");

如果car被服務(wù)容器解析實(shí)例化成對象之后,有人修改了 fuel 接口的實(shí)現(xiàn),從 Petrol 改為 PremiumPetrol:

$this->app->bind("fuel", function ($app) {
    return new PremiumPetrol;
});

由于 car 已經(jīng)被實(shí)例化,那么這個接口實(shí)現(xiàn)的改變并不會影響到 car 的實(shí)現(xiàn),假若我們想要 car 的成員變量 fuel 隨著 fuel 接口的變化而變化,我們就需要一個回調(diào)函數(shù),每當(dāng)對 fuel 接口實(shí)現(xiàn)進(jìn)行改變的時候,都要對 car 的 fuel 變量進(jìn)行更新,這就是 rebinding 的用途:

$this->app->bindShared("car", function ($app) {
    return new JeepWrangler($app->rebinding("fuel", function ($app, $fuel) {
        $app["car"]->setFuel($fuel);
    }));
});
服務(wù)別名 什么是服務(wù)別名

在說服務(wù)容器的解析之前,需要先說說服務(wù)的別名。什么是服務(wù)別名呢?不同于上一個博客中提到的 Facade 門面的別名(在 config/app 中定義),這里的別名服務(wù)綁定名稱的別名。通過服務(wù)綁定的別名,在解析服務(wù)的時候,跟不使用別名的效果一致。別名的作用也是為了同時支持全類型的服務(wù)綁定名稱以及簡短的服務(wù)綁定名稱考慮的。
?
通俗的講,假如我們想要創(chuàng)建 auth 服務(wù),我們既可以這樣寫:

$this->app->make("auth")

又可以寫成:

$this->app->make("IlluminateAuthAuthManager::class")

還可以寫成

$this->app->make("IlluminateContractsAuthFactory::class")

后面兩個服務(wù)的名字都是 auth 的別名,使用別名和使用 auth 的效果是相同的。

服務(wù)別名的遞歸

需要注意的是別名是可以遞歸的:

app()->alias("service", "alias_a");
app()->alias("alias_a", "alias_b");
app()-alias("alias_b", "alias_c");

會得到:

"alias_a" => "service"
"alias_b" => "alias_a"
"alias_c" => "alias_b"
服務(wù)別名的實(shí)現(xiàn)

那么這些別名是如何加載到服務(wù)容器里面的呢?實(shí)際上,服務(wù)容器里面有個 aliases 數(shù)組:

$aliases = [
  "app" => [IlluminateFoundationApplication::class, IlluminateContractsContainerContainer::class, IlluminateContractsFoundationApplication::class],
  "auth" => [IlluminateAuthAuthManager::class, IlluminateContractsAuthFactory::class],
  "auth.driver" => [IlluminateContractsAuthGuard::class],
  "blade.compiler" => [IlluminateViewCompilersBladeCompiler::class],
  "cache" => [IlluminateCacheCacheManager::class, IlluminateContractsCacheFactory::class],
...
]

而服務(wù)容器的初始化的過程中,會運(yùn)行一個函數(shù):

public function registerCoreContainerAliases()
{
  foreach ($aliases as $key => $aliases) {
    foreach ($aliases as $alias) {
      $this->alias($key, $alias);
    }
  }
}

public function alias($abstract, $alias)
{
  $this->aliases[$alias] = $abstract;

  $this->abstractAliases[$abstract][] = $alias;
}

加載后,服務(wù)容器的aliases和abstractAliases數(shù)組:

$aliases = [
  "IlluminateFoundationApplication" = "app"
  "IlluminateContractsContainerContainer" = "app"
  "IlluminateContractsFoundationApplication" = "app"
  "IlluminateAuthAuthManager" = "auth"
  "IlluminateContractsAuthFactory" = "auth"
  "IlluminateContractsAuthGuard" = "auth.driver"
  "IlluminateViewCompilersBladeCompiler" = "blade.compiler"
  "IlluminateCacheCacheManager" = "cache"
  "IlluminateContractsCacheFactory" = "cache"
  ...
]
$abstractAliases = [
  app = {array} [3]
  0 = "IlluminateFoundationApplication"
  1 = "IlluminateContractsContainerContainer"
  2 = "IlluminateContractsFoundationApplication"
  auth = {array} [2]
  0 = "IlluminateAuthAuthManager"
  1 = "IlluminateContractsAuthFactory"
  auth.driver = {array} [1]
  0 = "IlluminateContractsAuthGuard"
  blade.compiler = {array} [1]
  0 = "IlluminateViewCompilersBladeCompiler"
  cache = {array} [2]
  0 = "IlluminateCacheCacheManager"
  1 = "IlluminateContractsCacheFactory"
  ...
]
服務(wù)解析 make 解析

有很多方式可以從容器中解析對象,首先,你可以使用 make 方法,該方法接收你想要解析的類名或接口名作為參數(shù):

public function testAutoConcreteResolution()
{
  $container = new Container;
  $this->assertInstanceOf("IlluminateTestsContainerContainerConcreteStub", 
         $container->make("IlluminateTestsContainerContainerConcreteStub"));
}

//帶有依賴注入和默認(rèn)值的解析
public function testResolutionOfDefaultParameters()
{
     $container = new Container;
     $instance = $container->make("IlluminateTestsContainerContainerDefaultValueStub");
     $this->assertInstanceOf("IlluminateTestsContainerContainerConcreteStub", 
                              $instance->stub);
     $this->assertEquals("taylor", $instance->default);
}

//
public function testResolvingWithArrayOfParameters()
{
  $container = new Container;
  
  $instance = $container->makeWith(ContainerDefaultValueStub::class, ["default" => "adam"]);
  $this->assertEquals("adam", $instance->default);
  
  $instance = $container->make(ContainerDefaultValueStub::class);
  $this->assertEquals("taylor", $instance->default);
  
  $container->bind("foo", function ($app, $config) {
      return $config;
  });
  $this->assertEquals([1, 2, 3], $container->makeWith("foo", [1, 2, 3]));
}

public function testNestedDependencyResolution()
{
  $container = new Container;
  $container->bind("IlluminateTestsContainerIContainerContractStub", "IlluminateTestsContainerContainerImplementationStub");
  $class = $container->make("IlluminateTestsContainerContainerNestedDependentStub");
  $this->assertInstanceOf("IlluminateTestsContainerContainerDependentStub", $class->inner);
  $this->assertInstanceOf("IlluminateTestsContainerContainerImplementationStub", $class->inner->impl);
  }

class ContainerDefaultValueStub
{
  public $stub;
  public $default;
  public function __construct(ContainerConcreteStub $stub, $default = "taylor")
  {
      $this->stub = $stub;
      $this->default = $default;
  }
}

class ContainerConcreteStub
{
}

class ContainerImplementationStub implements IContainerContractStub
{
}

class ContainerDependentStub
{
  public $impl;
  public function __construct(IContainerContractStub $impl)
  {
      $this->impl = $impl;
  }
}
class ContainerNestedDependentStub
{
  public $inner;
  public function __construct(ContainerDependentStub $inner)
  {
      $this->inner = $inner;
  }
}

如果你所在的代碼位置訪問不了 $app 變量,可以使用輔助函數(shù)resolve:

$api = resolve("HelpSpotAPI");
自動注入
namespace AppHttpControllers;

use AppUsersRepository as UserRepository;

class UserController extends Controller{
  /**
  * 用戶倉庫實(shí)例
  */
  protected $users;

  /**
  * 創(chuàng)建一個控制器實(shí)例
  *
  * @param UserRepository $users 自動注入
  * @return void
  */
  public function __construct(UserRepository $users)
  {
    $this->users = $users;
  }
}
call 方法注入

make 解析是服務(wù)容器進(jìn)行解析構(gòu)建類對象時所用的方法,在實(shí)際應(yīng)用中,還有另外一個需求,那就是當(dāng)前已經(jīng)獲取了一個類對象,我們想要調(diào)用它的一個方法函數(shù),這時發(fā)現(xiàn)這個方法中參數(shù)眾多,如果一個個的 make 會比較繁瑣,這個時候就要用到 call 解析了。我們可以看這個例子:

class TaskRepository{

    public function testContainerCall(User $user,Task $task){
        $this->assertInstanceOf(User::class, $user);

        $this->assertInstanceOf(Task::class, $task);
    }

    public static function testContainerCallStatic(User $user,Task $task){
        $this->assertInstanceOf(User::class, $user);

        $this->assertInstanceOf(Task::class, $task);
    }

    public function testCallback(){
        echo "call callback successfully!";
    }

    public function testDefaultMethod(){
        echo "default Method successfully!";
    }
}
閉包函數(shù)注入
  public function testCallWithDependencies()
  {
      $container = new Container;
      $result = $container->call(function (StdClass $foo, $bar = []) {
          return func_get_args();
      });

      $this->assertInstanceOf("stdClass", $result[0]);
      $this->assertEquals([], $result[1]);

      $result = $container->call(function (StdClass $foo, $bar = []) {
          return func_get_args();
      }, ["bar" => "taylor"]);

      $this->assertInstanceOf("stdClass", $result[0]);
      $this->assertEquals("taylor", $result[1]);
}
普通函數(shù)注入
public function testCallWithGlobalMethodName()
{
    $container = new Container;
    $result = $container->call("IlluminateTestsContainercontainerTestInject");
    $this->assertInstanceOf("IlluminateTestsContainerContainerConcreteStub", $result[0]);
    $this->assertEquals("taylor", $result[1]);
}
靜態(tài)方法注入

服務(wù)容器的 call 解析主要依靠 call_user_func_array() 函數(shù),關(guān)于這個函數(shù)可以查看 Laravel學(xué)習(xí)筆記之Callback Type - 來生做個漫畫家,這個函數(shù)對類中的靜態(tài)函數(shù)和非靜態(tài)函數(shù)有一些區(qū)別,對于靜態(tài)函數(shù)來說:

class ContainerCallTest
{
    public function testContainerCallStatic(){
        App::call(TaskRepository::class."@testContainerCallStatic");
        App::call(TaskRepository::class."::testContainerCallStatic");
        App::call([TaskRepository::class,"testContainerCallStatic"]);
    }
}

服務(wù)容器調(diào)用類的靜態(tài)方法有三種,注意第三種使用數(shù)組的形式,數(shù)組中可以直接傳類名 TaskRepository::class;

非靜態(tài)方法注入

對于類的非靜態(tài)方法:

class ContainerCallTest
{
    public function testContainerCall(){
        $taskRepo = new TaskRepository();
        App::call(TaskRepository::class."@testContainerCall");
        App::call([$taskRepo,"testContainerCall"]);
    }
}

我們可以看到非靜態(tài)方法只有兩種調(diào)用方式,而且第二種數(shù)組傳遞的參數(shù)是類對象,原因就是 call_user_func_array函數(shù)的限制,對于非靜態(tài)方法只能傳遞對象。

bindmethod 方法綁定

服務(wù)容器還有一個 bindmethod 的方法,可以綁定類的一個方法到自定義的函數(shù):

public function testContainCallMethodBind(){

    App::bindMethod(TaskRepository::class."@testContainerCallStatic",function () {
         $taskRepo = new TaskRepository();
         $taskRepo->testCallback();
    });

    App::call(TaskRepository::class."@testContainerCallStatic");
    App::call(TaskRepository::class."::testContainerCallStatic");
    App::call([TaskRepository::class,"testContainerCallStatic"]);

    App::bindMethod(TaskRepository::class."@testContainerCall",function (TaskRepository $taskRepo) { $taskRepo->testCallback(); });

    $taskRepo = new TaskRepository();
    App::call(TaskRepository::class."@testContainerCall");
    App::call([$taskRepo,"testContainerCall"]);
}

從結(jié)果上看,bindmethod 不會對靜態(tài)的第二種解析方法( :: 解析方式)起作用,對于其他方式都會調(diào)用綁定的函數(shù)。

  public function testCallWithBoundMethod()
  {
      $container = new Container;
      $container->bindMethod("IlluminateTestsContainerContainerTestCallStub@unresolvable", function ($stub) {
          return $stub->unresolvable("foo", "bar");
      });
      $result = $container->call("IlluminateTestsContainerContainerTestCallStub@unresolvable");
      $this->assertEquals(["foo", "bar"], $result);

      $container = new Container;
      $container->bindMethod("IlluminateTestsContainerContainerTestCallStub@unresolvable", function ($stub) {
          return $stub->unresolvable("foo", "bar");
  });
      $result = $container->call([new ContainerTestCallStub, "unresolvable"]);
      $this->assertEquals(["foo", "bar"], $result);
  }

class ContainerTestCallStub
{
    public function unresolvable($foo, $bar)
    {
        return func_get_args();
    }
}
默認(rèn)函數(shù)注入
public function testContainCallDefultMethod(){

    App::call(TaskRepository::class,[],"testContainerCall");

    App::call(TaskRepository::class,[],"testContainerCallStatic");

    App::bindMethod(TaskRepository::class."@testContainerCallStatic",function () {
        $taskRepo = new TaskRepository();
        $taskRepo->testCallback();
    });

    App::bindMethod(TaskRepository::class."@testContainerCall",function (TaskRepository $taskRepo) {  $taskRepo->testCallback(); });

    App::call(TaskRepository::class,[],"testContainerCall");

    App::call(TaskRepository::class,[],"testContainerCallStatic");

}

值得注意的是,這種默認(rèn)函數(shù)注入的方法使得非靜態(tài)的方法也可以利用類名去調(diào)用,并不需要對象。默認(rèn)函數(shù)注入也回受到 bindmethod 函數(shù)的影響。

數(shù)組解析
app()["service"];
app($service)的形式
app("service");
服務(wù)容器事件

每當(dāng)服務(wù)容器解析一個對象時就會觸發(fā)一個事件。你可以使用 resolving 方法監(jiān)聽這個事件:

$this->app->resolving(function ($object, $app) {
  // 解析任何類型的對象時都會調(diào)用該方法...
});
$this->app->resolving(HelpSpotAPI::class, function ($api, $app) {
  // 解析「HelpSpotAPI」類型的對象時調(diào)用...
});
$this->app->afterResolving(function ($object, $app) {
  // 解析任何類型的對象后都會調(diào)用該方法...
});
$this->app->afterResolving(HelpSpotAPI::class, function ($api, $app) {
  // 解析「HelpSpotAPI」類型的對象后調(diào)用...
});

服務(wù)容器每次解析對象的時候,都會調(diào)用這些通過 resolving 和 afterResolving 函數(shù)傳入的閉包函數(shù),也就是觸發(fā)這些事件。
注意:如果是單例,則只在解析時會觸發(fā)一次

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

    $this->assertEquals("taylor", $instance->name);
}

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);
}
裝飾函數(shù)

容器的裝飾函數(shù)有兩種,wrap用于裝飾call,factory用于裝飾make:

public function testContainerWrap()
{
      $result = $container->wrap(function (StdClass $foo, $bar = []) {
          return func_get_args();
      }, ["bar" => "taylor"]);

      $this->assertInstanceOf("Closure", $result);
      $result = $result();

      $this->assertInstanceOf("stdClass", $result[0]);
      $this->assertEquals("taylor", $result[1]);
  }

public function testContainerGetFactory()
{
    $container = new Container;
    $container->bind("name", function () {
        return "Taylor’;
    });
    $factory = $container->factory("name");
    $this->assertEquals($container->make("name"), $factory());
}
容器重置flush

容器的重置函數(shù)flush會清空容器內(nèi)部的aliases、abstractAliases、resolved、bindings、instances

public function testContainerFlushFlushesAllBindingsAliasesAndResolvedInstances()
{
    $container = new Container;
    $container->bind("ConcreteStub", function () {
        return new ContainerConcreteStub;
    }, true);
    $container->alias("ConcreteStub", "ContainerConcreteStub");
    
    $concreteStubInstance = $container->make("ConcreteStub");
    $this->assertTrue($container->resolved("ConcreteStub"));
    $this->assertTrue($container->isAlias("ContainerConcreteStub"));
    $this->assertArrayHasKey("ConcreteStub", $container->getBindings());
    $this->assertTrue($container->isShared("ConcreteStub"));
    
    $container->flush();
    $this->assertFalse($container->resolved("ConcreteStub"));
    $this->assertFalse($container->isAlias("ContainerConcreteStub"));
    $this->assertEmpty($container->getBindings());
    $this->assertFalse($container->isShared("ConcreteStub"));
}

Written with StackEdit.

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

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

相關(guān)文章

  • Laravel思維導(dǎo)圖之Laravel核心概念

    摘要:的核心概念包括服務(wù)容器服務(wù)提供者門面契約。所有服務(wù)提供者都需要繼承類??梢詾榉?wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊之后調(diào)用。同樣會整理成思維導(dǎo)圖的形式以方便記憶與回顧。 showImg(https://segmentfault.com/img/remote/1460000010771201); Laravel 的核心概念包括:服務(wù)容器、服務(wù)提供者、門面(Fac...

    wthee 評論0 收藏0
  • Laravel中的核心概念

    摘要:可以為服務(wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊之后調(diào)用。所有服務(wù)提供者都在配置文件中注冊??梢赃x擇推遲服務(wù)提供者的注冊,直到真正需要注冊綁定時,這樣可以提供應(yīng)用程序的性能。 本文最早發(fā)布于 Rootrl的Blog 導(dǎo)言 Laravel是一款先進(jìn)的現(xiàn)代化框架,里面有一些概念非常重要。在上手Laravel之前,我認(rèn)為先弄懂這些概念是很有必要的。你甚至需要重溫下PHP...

    ddongjian0000 評論0 收藏0
  • PHPer面試指南-Laravel

    摘要:簡述的生命周期采用了單一入口模式,應(yīng)用的所有請求入口都是文件。分發(fā)請求一旦應(yīng)用完成引導(dǎo)和所有服務(wù)提供者都注冊完成,將會移交給路由進(jìn)行分發(fā)。此外,由于對動態(tài)方法的獨(dú)特用法,也使測試起來非常容易。 本書的 GitHub 地址:https://github.com/todayqq/PH... Laravel 作為現(xiàn)在最流行的 PHP 框架,其中的知識較多,所以單獨(dú)拿出來寫一篇。 簡述 La...

    alaege 評論0 收藏0
  • 深入剖析 Laravel 服務(wù)容器

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

    abson 評論0 收藏0
  • php實(shí)現(xiàn)依賴注入(DI)和控制反轉(zhuǎn)(IOC)

    摘要:工廠模式,依賴轉(zhuǎn)移當(dāng)然,實(shí)現(xiàn)控制反轉(zhuǎn)的方法有幾種。其實(shí)我們稍微改造一下這個類,你就明白,工廠類的真正意義和價值了。雖然如此,工廠模式依舊十分優(yōu)秀,并且適用于絕大多數(shù)情況。 此篇文章轉(zhuǎn)載自laravel-china,chongyi的文章https://laravel-china.org/top...原文地址: http://www.insp.top/learn-lar... ,轉(zhuǎn)載務(wù)必保...

    tomato 評論0 收藏0
  • 深入理解控制反轉(zhuǎn)(IoC)和依賴注入(DI)

    摘要:本文一大半內(nèi)容都是通過舉例來讓讀者去理解什么是控制反轉(zhuǎn)和依賴注入,通過理解這些概念,來更加深入。這種由外部負(fù)責(zé)其依賴需求的行為,我們可以稱其為控制反轉(zhuǎn)。工廠模式,依賴轉(zhuǎn)移當(dāng)然,實(shí)現(xiàn)控制反轉(zhuǎn)的方法有幾種。 容器,字面上理解就是裝東西的東西。常見的變量、對象屬性等都可以算是容器。一個容器能夠裝什么,全部取決于你對該容器的定義。當(dāng)然,有這樣一種容器,它存放的不是文本、數(shù)值,而是對象、對象的描...

    HollisChuang 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<