久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      Laravel實(shí)例詳解之容器、控制反轉(zhuǎn)和依賴注入

      本篇文章給大家?guī)砹岁P(guān)于Laravel的相關(guān)知識,其中主要介紹了關(guān)于容器、控制反轉(zhuǎn)以及依賴注入的相關(guān)問題,下面就一起來看一下什么相關(guān)的內(nèi)容,希望對大家有幫助。

      Laravel實(shí)例詳解之容器、控制反轉(zhuǎn)和依賴注入

      推薦學(xué)習(xí):Laravel入門

      隨著現(xiàn)在應(yīng)用的規(guī)模越來越龐大,對象之間的依賴關(guān)系也越來越復(fù)雜,耦合程度越來越高,經(jīng)常會出現(xiàn)對象之間多重依賴的情況。對于如此龐大復(fù)雜的應(yīng)用,任何修改都可能會牽一發(fā)而動全身,這就為應(yīng)用的后期維護(hù)造成了很多困擾。

      ??為了解決對象之間耦合度高的問題,控制反轉(zhuǎn)(IoC)的思想也隨之誕生。所謂控制反轉(zhuǎn),是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則,其目的是為了降低代碼之間的耦合程度。在 Laravel 中,控制反轉(zhuǎn)是通過依賴注入(DI)的方式實(shí)現(xiàn)的。

      ??控制反轉(zhuǎn)的基本思想是借助 IoC 容器實(shí)現(xiàn)對象之間的依賴關(guān)系的解耦。引入 IoC 容器之后,所有對象的控制權(quán)都上交給 IoC 容器,IoC 容器成了整個系統(tǒng)的核心,把所有對象粘合在一起發(fā)揮作用。Laravel 中的容器即起到了這個作用。

      ⒈ 容器

      ??所謂容器,在 Laravel 中指的是 IlluminateFoundationApplication 對象,Laravel 框架在啟動時即創(chuàng)建了該對象。

      # public/index.php $app = require_once __DIR__.'/../bootstrap/app.php'; # bootstrap/app.php $app = new IlluminateFoundationApplication(     $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) );

      ??在創(chuàng)建容器的過程中,Laravel 還會對容器進(jìn)行一些基礎(chǔ)的綁定和服務(wù)注冊。Laravel 首先會將容器實(shí)例與 app 和 IlluminateContainerContainer 進(jìn)行綁定;之后,Laravel 會將基礎(chǔ)的服務(wù)提供者注冊到容器實(shí)例中,包括事件、日志、路由服務(wù)提供者;最后,Laravel 會將框架核心 class 與其相對應(yīng)的別名一起注冊到容器實(shí)例當(dāng)中。

      // 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; }

      ??在完成這三步基本的注冊之后,我們可以很方便的訪問已經(jīng)注冊到容器中的對象實(shí)例。例如,可以直接通過 $app['app'] 或 $app['IlluminateContainerContainer'] 訪問容器本身,還可以通過 $app['db'] 直接訪問數(shù)據(jù)庫連接。

      ⒉ 服務(wù)提供者的注冊以及服務(wù)的訪問

      注冊服務(wù)提供者

      ??在容器創(chuàng)建的過程中會注冊基礎(chǔ)服務(wù)提供者,其注冊過程通過調(diào)用 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 首先會判斷指定的服務(wù)提供者是否已經(jīng)在容器中注冊(通過調(diào)用 getProvider() 方法實(shí)現(xiàn)),如果指定的服務(wù)提供者已經(jīng)在容器中注冊,并且本次注冊操作并非強(qiáng)制執(zhí)行,那么直接返回已經(jīng)注冊好的服務(wù)提供者。

      ??如果不滿足上述條件,那么 Laravel 就會開始注冊服務(wù)提供者。此時,如果傳參為字符串,那么 Laravel 會默認(rèn)參數(shù)為服務(wù)提供者的 class 名稱并進(jìn)行實(shí)例化(通過 resolveProvider() 方法實(shí)現(xiàn))。之后,就會調(diào)用服務(wù)提供者定義的 register() 方法進(jìn)行注冊。以日志服務(wù)提供者為例,其 register() 方法的方法體如下

      // namespace IlluminateLogLogServiceProvider public function register() {     $this->app->singleton('log', function ($app) {         return new LogManager($app);     }); }

      ??register() 方法的作用就是將 IlluminateLogLogManager 對象以單例的模式注冊到容器當(dāng)中,注冊完成之后,容器的 $bindings 屬性中會增加一項(xiàng)

      $app->bindings['log'] = [     'concrete' => 'IlluminateLogLogManager {#162}',     'shared' => true, ];

      ??如果服務(wù)提供者自身還定義了 $bindings 屬性以及 $singletons 屬性,那么 Laravel 還會調(diào)用相應(yīng)的 bind() 方法和 singleton() 方法完成這些服務(wù)提供者自定義的綁定的注冊。

      ??這之后 Laravel 會將服務(wù)提供者標(biāo)記為已經(jīng)注冊的狀態(tài),隨后會調(diào)用服務(wù)提供者定義的 boot() 方法啟動服務(wù)提供者(前提是應(yīng)用已經(jīng)啟動)。

      ??在向容器中注冊綁定時,有 bind() 和 singleton() 兩種方法,其區(qū)別僅在于注冊的綁定是否為單例模式,即 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         );     }; }

      ??仍然以日志服務(wù)提供者為例,日志服務(wù)提供者在注冊時以單例模式進(jìn)行注冊,并且 $concrete 參數(shù)為閉包。在綁定開始之前,Laravel 首先會刪除舊的綁定。由于此時 $concrete 為閉包,所以 Laravel 并不會進(jìn)行什么操作,只是將綁定信息存入 $bindings 屬性當(dāng)中。

      訪問服務(wù)

      ??在服務(wù)提供者注冊完成之后,我們可以用上文提到的類似訪問數(shù)據(jù)庫連接的方式那樣訪問服務(wù)。仍然以日志服務(wù)為例,我們可以通過 $app['log'] 的方式訪問日志服務(wù)。另外,在 Laravel 中,我們還可以使用 facade 的方式訪問服務(wù),例如,我們可以調(diào)用 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];     } }

      ??在通過靜態(tài)調(diào)用的方式進(jìn)行日志記錄時,首先會訪問 Facade 中的魔術(shù)方法 __callStatic() ,該方法的首先進(jìn)行的就是解析出 facade 對應(yīng)的服務(wù)實(shí)例,然后調(diào)用該服務(wù)實(shí)例下的方法來執(zhí)行相應(yīng)的功能。每個 facade 中都會定義一個 getFacadeAccessor() 方法,這個方法會返回一個 tag,在日志服務(wù)中,這個 tag 就是日志服務(wù)提供者的閉包在容器的 $bindings 屬性中的 key。也就是說,通過 facade 方式最終得到的是 $app['log']。

      ??那么為什么可以通過關(guān)聯(lián)數(shù)組的方式訪問容器中注冊的對象/服務(wù)?IlluminateContainerContainer 實(shí)現(xiàn)了 ArrayAccess 并且定義了 OffsetGet() 方法,而 IlluminateFoundationApplication 繼承了 Container ,$app 為 Application 實(shí)例化的對象,所以通過關(guān)聯(lián)數(shù)組的方式訪問容器中注冊的對象時會訪問 Container 的 OffsetGet() 方法。在 OffsetGet() 方法中會調(diào)用 Container 的 make() 方法,而 make() 方法中又會調(diào)用 resolve() 方法。resolve() 方法最終會解析并返回相應(yīng)的對象。

      // 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'] 的方式解析日志服務(wù)實(shí)例時,resolve() 方法中的 $concrete 解析得到的是一個閉包,導(dǎo)致 isBuildable() 方法返回結(jié)果為 true,所以 Laravel 會直接調(diào)用 build() 方法。而由于此時 $concrete 是一個閉包,所以在 build() 方法中會直接執(zhí)行這個閉包函數(shù),最終返回 LogManager 實(shí)例。

      ⒊ 請求處理

      ??在基礎(chǔ)的綁定和服務(wù)注冊完成之后,容器創(chuàng)建成功并返回 $app 。之后 Laravel 會將內(nèi)核(包括 Http 內(nèi)核和 Console 內(nèi)核)和異常處理注冊到容器當(dāng)中。然后 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 內(nèi)核對象 $kernel,即 AppHttpKernel 實(shí)例化的對象。而 AppHttpKernel 繼承了 IlluminateFoundationKernel,所以 $kernel 實(shí)際調(diào)用的是 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; } // 上報(bào)錯誤 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() 方法在處理請求的過程中如果出現(xiàn)任何異?;蝈e誤,Laravel 都會調(diào)用容器中已經(jīng)注冊好的異常處理對象來上報(bào)異常并且渲染返回信息。

      ??在容器創(chuàng)建成功以后,Laravel 會將 IlluminateContractsDebugExceptionHandler 和 AppExceptionsHandler 之間的綁定注冊到容器當(dāng)中,所以 Laravel 處理異常實(shí)際調(diào)用的都是 AppExceptionsHandler 中的方法。在實(shí)際開發(fā)過程中,開發(fā)者可以根據(jù)自身需要在 AppExceptionsHandler 中自定義 report() 和 render() 方法。

      在 PHP 7 中,`Exception` 和 `Error` 是兩種不同的類型,但它們同時都繼承了 `Throwable` ,所以 `handler()` 方法中捕獲的是 `Throwable` 對象。

      ??在正式開始處理請求之前,Laravel 會進(jìn)行一些引導(dǎo)啟動,包括加載環(huán)境變量、配置信息等,這些引導(dǎo)啟動在 Laravel 運(yùn)行過程中起到了非常重要的作用。

      // 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]);     } }

      ??從代碼中可以看出,引導(dǎo)啟動的過程實(shí)際就是調(diào)用各個 class 中的 bootstrap() 方法。其中:

      LoadEnvironmentVariables 用來加載環(huán)境變量

      LoadConfiguration 用來加載 config 目錄下的配置文件

      HandleExceptions 用來設(shè)置 PHP 的錯誤報(bào)告級別以及相應(yīng)的異常和錯誤處理函數(shù),另外還會設(shè)置 PHP 的程序終止執(zhí)行函數(shù)

      // 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 中定義了異常、錯誤、程序終止的處理函數(shù),但無論是哪種情況,最終還是調(diào)用 AppExceptionsHandler 中的方法來處理異?;蝈e誤。

      RegisterFacades 的作用一個是注冊配置文件以及第三方包中自定義的 alias 類,還有一個非常重要的作用就是為 IlluminateSupportFacadesFacade 類設(shè)置 $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 方式反問容器中注冊的服務(wù)時,F(xiàn)acade 在解析容器中的服務(wù)實(shí)例時用到的 static::$app 即是在這個時候設(shè)置的。

      RegisterProviders 的作用是注冊配置文件以及第三方包中定義的服務(wù)提供者

      // 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()); }

      ??在實(shí)際注冊的過程中,Laravel 會按照 Laravel 框架的服務(wù)提供者 > 第三方包的服務(wù)提供者 > 開發(fā)者自定義的服務(wù)提供者 的順序進(jìn)行注冊

      BootProviders 則是按順序調(diào)用已經(jīng)注冊到容器中的服務(wù)提供者的 boot() 方法(前提是服務(wù)提供者定義的 boot() 方法)

      ??在引導(dǎo)啟動完成之后,Laravel 開始處理請求,首先要做的就是將全局的中間件應(yīng)用于 request 。這之后 Laravel 會將請求分發(fā)到相應(yīng)的路由進(jìn)行處理,處理之前需要先根據(jù) request 找到相應(yīng)的路由對象 IlluminateRoutingRoute。在 Laravel 中,除了全局中間件,還有一些中間件只作用于特定的路由或路由分組,此時這些中間件就會被作用于 request 。這些工作都完成之后,路由對象開始執(zhí)行代碼,完成請求。

      // 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 對象在執(zhí)行 run() 方法時會根據(jù) action 的類型分別進(jìn)行控制器方法調(diào)用或閉包函數(shù)的調(diào)用。但兩種方法最終都需要解析參數(shù),而如果參數(shù)中用到了 class ,就需要進(jìn)行依賴注入。

      // 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; }

      ??在執(zhí)行過程中,Laravel 首先通過反射取得參數(shù)列表(對于控制器方法,使用 ReflectionMethod ,對于閉包函數(shù),則使用 ReflectionFunction )。在得到參數(shù)列表后,Laravel 仍然是利用反射,逐個判斷參數(shù)類型。如果參數(shù)類型為 PHP 的內(nèi)置類型,那么不需要什么特殊處理;但如果參數(shù)不是 PHP 內(nèi)置類型,則需要利用反射解析出參數(shù)的具體類型。在解析出參數(shù)的具體類型之后,緊接著會判斷該類型的對象是不是已經(jīng)存在于參數(shù)列表中,如果不存在并且該類型也沒有設(shè)置默認(rèn)值,那么就需要通過容器創(chuàng)建出該類型的實(shí)例。

      ??要通過容器創(chuàng)建指定 class 的實(shí)例,仍然需要用到 resolve() 方法。前文已經(jīng)敘述過使用 resolve() 方法解析閉包函數(shù)的情況,所以這里值敘述實(shí)例化 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; }

      ??容器在實(shí)例化 class 的時候,仍然是通過反射獲取 class 基本信息。對于一些無法進(jìn)行實(shí)例化的 class (例如 interface 、abstract class ),Laravel 會拋出異常;否則 Laravel 會繼續(xù)獲取 class 的構(gòu)造函數(shù)的信息。對于不存在構(gòu)造函數(shù)的 class ,意味著這些 class 在實(shí)例化的時候不需要額外的依賴,可以直接通過 new 來實(shí)例化;否則仍然是通過反射解析出構(gòu)造函數(shù)的參數(shù)列表信息,然后逐個實(shí)例化這些參數(shù)列表中用到的 class 。在這些參數(shù)列表中的 class 都實(shí)例化完成之后,通過容器創(chuàng)建 class 的準(zhǔn)備工作也已經(jīng)完成,此時容器可以順利創(chuàng)建出指定 class 的實(shí)例,然后注入到控制器方法或閉包中。

      推薦學(xué)習(xí):Laravel入門

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號