摘要:服務(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)
也就是說:
Laravel中的服務(wù)容器依賴注入是從應(yīng)用程序的角度在描述,可以把依賴注入描述完整點(diǎn):應(yīng)用程序依賴容器創(chuàng)建并注入它所需要的外部資源;
控制反轉(zhuǎn)是從容器的角度在描述,描述完整點(diǎn):容器控制應(yīng)用程序,由容器反向的向應(yīng)用程序注入應(yīng)用程序所需要的外部資源。
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
摘要:的核心概念包括服務(wù)容器服務(wù)提供者門面契約。所有服務(wù)提供者都需要繼承類??梢詾榉?wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊之后調(diào)用。同樣會整理成思維導(dǎo)圖的形式以方便記憶與回顧。 showImg(https://segmentfault.com/img/remote/1460000010771201); Laravel 的核心概念包括:服務(wù)容器、服務(wù)提供者、門面(Fac...
摘要:可以為服務(wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊之后調(diào)用。所有服務(wù)提供者都在配置文件中注冊??梢赃x擇推遲服務(wù)提供者的注冊,直到真正需要注冊綁定時,這樣可以提供應(yīng)用程序的性能。 本文最早發(fā)布于 Rootrl的Blog 導(dǎo)言 Laravel是一款先進(jìn)的現(xiàn)代化框架,里面有一些概念非常重要。在上手Laravel之前,我認(rèn)為先弄懂這些概念是很有必要的。你甚至需要重溫下PHP...
摘要:簡述的生命周期采用了單一入口模式,應(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...
摘要:劃下重點(diǎn),服務(wù)容器是用于管理類的依賴和執(zhí)行依賴注入的工具。類的實(shí)例化及其依賴的注入,完全由服務(wù)容器自動的去完成。 本文首發(fā)于 深入剖析 Laravel 服務(wù)容器,轉(zhuǎn)載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應(yīng)并最終呈現(xiàn)給用戶的工作原理。 本章將帶領(lǐng)大...
摘要:工廠模式,依賴轉(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ù)必保...
摘要:本文一大半內(nèi)容都是通過舉例來讓讀者去理解什么是控制反轉(zhuǎn)和依賴注入,通過理解這些概念,來更加深入。這種由外部負(fù)責(zé)其依賴需求的行為,我們可以稱其為控制反轉(zhuǎn)。工廠模式,依賴轉(zhuǎn)移當(dāng)然,實(shí)現(xiàn)控制反轉(zhuǎn)的方法有幾種。 容器,字面上理解就是裝東西的東西。常見的變量、對象屬性等都可以算是容器。一個容器能夠裝什么,全部取決于你對該容器的定義。當(dāng)然,有這樣一種容器,它存放的不是文本、數(shù)值,而是對象、對象的描...
閱讀 679·2023-04-26 02:03
閱讀 1049·2021-11-23 09:51
閱讀 1161·2021-10-14 09:42
閱讀 1758·2021-09-13 10:23
閱讀 980·2021-08-27 13:12
閱讀 855·2019-08-30 11:21
閱讀 1013·2019-08-30 11:14
閱讀 1061·2019-08-30 11:09