本篇文章給大家帶來了關于laravel的相關知識,其中主要介紹了關于容器、控制反轉以及依賴注入的相關問題,下面就一起來看一下什么相關的內容,希望對大家有幫助。
推薦學習:laravel
隨著現在應用的規模越來越龐大,對象之間的依賴關系也越來越復雜,耦合程度越來越高,經常會出現對象之間多重依賴的情況。對于如此龐大復雜的應用,任何修改都可能會牽一發而動全身,這就為應用的后期維護造成了很多困擾。
??為了解決對象之間耦合度高的問題,控制反轉(IoC)的思想也隨之誕生。所謂控制反轉,是面向對象編程中的一種設計原則,其目的是為了降低代碼之間的耦合程度。在 laravel 中,控制反轉是通過依賴注入(DI)的方式實現的。
??控制反轉的基本思想是借助 IoC 容器實現對象之間的依賴關系的解耦。引入 IoC 容器之后,所有對象的控制權都上交給 IoC 容器,IoC 容器成了整個系統的核心,把所有對象粘合在一起發揮作用。Laravel 中的容器即起到了這個作用。
⒈ 容器
??所謂容器,在 Laravel 中指的是 IlluminateFoundationApplication 對象,Laravel 框架在啟動時即創建了該對象。
#?public/index.php $app?=?require_once?__DIR__.'/../bootstrap/app.php'; #?bootstrap/app.php $app?=?new?IlluminateFoundationApplication( ????$_ENV['APP_BASE_PATH']????dirname(__DIR__) );
??在創建容器的過程中,Laravel 還會對容器進行一些基礎的綁定和服務注冊。Laravel 首先會將容器實例與 app 和 IlluminateContainerContainer 進行綁定;之后,Laravel 會將基礎的服務提供者注冊到容器實例中,包括事件、日志、路由服務提供者;最后,Laravel 會將框架核心 class 與其相對應的別名一起注冊到容器實例當中。
//?namespace?IlluminateFoundationApplication public?function?__construct($basePath?=?null) { ????if?($basePath)?{ ????????$this->setBasePath($basePath); ????} ????$this->registerBaseBindings(); ????$this->registerBaseServiceProviders(); ????$this->registerCoreContainerAliases(); } ???? protected?function?registerBaseBindings() { ????Static::setInstance($this); ????$this->instance('app',?$this); ????$this->instance(Container::class,?$this); ????/*?...?...?*/ } protected?function?registerBaseServiceProviders() { ????$this->register(new?EventServiceProvider($this)); ????$this->register(new?LogServiceProvider($this)); ????$this->register(new?RoutingServiceProvider($this)); } public?function?registerCoreContainerAliases() { ????foreach?([ ????????'app'?=>?[self::class,?IlluminateContractsContainerContainer::class,?IlluminateContractsFoundationApplication::class,?PsrContainerContainerInterface::class], ????????/*?...?...*/ ????????'db'?=>?[IlluminateDatabaseDatabaseManager::class,?IlluminateDatabaseConnectionResolverInterface::class], ????????'db.connection'?=>?[IlluminateDatabaseConnection::class,?IlluminateDatabaseConnectionInterface::class], ????????/*?...?...?*/ ????????'request'?=>?[IlluminatehttpRequest::class,?SymfonyComponentHttpFoundationRequest::class], ????????'router'?=>?[IlluminateRoutingRouter::class,?IlluminateContractsRoutingRegistrar::class,?IlluminateContractsRoutingBindingRegistrar::class], ????????/*?...?...?*/ ????]?as?$key?=>?$aliases)?{ ????????foreach?($aliases?as?$alias)?{ ????????????$this->alias($key,?$alias); ????????} ????} } //?namespace?IlluminateContainerContainer public?function?alias($abstract,?$alias) { ????if?($alias?===?$abstract)?{ ????????throw?new?LogicException("[{$abstract}]?is?aliased?to?itself."); ????} ????$this->aliases[$alias]?=?$abstract; ????$this->abstractAliases[$abstract][]?=?$alias; }
??在完成這三步基本的注冊之后,我們可以很方便的訪問已經注冊到容器中的對象實例。例如,可以直接通過 $app[‘app’] 或 $app[‘IlluminateContainerContainer’] 訪問容器本身,還可以通過? $app[‘db’] 直接訪問數據庫連接。
⒉ 服務提供者的注冊以及服務的訪問
注冊服務提供者
??在容器創建的過程中會注冊基礎服務提供者,其注冊過程通過調用 register() 方法完成。
//?namespace?IlluminateFoundationApplication public?function?register($provider,?$force?=?false) { ????if?(($registered?=?$this->getProvider($provider))?&&?!?$force)?{ ????????return?$registered; ????} ????if?(is_string($provider))?{ ????????$provider?=?$this->resolveProvider($provider); ????} ????$provider->register(); ????if?(property_exists($provider,?'bindings'))?{ ????????foreach?($provider->bindings?as?$key?=>?$value)?{ ????????????$this->bind($key,?$value); ????????} ????} ????if?(property_exists($provider,?'singletons'))?{ ????????foreach?($provider->singletons?as?$key?=>?$value)?{ ????????????$this->singleton($key,?$value); ????????} ????} ????$this->markAsRegistered($provider); ????if?($this->isBooted())?{ ????????$this->bootProvider($provider); ????} ????return?$provider; }
??Laravel 首先會判斷指定的服務提供者是否已經在容器中注冊(通過調用 getProvider() 方法實現),如果指定的服務提供者已經在容器中注冊,并且本次注冊操作并非強制執行,那么直接返回已經注冊好的服務提供者。
??如果不滿足上述條件,那么 Laravel 就會開始注冊服務提供者。此時,如果傳參為字符串,那么 Laravel 會默認參數為服務提供者的 class 名稱并進行實例化(通過 resolveProvider() 方法實現)。之后,就會調用服務提供者定義的 register() 方法進行注冊。以日志服務提供者為例,其 register() 方法的方法體如下
//?namespace?IlluminateLogLogServiceProvider public?function?register() { ????$this->app->singleton('log',?function?($app)?{ ????????return?new?LogManager($app); ????}); }
??register() 方法的作用就是將 IlluminateLogLogManager 對象以單例的模式注冊到容器當中,注冊完成之后,容器的 $bindings 屬性中會增加一項
$app->bindings['log']?=?[ ????'concrete'?=>?'IlluminateLogLogManager?{#162}', ????'shared'?=>?true, ];
??如果服務提供者自身還定義了 $bindings 屬性以及 $singletons 屬性,那么 Laravel 還會調用相應的 bind() 方法和 singleton() 方法完成這些服務提供者自定義的綁定的注冊。
??這之后 Laravel 會將服務提供者標記為已經注冊的狀態,隨后會調用服務提供者定義的 boot() 方法啟動服務提供者(前提是應用已經啟動)。
??在向容器中注冊綁定時,有 bind() 和 singleton() 兩種方法,其區別僅在于注冊的綁定是否為單例模式,即 shared 屬性是否為 true 。
//?namespace?IlluminateContainerContainer public?function?singleton($abstract,?$concrete?=?null) { ????$this->bind($abstract,?$concrete,?true); } public?function?bind($abstract,?$concrete?=?null,?$shared?=?false) { ????//?刪除舊的綁定 ????$this->dropStaleInstances($abstract); ????if?(is_null($concrete))?{ ????????$concrete?=?$abstract; ????} ????if?(!?$concrete?instanceof?Closure)?{ ????????if?(!?is_string($concrete))?{ ????????????throw?new?TypeError(self::class.'::bind():?Argument?#2?($concrete)?must?be?of?type?Closure|string|null'); ????????} ????????$concrete?=?$this->getClosure($abstract,?$concrete); ????} ????$this->bindings[$abstract]?=?compact('concrete',?'shared'); ????if?($this->resolved($abstract))?{ ????????$this->rebound($abstract); ????} } protected?function?getClosure($abstract,?$concrete) { ????return?function?($container,?$parameters?=?[])?use?($abstract,?$concrete)?{ ????????if?($abstract?==?$concrete)?{ ????????????return?$container->build($concrete); ????????} ????????return?$container->resolve( ????????????$concrete,?$parameters,?$raiseEvents?=?false ????????); ????}; }
??仍然以日志服務提供者為例,日志服務提供者在注冊時以單例模式進行注冊,并且 $concrete 參數為閉包。在綁定開始之前,Laravel 首先會刪除舊的綁定。由于此時 $concrete 為閉包,所以 Laravel 并不會進行什么操作,只是將綁定信息存入 $bindings 屬性當中。
訪問服務
??在服務提供者注冊完成之后,我們可以用上文提到的類似訪問數據庫連接的方式那樣訪問服務。仍然以日志服務為例,我們可以通過 $app[‘log’] 的方式訪問日志服務。另外,在 Laravel 中,我們還可以使用 facade 的方式訪問服務,例如,我們可以調用 IlluminateSupportFacadesLog::info() 來記錄日志。
//?namespace?IlluminateSupportFacadesLog class?Log?extends?Facade { ????protected?static?function?getFacadeAccessor() ????{ ????????return?'log'; ????} } //?namespace?IlluminateSupportFacadesFacade public?static?function?__callStatic($method,?$args) { ????$instance?=?static::getFacadeRoot(); ????/*?...?...?*/ ????return?$instance->$method(...$args); } public?static?function?getFacadeRoot() { ????return?static::resolveFacadeInstance(static::getFacadeAccessor()); } protected?static?function?resolveFacadeInstance($name) { ????if?(is_object($name))?{ ????????return?$name; ????} ????if?(isset(static::$resolvedInstance[$name]))?{ ????????return?static::$resolvedInstance[$name]; ????} ????if?(static::$app)?{ ????????return?static::$resolvedInstance[$name]?=?static::$app[$name]; ????} }
??在通過靜態調用的方式進行日志記錄時,首先會訪問 Facade 中的魔術方法 __callStatic() ,該方法的首先進行的就是解析出 facade 對應的服務實例,然后調用該服務實例下的方法來執行相應的功能。每個 facade 中都會定義一個 getFacadeAccessor() 方法,這個方法會返回一個 tag,在日志服務中,這個 tag 就是日志服務提供者的閉包在容器的 $bindings 屬性中的 key。也就是說,通過 facade 方式最終得到的是 $app[‘log’]。
??那么為什么可以通過關聯數組的方式訪問容器中注冊的對象/服務?IlluminateContainerContainer 實現了 ArrayAccess 并且定義了 OffsetGet() 方法,而 IlluminateFoundationApplication 繼承了 Container ,$app 為 Application 實例化的對象,所以通過關聯數組的方式訪問容器中注冊的對象時會訪問 Container 的 OffsetGet() 方法。在 OffsetGet() 方法中會調用 Container 的 make() 方法,而 make() 方法中又會調用 resolve() 方法。resolve() 方法最終會解析并返回相應的對象。
//?namespace?IlluminateContainer public?function?offsetGet($key) { ????return?$this->make($key); } public?function?make($abstract,?array?$parameters?=?[]) { ????return?$this->resolve($abstract,?$parameters); } protected?function?resolve($abstract,?$parameters?=?[],?$raiseEvents?=?true) { ????/*?...?...?*/ ????$this->with[]?=?$parameters; ????if?(is_null($concrete))?{ ????????$concrete?=?$this->getConcrete($abstract); ????} ????if?($this->isBuildable($concrete,?$abstract))?{ ????????$object?=?$this->build($concrete); ????}?else?{ ????????$object?=?$this->make($concrete); ????} ????/*?...?...?*/ ????$this->resolved[$abstract]?=?true; ????array_pop($this->with); ????return?$object; } protected?function?getConcrete($abstract) { ????if?(isset($this->bindings[$abstract]))?{ ????????return?$this->bindings[$abstract]['concrete']; ????} ????return?$abstract; } protected?function?isBuildable($concrete,?$abstract) { ????return?$concrete?===?$abstract?||?$concrete?instanceof?Closure; } public?function?build($concrete) { ????if?($concrete?instanceof?Closure)?{ ????????return?$concrete($this,?$this->getLastParameterOverride()); ????} ????/*?...?...?*/ } protected?function?getLastParameterOverride() { ????return?count($this->with)???end($this->with)?:?[]; }
??這里需要說明,在通過 $app[‘log’] 的方式解析日志服務實例時,resolve() 方法中的 $concrete 解析得到的是一個閉包,導致 isBuildable() 方法返回結果為 true,所以 Laravel 會直接調用 build() 方法。而由于此時 $concrete 是一個閉包,所以在 build() 方法中會直接執行這個閉包函數,最終返回 LogManager 實例。
⒊ 請求處理
??在基礎的綁定和服務注冊完成之后,容器創建成功并返回 $app 。之后 Laravel 會將內核(包括 Http 內核和 console 內核)和異常處理注冊到容器當中。然后 Laravel 開始處理請求。
//?namespace?bootstrap/app.php $app->singleton( ????IlluminateContractsHttpKernel::class, ????AppHttpKernel::class ); $app->singleton( ????IlluminateContractsConsoleKernel::class, ????AppConsoleKernel::class ); $app->singleton( ????IlluminateContractsDebugExceptionHandler::class, ????AppExceptionsHandler::class ); //?public/index.php $kernel?=?$app->make(IlluminateContractsHttpKernel::class); $response?=?$kernel->handle( ????$request?=?Request::capture() )->send(); $kernel->terminate($request,?$response);
??在開始處理請求之前,Laravel 首先會解析出 Http 內核對象 $kernel,即 AppHttpKernel 實例化的對象。而 AppHttpKernel 繼承了 IlluminateFoundationKernel,所以 $kernel 實際調用的是 IlluminateFoundationKernel 中的 handle() 方法。
namespace?IlluminateFoundationHttp use?IlluminateContractsDebugExceptionHandler public?function?handle($request) { ????try?{ ????????$request->enableHttpMethodParameterOverride(); ????????$response?=?$this->sendRequestThroughRouter($request); ????}?catch?(Throwable?$e)?{ ????????$this->reportException($e); ????????$response?=?$this->renderException($request,?$e); ????} ????$this->app['events']->dispatch( ????????new?RequestHandled($request,?$response) ????); ????return?$response; } //?上報錯誤 protected?function?reportException(Throwable?$e) { ????$this->app[ExceptionHandler::class]->report($e); } //?渲染錯誤信息???? protected?function?renderException($request,?Throwable?$e) { ????return?$this->app[ExceptionHandler::class]->render($request,?$e); }
??handle() 方法在處理請求的過程中如果出現任何異常或錯誤,Laravel 都會調用容器中已經注冊好的異常處理對象來上報異常并且渲染返回信息。
??在容器創建成功以后,Laravel 會將 IlluminateContractsDebugExceptionHandler 和 AppExceptionsHandler 之間的綁定注冊到容器當中,所以 Laravel 處理異常實際調用的都是 AppExceptionsHandler 中的方法。在實際開發過程中,開發者可以根據自身需要在 AppExceptionsHandler 中自定義 report() 和 render() 方法。
在 PHP 7 中,`Exception` 和 `Error` 是兩種不同的類型,但它們同時都繼承了 `Throwable` ,所以 `handler()` 方法中捕獲的是 `Throwable` 對象。
??在正式開始處理請求之前,Laravel 會進行一些引導啟動,包括加載環境變量、配置信息等,這些引導啟動在 Laravel 運行過程中起到了非常重要的作用。
//?namespace?IlluminateFoundationHttpKernel protected?$bootstrappers?=?[ ????IlluminateFoundationBootstrapLoadEnvironmentVariables::class, ????IlluminateFoundationBootstrapLoadConfiguration::class, ????IlluminateFoundationBootstrapHandleExceptions::class, ????IlluminateFoundationBootstrapRegisterFacades::class, ????IlluminateFoundationBootstrapRegisterProviders::class, ????IlluminateFoundationBootstrapBootProviders::class, ]; protected?function?sendRequestThroughRouter($request) { ????/*?...?...?*/ ????$this->bootstrap(); ????/*?...?...?*/ } public?function?bootstrap() { ????if?(!?$this->app->hasBeenBootstrapped())?{ ????????$this->app->bootstrapWith($this->bootstrappers()); ????} } //?namespace?IlluminateFoundationApplication public?function?bootstrapWith(array?$bootstrappers) { ????$this->hasBeenBootstrapped?=?true; ????foreach?($bootstrappers?as?$bootstrapper)?{ ????????$this['events']->dispatch('bootstrapping:?'.$bootstrapper,?[$this]); ????????$this->make($bootstrapper)->bootstrap($this); ????????$this['events']->dispatch('bootstrapped:?'.$bootstrapper,?[$this]); ????} }
??從代碼中可以看出,引導啟動的過程實際就是調用各個 class 中的 bootstrap() 方法。其中:
LoadEnvironmentVariables 用來加載環境變量
LoadConfiguration 用來加載 config 目錄下的配置文件
HandleExceptions 用來設置 PHP 的錯誤報告級別以及相應的異常和錯誤處理函數,另外還會設置 PHP 的程序終止執行函數
//?namespace?IlluminateFoundationBootstrapHandleExceptions public?function?bootstrap(Application?$app) { ????/*?...?...?*/ ????$this->app?=?$app; ????error_reporting(-1); ????set_error_handler([$this,?'handleError']); ????set_exception_handler([$this,?'handleException']); ????register_shutdown_function([$this,?'handleShutdown']); ????/*?...?...?*/ } public?function?handleError($level,?$message,?$file?=?'',?$line?=?0,?$context?=?[]) { ????if?(error_reporting()?&?$level)?{ ????????/*?...?...?*/ ????????throw?new?ErrorException($message,?0,?$level,?$file,?$line); ????} } public?function?handleException(Throwable?$e) { ????/*?...?...?*/ ????????$this->getExceptionHandler()->report($e); ????/*?...?...?*/ } public?function?handleShutdown() { ????if?(!?is_null($error?=?error_get_last())?&&?$this->isFatal($error['type']))?{ ????????$this->handleException($this->fatalErrorFromPhpError($error,?0)); ????} } protected?function?getExceptionHandler() { ????return?$this->app->make(IlluminateContractsDebugExceptionHandler::class); }
??從以上代碼中可以看出,雖然 HandleExceptions 中定義了異常、錯誤、程序終止的處理函數,但無論是哪種情況,最終還是調用 AppExceptionsHandler 中的方法來處理異常或錯誤。
RegisterFacades 的作用一個是注冊配置文件以及第三方包中自定義的 alias 類,還有一個非常重要的作用就是為 IlluminateSupportFacadesFacade 類設置 $app 屬性。
//?namespace?IlluminateFoundationBootstrapRegisterFAcades public?function?bootstrap(Application?$app) { ????Facade::clearResolvedInstances(); ????Facade::setFacadeApplication($app); ????AliasLoader::getInstance(array_merge( ????????$app->make('config')->get('app.aliases',?[]), ????????$app->make(PackageManifest::class)->aliases() ????))->register(); }
&emsp?我們在通過 facade 方式反問容器中注冊的服務時,Facade 在解析容器中的服務實例時用到的 static::$app 即是在這個時候設置的。
RegisterProviders 的作用是注冊配置文件以及第三方包中定義的服務提供者
//?namespace?IlluminateFoundationBootstrapRegisterProviders public?function?bootstrap(Application?$app) { ????$app->registerConfiguredProviders(); } public?function?registerConfiguredProviders() { ????$providers?=?Collection::make($this->make('config')->get('app.providers')) ????????????????????->partition(function?($provider)?{ ????????????????????????return?strpos($provider,?'Illuminate')?===?0; ????????????????????}); ????$providers->splice(1,?0,?[$this->make(PackageManifest::class)->providers()]); ????(new?ProviderRepository($this,?new?Filesystem,?$this->getCachedServicesPath())) ????????????????->load($providers->collapse()->toArray()); }
??在實際注冊的過程中,Laravel 會按照 Laravel 框架的服務提供者 > 第三方包的服務提供者 > 開發者自定義的服務提供者 的順序進行注冊
BootProviders 則是按順序調用已經注冊到容器中的服務提供者的 boot() 方法(前提是服務提供者定義的 boot() 方法)
??在引導啟動完成之后,Laravel 開始處理請求,首先要做的就是將全局的中間件應用于 request 。這之后 Laravel 會將請求分發到相應的路由進行處理,處理之前需要先根據 request 找到相應的路由對象 IlluminateRoutingRoute。在 Laravel 中,除了全局中間件,還有一些中間件只作用于特定的路由或路由分組,此時這些中間件就會被作用于 request 。這些工作都完成之后,路由對象開始執行代碼,完成請求。
//?namespace?IlluminateFoundationHttpKernel protected?function?sendRequestThroughRouter($request) { ????/*?...?...?*/ ????return?(new?Pipeline($this->app)) ????????????????->send($request) ????????????????->through($this->app->shouldSkipMiddleware()???[]?:?$this->middleware) ????????????????->then($this->dispatchToRouter()); } protected?function?dispatchToRouter() { ????return?function?($request)?{ ????????$this->app->instance('request',?$request); ???????? ????????return?$this->router->dispatch($request); ????}; } //?namespace?IlluminateRoutingRouter public?function?dispatch(Request?$request) { ????$this->currentRequest?=?$request; ????return?$this->dispatchToRoute($request); } public?function?dispatchToRoute(Request?$request) { ????return?$this->runRoute($request,?$this->findRoute($request)); } protected?function?runRoute(Request?$request,?Route?$route) { ????/*?...?...?*/ ????return?$this->prepareResponse($request, ????????$this->runRouteWithinStack($route,?$request) ????); } protected?function?runRouteWithinStack(Route?$route,?Request?$request) { ????/*?...?...?*/??? ????return?(new?Pipeline($this->container)) ????????????????????->send($request) ????????????????????->through($middleware) ????????????????????->then(function?($request)?use?($route)?{ ????????????????????????return?$this->prepareResponse( ????????????????????????????$request,?$route->run() ????????????????????????); ????????????????????}); }
⒋ 依賴注入
??Laravel 中的路由在注冊時,action 可以是控制器方法,也可以是閉包。但無論是那種形式,都需要傳參,而傳參就會遇到需要依賴注入的情況。
??Route 對象在執行 run() 方法時會根據 action 的類型分別進行控制器方法調用或閉包函數的調用。但兩種方法最終都需要解析參數,而如果參數中用到了 class ,就需要進行依賴注入。
//?namespace?IlluminateRoutingRouter public?function?run() { ????$this->container?=?$this->container??:?new?Container; ????try?{ ????????if?($this->isControllerAction())?{ ????????????return?$this->runController(); ????????} ????????return?$this->runCallable(); ????}?catch?(HttpResponseException?$e)?{ ????????return?$e->getResponse(); ????} } protected?function?runController() { ????return?$this->controllerDispatcher()->dispatch( ????????$this,?$this->getController(),?$this->getControllerMethod() ????); } protected?function?runCallable() { ????/*?...?...?*/ ????return?$callable(...array_values($this->resolveMethodDependencies( ????????$this->parametersWithoutNulls(),?new?ReflectionFunction($callable) ????))); } //?namespace?IlluminateRoutingControllerDispatcher public?function?dispatch(Route?$route,?$controller,?$method) { ????$parameters?=?$this->resolveClassMethodDependencies( ????????$route->parametersWithoutNulls(),?$controller,?$method ????); ????/*?...?...?*/ } //?namespace?IlluminateRoutingRouteDependencyResolverTrait protected?function?resolveClassMethodDependencies(array?$parameters,?$instance,?$method) { ????/*?...?...?*/ ????return?$this->resolveMethodDependencies( ????????$parameters,?new?ReflectionMethod($instance,?$method) ????); } public?function?resolveMethodDependencies(array?$parameters,?ReflectionFunctionAbstract?$reflector) { ????/*?...?...?*/ ????foreach?($reflector->getParameters()?as?$key?=>?$parameter)?{ ????????$instance?=?$this->transformDependency($parameter,?$parameters,?$skippableValue); ????????/*?...?...?*/ ????} ????return?$parameters; } protected?function?transformDependency(ReflectionParameter?$parameter,?$parameters,?$skippableValue) { ????$className?=?Reflector::getParameterClassName($parameter); ????if?($className?&&?!?$this->alreadyInParameters($className,?$parameters))?{ ????????return?$parameter->isDefaultValueAvailable()???null?:?$this->container->make($className); ????} ????return?$skippableValue; }
??在執行過程中,Laravel 首先通過反射取得參數列表(對于控制器方法,使用 ReflectionMethod ,對于閉包函數,則使用 ReflectionFunction )。在得到參數列表后,Laravel 仍然是利用反射,逐個判斷參數類型。如果參數類型為 PHP 的內置類型,那么不需要什么特殊處理;但如果參數不是 PHP 內置類型,則需要利用反射解析出參數的具體類型。在解析出參數的具體類型之后,緊接著會判斷該類型的對象是不是已經存在于參數列表中,如果不存在并且該類型也沒有設置默認值,那么就需要通過容器創建出該類型的實例。
??要通過容器創建指定 class 的實例,仍然需要用到 resolve() 方法。前文已經敘述過使用 resolve() 方法解析閉包函數的情況,所以這里值敘述實例化 class 的情況。
//?namespace?IlluminateContainerContainer public?function?build($concrete) { ????/*?...?...?*/ ????try?{ ????????$reflector?=?new?ReflectionClass($concrete); ????}?catch?(ReflectionException?$e)?{ ????????throw?new?BindingResolutionException("Target?class?[$concrete]?does?not?exist.",?0,?$e); ????} ????if?(!?$reflector->isInstantiable())?{ ????????return?$this->notInstantiable($concrete); ????} ????$this->buildStack[]?=?$concrete; ????$constructor?=?$reflector->getConstructor(); ????if?(is_null($constructor))?{ ????????array_pop($this->buildStack); ????????return?new?$concrete; ????} ????$dependencies?=?$constructor->getParameters(); ????try?{ ????????$instances?=?$this->resolveDependencies($dependencies); ????}?catch?(BindingResolutionException?$e)?{ ????????array_pop($this->buildStack); ????????throw?$e; ????} ????array_pop($this->buildStack); ????return?$reflector->newInstanceArgs($instances); } protected?function?resolveDependencies(array?$dependencies) { ????$results?=?[]; ????foreach?($dependencies?as?$dependency)?{ ????????if?($this->hasParameterOverride($dependency))?{ ????????????$results[]?=?$this->getParameterOverride($dependency); ????????????continue; ????????} ????????$result?=?is_null(Util::getParameterClassName($dependency)) ??????????????????????????$this->resolvePrimitive($dependency) ????????????????????????:?$this->resolveClass($dependency); ????????if?($dependency->isVariadic())?{ ????????????$results?=?array_merge($results,?$result); ????????}?else?{ ????????????$results[]?=?$result; ????????} ????} ????return?$results; }
??容器在實例化 class 的時候,仍然是通過反射獲取 class 基本信息。對于一些無法進行實例化的 class (例如 interface 、abstract class ),Laravel 會拋出異常;否則 Laravel 會繼續獲取 class 的構造函數的信息。對于不存在構造函數的 class ,意味著這些 class 在實例化的時候不需要額外的依賴,可以直接通過 new 來實例化;否則仍然是通過反射解析出構造函數的參數列表信息,然后逐個實例化這些參數列表中用到的 class 。在這些參數列表中的 class 都實例化完成之后,通過容器創建 class 的準備工作也已經完成,此時容器可以順利創建出指定 class 的實例,然后注入到控制器方法或閉包中。
推薦學習:laravel