摘要:服務容器在說容器之前,我們需要了解什么是容器。服務容器是一個用于管理類依賴和執行依賴注入的強大工具。幾乎所有的服務容器綁定都是在服務提供者中完成,也就是在服務提供者中綁定。
服務容器
在說 Ioc 容器之前,我們需要了解什么是 Ioc 容器。
Laravel 服務容器是一個用于管理類依賴和執行依賴注入的強大工具。
在理解這句話之前,我們需要先了解一下服務容器的來龍去脈: laravel神奇的服務容器。這篇博客告訴我們,服務容器就是工廠模式的升級版,對于傳統的工廠模式來說,雖然解耦了對象和外部資源之間的關系,但是工廠和外部資源之間卻存在了耦和。而服務容器在為對象創建了外部資源的同時,又與外部資源沒有任何關系,這個就是 Ioc 容器。
?
所謂的依賴注入和控制反轉: 依賴注入和控制反轉,就是
只要不是由內部生產(比如初始化、構造函數 __construct 中通過工廠方法、自行手動 new 的),而是由外部以參數或其他形式注入的,都屬于依賴注入(DI)
也就是說:
Laravel中的服務容器依賴注入是從應用程序的角度在描述,可以把依賴注入描述完整點:應用程序依賴容器創建并注入它所需要的外部資源;
控制反轉是從容器的角度在描述,描述完整點:容器控制應用程序,由容器反向的向應用程序注入應用程序所需要的外部資源。
Laravel服務容器主要承擔兩個作用:綁定與解析,服務容器的結構如下:
?
所謂的綁定就是將接口與實現建立對應關系。幾乎所有的服務容器綁定都是在服務提供者中完成,也就是在服務提供者中綁定。
如果一個類沒有基于任何接口那么就沒有必要將其綁定到容器。容器并不需要被告知如何構建對象,因為它會使用 PHP 的反射服務自動解析出具體的對象。
也就是說,如果需要依賴注入的外部資源如果沒有接口,那么就不需要綁定,直接利用服務容器進行解析就可以了,服務容器會根據類名利用反射對其進行自動構造。
bind綁定綁定有多種方法,首先最常用的是bind函數的綁定:
綁定自身
$this->app->bind("AppServicesRedisEventPusher", null);
綁定閉包
$this->app->bind("name", function () { return "Taylor"; });//閉包返回變量 $this->app->bind("HelpSpotAPI", function () { return HelpSpotAPI::class; });//閉包直接提供類實現方式 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(); });//閉包直接提供類實現方式 $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 方法綁定一個只需要解析一次的類或接口到容器,然后接下來對容器的調用將會返回同一個實例:
$this->app->singleton("HelpSpotAPI", function ($app) { return new HelpSpotAPI($app->make("HttpClient")); });
值得注意的是,singleton綁定在解析的時候若存在參數重載,那么就自動取消單例模式。
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 方法綁定一個已存在的對象實例到容器,隨后調用容器將總是返回給定的實例:
$api = new HelpSpotAPI(new HttpClient); $this->app->instance("HelpSpotApi", $api);Context綁定
有時侯我們可能有兩個類使用同一個接口,但我們希望在每個類中注入不同實現,例如,兩個控制器依賴 IlluminateContractsFilesystemFilesystem 契約的不同實現。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(); });//提供實現方式 $this->app->when(VideoController::class) ->needs(Filesystem::class) ->give(function () { return new Storage($app->make(Disk::class)); });//需要依賴注入原始值綁定
我們可能有一個接收注入類的類,同時需要注入一個原生的數值比如整型,可以結合上下文輕松注入這個類需要的任何值:
$this->app->when("AppHttpControllersUserController") ->needs("$variableName") ->give($value);數組綁定
數組綁定一般用于綁定閉包和變量,但是不能綁定接口,否則只能返回接口的實現類名字符串,并不能返回實現類的對象。
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"])); }標簽綁定
少數情況下,我們需要解析特定分類下的所有綁定,例如,你正在構建一個接收多個不同 Report 接口實現的報告聚合器,在注冊完 Report 實現之后,可以通過 tag 方法給它們分配一個標簽:
$this->app->bind("SpeedReport", function () { // }); $this->app->bind("MemoryReport", function () { // }); $this->app->tag(["SpeedReport", "MemoryReport"], "reports");
這些服務被打上標簽后,可以通過 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擴展
extend是在當原來的類被注冊或者實例化出來后,可以對其進行擴展,而且可以支持多重擴展:
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
綁定是針對接口的,是為接口提供實現方式的方法。我們可以對接口在不同的時間段里提供不同的實現方法,一般來說,對同一個接口提供新的實現方法后,不會對已經實例化的對象產生任何影響。但是在一些場景下,在提供新的接口實現后,我們希望對已經實例化的對象重新做一些改變,這個就是 rebinding 函數的用途。
下面就是一個例子:
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; } }
我們在服務容器中是這樣對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被服務容器解析實例化成對象之后,有人修改了 fuel 接口的實現,從 Petrol 改為 PremiumPetrol:
$this->app->bind("fuel", function ($app) { return new PremiumPetrol; });
由于 car 已經被實例化,那么這個接口實現的改變并不會影響到 car 的實現,假若我們想要 car 的成員變量 fuel 隨著 fuel 接口的變化而變化,我們就需要一個回調函數,每當對 fuel 接口實現進行改變的時候,都要對 car 的 fuel 變量進行更新,這就是 rebinding 的用途:
$this->app->bindShared("car", function ($app) { return new JeepWrangler($app->rebinding("fuel", function ($app, $fuel) { $app["car"]->setFuel($fuel); })); });服務別名 什么是服務別名
在說服務容器的解析之前,需要先說說服務的別名。什么是服務別名呢?不同于上一個博客中提到的 Facade 門面的別名(在 config/app 中定義),這里的別名服務綁定名稱的別名。通過服務綁定的別名,在解析服務的時候,跟不使用別名的效果一致。別名的作用也是為了同時支持全類型的服務綁定名稱以及簡短的服務綁定名稱考慮的。
?
通俗的講,假如我們想要創建 auth 服務,我們既可以這樣寫:
$this->app->make("auth")
又可以寫成:
$this->app->make("IlluminateAuthAuthManager::class")
還可以寫成
$this->app->make("IlluminateContractsAuthFactory::class")
后面兩個服務的名字都是 auth 的別名,使用別名和使用 auth 的效果是相同的。
服務別名的遞歸需要注意的是別名是可以遞歸的:
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"服務別名的實現
那么這些別名是如何加載到服務容器里面的呢?實際上,服務容器里面有個 aliases 數組:
$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], ... ]
而服務容器的初始化的過程中,會運行一個函數:
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; }
加載后,服務容器的aliases和abstractAliases數組:
$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" ... ]服務解析 make 解析
有很多方式可以從容器中解析對象,首先,你可以使用 make 方法,該方法接收你想要解析的類名或接口名作為參數:
public function testAutoConcreteResolution() { $container = new Container; $this->assertInstanceOf("IlluminateTestsContainerContainerConcreteStub", $container->make("IlluminateTestsContainerContainerConcreteStub")); } //帶有依賴注入和默認值的解析 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 變量,可以使用輔助函數resolve:
$api = resolve("HelpSpotAPI");自動注入
namespace AppHttpControllers; use AppUsersRepository as UserRepository; class UserController extends Controller{ /** * 用戶倉庫實例 */ protected $users; /** * 創建一個控制器實例 * * @param UserRepository $users 自動注入 * @return void */ public function __construct(UserRepository $users) { $this->users = $users; } }call 方法注入
make 解析是服務容器進行解析構建類對象時所用的方法,在實際應用中,還有另外一個需求,那就是當前已經獲取了一個類對象,我們想要調用它的一個方法函數,這時發現這個方法中參數眾多,如果一個個的 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!"; } }閉包函數注入
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]); }普通函數注入
public function testCallWithGlobalMethodName() { $container = new Container; $result = $container->call("IlluminateTestsContainercontainerTestInject"); $this->assertInstanceOf("IlluminateTestsContainerContainerConcreteStub", $result[0]); $this->assertEquals("taylor", $result[1]); }靜態方法注入
服務容器的 call 解析主要依靠 call_user_func_array() 函數,關于這個函數可以查看 Laravel學習筆記之Callback Type - 來生做個漫畫家,這個函數對類中的靜態函數和非靜態函數有一些區別,對于靜態函數來說:
class ContainerCallTest { public function testContainerCallStatic(){ App::call(TaskRepository::class."@testContainerCallStatic"); App::call(TaskRepository::class."::testContainerCallStatic"); App::call([TaskRepository::class,"testContainerCallStatic"]); } }
服務容器調用類的靜態方法有三種,注意第三種使用數組的形式,數組中可以直接傳類名 TaskRepository::class;
非靜態方法注入對于類的非靜態方法:
class ContainerCallTest { public function testContainerCall(){ $taskRepo = new TaskRepository(); App::call(TaskRepository::class."@testContainerCall"); App::call([$taskRepo,"testContainerCall"]); } }
我們可以看到非靜態方法只有兩種調用方式,而且第二種數組傳遞的參數是類對象,原因就是 call_user_func_array函數的限制,對于非靜態方法只能傳遞對象。
bindmethod 方法綁定服務容器還有一個 bindmethod 的方法,可以綁定類的一個方法到自定義的函數:
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"]); }
從結果上看,bindmethod 不會對靜態的第二種解析方法( :: 解析方式)起作用,對于其他方式都會調用綁定的函數。
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(); } }默認函數注入
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"); }
值得注意的是,這種默認函數注入的方法使得非靜態的方法也可以利用類名去調用,并不需要對象。默認函數注入也回受到 bindmethod 函數的影響。
數組解析app()["service"];app($service)的形式
app("service");服務容器事件
每當服務容器解析一個對象時就會觸發一個事件。你可以使用 resolving 方法監聽這個事件:
$this->app->resolving(function ($object, $app) { // 解析任何類型的對象時都會調用該方法... }); $this->app->resolving(HelpSpotAPI::class, function ($api, $app) { // 解析「HelpSpotAPI」類型的對象時調用... }); $this->app->afterResolving(function ($object, $app) { // 解析任何類型的對象后都會調用該方法... }); $this->app->afterResolving(HelpSpotAPI::class, function ($api, $app) { // 解析「HelpSpotAPI」類型的對象后調用... });
服務容器每次解析對象的時候,都會調用這些通過 resolving 和 afterResolving 函數傳入的閉包函數,也就是觸發這些事件。
注意:如果是單例,則只在解析時會觸發一次
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); }裝飾函數
容器的裝飾函數有兩種,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
容器的重置函數flush會清空容器內部的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.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/22926.html
摘要:的核心概念包括服務容器服務提供者門面契約。所有服務提供者都需要繼承類。可以為服務提供者的方法設置類型提示。方法將在所有其他服務提供者均已注冊之后調用。同樣會整理成思維導圖的形式以方便記憶與回顧。 showImg(https://segmentfault.com/img/remote/1460000010771201); Laravel 的核心概念包括:服務容器、服務提供者、門面(Fac...
摘要:可以為服務提供者的方法設置類型提示。方法將在所有其他服務提供者均已注冊之后調用。所有服務提供者都在配置文件中注冊。可以選擇推遲服務提供者的注冊,直到真正需要注冊綁定時,這樣可以提供應用程序的性能。 本文最早發布于 Rootrl的Blog 導言 Laravel是一款先進的現代化框架,里面有一些概念非常重要。在上手Laravel之前,我認為先弄懂這些概念是很有必要的。你甚至需要重溫下PHP...
摘要:簡述的生命周期采用了單一入口模式,應用的所有請求入口都是文件。分發請求一旦應用完成引導和所有服務提供者都注冊完成,將會移交給路由進行分發。此外,由于對動態方法的獨特用法,也使測試起來非常容易。 本書的 GitHub 地址:https://github.com/todayqq/PH... Laravel 作為現在最流行的 PHP 框架,其中的知識較多,所以單獨拿出來寫一篇。 簡述 La...
摘要:劃下重點,服務容器是用于管理類的依賴和執行依賴注入的工具。類的實例化及其依賴的注入,完全由服務容器自動的去完成。 本文首發于 深入剖析 Laravel 服務容器,轉載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應并最終呈現給用戶的工作原理。 本章將帶領大...
摘要:工廠模式,依賴轉移當然,實現控制反轉的方法有幾種。其實我們稍微改造一下這個類,你就明白,工廠類的真正意義和價值了。雖然如此,工廠模式依舊十分優秀,并且適用于絕大多數情況。 此篇文章轉載自laravel-china,chongyi的文章https://laravel-china.org/top...原文地址: http://www.insp.top/learn-lar... ,轉載務必保...
摘要:本文一大半內容都是通過舉例來讓讀者去理解什么是控制反轉和依賴注入,通過理解這些概念,來更加深入。這種由外部負責其依賴需求的行為,我們可以稱其為控制反轉。工廠模式,依賴轉移當然,實現控制反轉的方法有幾種。 容器,字面上理解就是裝東西的東西。常見的變量、對象屬性等都可以算是容器。一個容器能夠裝什么,全部取決于你對該容器的定義。當然,有這樣一種容器,它存放的不是文本、數值,而是對象、對象的描...
閱讀 663·2023-04-26 02:03
閱讀 1037·2021-11-23 09:51
閱讀 1111·2021-10-14 09:42
閱讀 1738·2021-09-13 10:23
閱讀 927·2021-08-27 13:12
閱讀 839·2019-08-30 11:21
閱讀 1001·2019-08-30 11:14
閱讀 1041·2019-08-30 11:09