深度挖掘 Laravel 生命周期
這篇文章我們來(lái)聊聊 「Laravel 生命周期」 這個(gè)主題。雖然網(wǎng)絡(luò)上已經(jīng)有很多關(guān)于這個(gè)主題的探討,但這個(gè)主題依然值得我們?nèi)パ芯亢蛯W(xué)習(xí)。
我想說(shuō)的是當(dāng)我們?cè)跊Q定使用某項(xiàng)技術(shù)的時(shí)候,除了需要了解它能「做什么」,其實(shí)還應(yīng)當(dāng)研究它是「怎么做的」。
Laravel 框架或者說(shuō)任何一個(gè) Web 項(xiàng)目,我們都需要理解它究竟是如何接收到用戶發(fā)起的 HTTP 請(qǐng)求的;又是如何響應(yīng)結(jié)果給用戶的;在處理請(qǐng)求和響應(yīng)的過(guò)程中都存在哪些處理值得深入學(xué)習(xí)。
所有這些內(nèi)容其實(shí)都包含在 「Laravel 生命周期」 這個(gè)主題里面。
本文較長(zhǎng)建議使用合適的 IDE 進(jìn)行代碼查閱;或者通過(guò)文中的鏈接,或是代碼注釋的 「@see」部分直接在 Github 暢讀代碼。
目錄結(jié)構(gòu)
- 一 摘要
-
二 生命周期之始末
- 2.1 加載項(xiàng)目依賴
-
2.2 創(chuàng)建 Laravel 應(yīng)用實(shí)例
- 2.2.1 創(chuàng)建應(yīng)用實(shí)例
- 2.2.2 內(nèi)核綁定
- 2.2.3 注冊(cè)異常處理
- 2.2.4 小結(jié)
-
2.3 接收請(qǐng)求并響應(yīng)
- 2.3.1 解析內(nèi)核實(shí)例
-
2.3.2 處理 HTTP 請(qǐng)求
- 2.3.2.1 創(chuàng)建請(qǐng)求實(shí)例
-
2.3.2.2 處理請(qǐng)求
- 2.3.2.2.1 啟動(dòng)「引導(dǎo)程序」
- 2.3.2.2.2 發(fā)送請(qǐng)求至路由
- 2.4 發(fā)送響應(yīng)
- 2.5 終止程序
- 三 總結(jié)
- 四 生命周期流程圖
- 參考資料
一 摘要
Laravel 生命周期(或者說(shuō)請(qǐng)求生命周期)概括起來(lái)主要分為 3 個(gè)主要階段:
- 加載項(xiàng)目依賴
- 創(chuàng)建 Laravel 應(yīng)用實(shí)例
- 接收請(qǐng)求并響應(yīng)
而這 3 個(gè)階段的處理都發(fā)生在入口文件 public/index.php 文件內(nèi)(public/index.php 是一個(gè)新安裝的 Laravel 項(xiàng)目默認(rèn)入口文件)。
然而 index.php 文件僅包含極少的代碼,但卻出色的完成了一個(gè) HTTP 請(qǐng)求從接收到響應(yīng)的全部過(guò)程,邏輯組織的幾近完美。
我們來(lái)看下入口文件實(shí)現(xiàn)的代碼:
<?php // 階段一 require __DIR__.'/../vendor/autoload.php';// 階段二 $app = require_once __DIR__.'/../bootstrap/app.php';// 階段三 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle($request = Illuminate\Http\Request::capture() );$response->send();// 其它 $kernel->terminate($request, $response);二 生命周期之始末
2.1 加載項(xiàng)目依賴
現(xiàn)代 PHP 依賴于 Composer 包管理器,入口文件通過(guò)引入由 Composer 包管理器自動(dòng)生成的類加載程序,可以輕松注冊(cè)并加載項(xiàng)目所依賴的第三方組件庫(kù)。
所有組件的加載工作,僅需一行代碼即可完成:
require __DIR__.'/../vendor/autoload.php';2.2 創(chuàng)建 Laravel 應(yīng)用實(shí)例
創(chuàng)建應(yīng)用實(shí)例(或稱服務(wù)容器),由位于 bootstrap/app.php 文件里的引導(dǎo)程序完成,創(chuàng)建服務(wù)容器的過(guò)程即為應(yīng)用初始化的過(guò)程,項(xiàng)目初始化時(shí)將完成包括:注冊(cè)項(xiàng)目基礎(chǔ)服務(wù)、注冊(cè)項(xiàng)目服務(wù)提供者別名、注冊(cè)目錄路徑等在內(nèi)的一些列注冊(cè)工作。
下面是 bootstrap/app.php 的代碼,包含兩個(gè)主要部分「創(chuàng)建應(yīng)用實(shí)例」和「綁定內(nèi)核至 APP 服務(wù)容器」:
<?php // 第一部分: 創(chuàng)建應(yīng)用實(shí)例 $app = new Illuminate\Foundation\Application(realpath(__DIR__.'/../') );// 第二部分: 完成內(nèi)核綁定 $app->singleton(Illuminate\Contracts\Http\Kernel::class,App\Http\Kernel::class );$app->singleton(Illuminate\Contracts\Console\Kernel::class,App\Console\Kernel::class );$app->singleton(Illuminate\Contracts\Debug\ExceptionHandler::class,App\Exceptions\Handler::class );return $app;2.2.1 創(chuàng)建應(yīng)用實(shí)例
創(chuàng)建應(yīng)用實(shí)例即實(shí)例化 Illuminate\Foundation\Application 這個(gè)服務(wù)容器,后續(xù)我們稱其為 APP 容器。在創(chuàng)建 APP 容器主要會(huì)完成:注冊(cè)應(yīng)用的基礎(chǔ)路徑并將路徑綁定到 APP 容器 、注冊(cè)基礎(chǔ)服務(wù)提供者至 APP 容器 、注冊(cè)核心容器別名至 APP 容器 等基礎(chǔ)服務(wù)的注冊(cè)工作。
/*** Create a new Illuminate application instance.** @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Application.php#L162:27* @param string|null $basePath* @return void*/public function __construct($basePath = null){if ($basePath) {$this->setBasePath($basePath);}$this->registerBaseBindings();$this->registerBaseServiceProviders();$this->registerCoreContainerAliases();}2.2.2 內(nèi)核綁定
接著將關(guān)注的焦點(diǎn)轉(zhuǎn)移到綁定內(nèi)核部分。
Laravel 會(huì)依據(jù) HTTP 請(qǐng)求的運(yùn)行環(huán)境的不同,將請(qǐng)求發(fā)送至相應(yīng)的內(nèi)核: HTTP 內(nèi)核 或 Console 內(nèi)核。無(wú)論 HTTP 內(nèi)核還是 Console 內(nèi)核,它們的作用都是是接收一個(gè) HTTP 請(qǐng)求,隨后返回一個(gè)響應(yīng),就是這么簡(jiǎn)單。
這篇文章主要研究 HTTP 內(nèi)核,HTTP 內(nèi)核繼承自 Illuminate\Foundation\Http\Kernel 類.
在 「HTTP 內(nèi)核」 內(nèi)它定義了 中間件 相關(guān)數(shù)組;在 「Illuminate\Foundation\Http\Kernel」 類內(nèi)部定義了屬性名為 「bootstrappers」 的 引導(dǎo)程序 數(shù)組。
- 中間件 提供了一種方便的機(jī)制來(lái)過(guò)濾進(jìn)入應(yīng)用的 HTTP 請(qǐng)求。
- 「引導(dǎo)程序」 包括完成環(huán)境檢測(cè)、配置加載、異常處理、Facades 注冊(cè)、服務(wù)提供者注冊(cè)、啟動(dòng)服務(wù)這六個(gè)引導(dǎo)程序。
至于 「中間件」 和 「引導(dǎo)程序」如何被使用的,會(huì)在后面的章節(jié)講解。
2.2.3 注冊(cè)異常處理
項(xiàng)目的異常處理由 App\Exceptions\Handler::class 類完成,這邊也不做深入的講解。
2.2.4 本節(jié)小結(jié)
通過(guò)上面的分析我們可以發(fā)現(xiàn)在「創(chuàng)建 Laravel 應(yīng)用實(shí)例」這個(gè)階段它做了很多的基礎(chǔ)工作,包括但不限于:創(chuàng)建 APP 容器、注冊(cè)應(yīng)用路徑、注冊(cè)基礎(chǔ)服務(wù)提供者、配置中間件和引導(dǎo)程序等。
2.3 接收請(qǐng)求并響應(yīng)
在完成創(chuàng)建 APP 容器后即進(jìn)入了第三個(gè)階段 「接收請(qǐng)求并響應(yīng)」。
「接收請(qǐng)求并響應(yīng)」有關(guān)代碼如下:
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle($request = Illuminate\Http\Request::capture() );$response->send();我們需要逐行分析上面的代碼,才能窺探其中的原貌。
2.3.1 解析內(nèi)核實(shí)例
在第二階段我們已經(jīng)將 HTTP 內(nèi)核 和 Console 內(nèi)核 綁定到了 APP 容器,使用時(shí)通過(guò) APP 容器 的 make() 方法將內(nèi)核解析出來(lái),解析的過(guò)程就是內(nèi)核實(shí)例化的過(guò)程。
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);內(nèi)核實(shí)例化時(shí)它的內(nèi)部究竟又做了哪些操作呢?
進(jìn)一步挖掘 Illuminate\Foundation\Http\Kernel 內(nèi)核的 __construct(IlluminateContractsFoundationApplication $app, \Illuminate\Routing\Router $router) 構(gòu)造方法,它接收 APP 容器 和 路由器 兩個(gè)參數(shù)。
在實(shí)例化內(nèi)核時(shí),構(gòu)造函數(shù)內(nèi)將在 HTTP 內(nèi)核定義的「中間件組」注冊(cè)到 路由器,注冊(cè)完后就可以在實(shí)際處理 HTTP 請(qǐng)求前調(diào)用這些「中間件」實(shí)現(xiàn) 過(guò)濾 請(qǐng)求的目的。
.../*** Create a new HTTP kernel instance. 創(chuàng)建 HTTP 內(nèi)核實(shí)例* * @class Illuminate\Foundation\Http\Kernel* @param \Illuminate\Contracts\Foundation\Application $app* @param \Illuminate\Routing\Router $router* @return void*/public function __construct(Application $app, Router $router){$this->app = $app;$this->router = $router;$router->middlewarePriority = $this->middlewarePriority;foreach ($this->middlewareGroups as $key => $middleware) {$router->middlewareGroup($key, $middleware);}foreach ($this->routeMiddleware as $key => $middleware) {$router->aliasMiddleware($key, $middleware);}} ... .../*** Register a group of middleware. 注冊(cè)中間件組** @class \Illuminate\Routing\Router* @param string $name* @param array $middleware* @return $this*/public function middlewareGroup($name, array $middleware){$this->middlewareGroups[$name] = $middleware;return $this;}/*** Register a short-hand name for a middleware. 注冊(cè)中間件別名** @class \Illuminate\Routing\Router* @param string $name* @param string $class* @return $this*/public function aliasMiddleware($name, $class){$this->middleware[$name] = $class;return $this;} ...2.3.2 處理 HTTP 請(qǐng)求
之前的所有處理,基本都是圍繞在配置變量、注冊(cè)服務(wù)等運(yùn)行環(huán)境的構(gòu)建上,構(gòu)建完成后才是真刀真槍的來(lái)處理一個(gè)「HTTP 請(qǐng)求」。
處理請(qǐng)求實(shí)際包含兩個(gè)階段:
- 創(chuàng)建請(qǐng)求實(shí)例
- 處理請(qǐng)求
2.3.2.1 創(chuàng)建請(qǐng)求實(shí)例
請(qǐng)求實(shí)例 Illuminate\Http\Request 的 capture() 方法內(nèi)部通過(guò) Symfony 實(shí)例創(chuàng)建一個(gè) Laravel 請(qǐng)求實(shí)例。這樣我們就可以獲取到用戶請(qǐng)求報(bào)文的相關(guān)信息了。
/*** Create a new Illuminate HTTP request from server variables.* * @class Illuminate\Http\Request* @return static*/public static function capture(){static::enableHttpMethodParameterOverride();return static::createFromBase(SymfonyRequest::createFromGlobals());}/*** Create an Illuminate request from a Symfony instance.** @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Request.php* @param \Symfony\Component\HttpFoundation\Request $request* @return \Illuminate\Http\Request*/public static function createFromBase(SymfonyRequest $request){if ($request instanceof static) {return $request;}$content = $request->content;$request = (new static)->duplicate($request->query->all(), $request->request->all(), $request->attributes->all(),$request->cookies->all(), $request->files->all(), $request->server->all());$request->content = $content;$request->request = $request->getInputSource();return $request;}2.3.2.2 處理請(qǐng)求
請(qǐng)求處理發(fā)生在 HTTP 內(nèi)核 的 handle() 方法內(nèi)。
/*** Handle an incoming HTTP request.** @class Illuminate\Foundation\Http\Kernel* @param \Illuminate\Http\Request $request* @return \Illuminate\Http\Response*/public function handle($request){try {$request->enableHttpMethodParameterOverride();$response = $this->sendRequestThroughRouter($request);} catch (Exception $e) {$this->reportException($e);$response = $this->renderException($request, $e);} catch (Throwable $e) {$this->reportException($e = new FatalThrowableError($e));$response = $this->renderException($request, $e);}$this->app['events']->dispatch(new Events\RequestHandled($request, $response));return $response;}handle() 方法接收一個(gè) HTTP 請(qǐng)求,并最終生成一個(gè) HTTP 響應(yīng)。
繼續(xù)深入到處理 HTTP 請(qǐng)求的方法 $this->sendRequestThroughRouter($request) 內(nèi)部。
/*** Send the given request through the middleware / router.** @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php* @param \Illuminate\Http\Request $request* @return \Illuminate\Http\Response*/protected function sendRequestThroughRouter($request){$this->app->instance('request', $request);Facade::clearResolvedInstance('request');$this->bootstrap();return (new Pipeline($this->app))->send($request)->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)->then($this->dispatchToRouter());}將發(fā)現(xiàn)這段代碼沒(méi)有一行廢話,它完成了大量的邏輯處理:
- 首先,將 $request 實(shí)例注冊(cè)到 APP 容器 供后續(xù)使用;
- 之后,清除之前 $request 實(shí)例緩存;
- 然后,啟動(dòng)「引導(dǎo)程序」;
- 最后,發(fā)送請(qǐng)求至路由。
2.3.2.2.1 啟動(dòng)「引導(dǎo)程序」
記得我們?cè)谥啊?.2.2 內(nèi)核綁定」章節(jié),有介紹在「HTTP 內(nèi)核」中有把「引導(dǎo)程序(bootstrappers)」綁定到了 APP 容器,以及這些引導(dǎo)程序的具體功能。
但是沒(méi)有聊如何調(diào)用這些「引導(dǎo)程序」。
/*** Send the given request through the middleware / router.** @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php* @param \Illuminate\Http\Request $request* @return \Illuminate\Http\Response*/protected function sendRequestThroughRouter($request){...// 啟動(dòng) 「引導(dǎo)程序」$this->bootstrap();...}上面的代碼塊說(shuō)明在 $this->bootstrap(); 方法內(nèi)部有實(shí)際調(diào)用「引導(dǎo)程序」,而 bootstrap() 實(shí)際調(diào)用的是 APP 容器的 bootstrapWith(),來(lái)看看
... /*** The bootstrap classes for the application. 應(yīng)用的引導(dǎo)程序** @var array*/protected $bootstrappers = [\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,\Illuminate\Foundation\Bootstrap\HandleExceptions::class,\Illuminate\Foundation\Bootstrap\RegisterFacades::class,\Illuminate\Foundation\Bootstrap\RegisterProviders::class,\Illuminate\Foundation\Bootstrap\BootProviders::class,];/*** Bootstrap the application for HTTP requests.* * @class Illuminate\Foundation\Http\Kernel* @return void*/public function bootstrap(){if (! $this->app->hasBeenBootstrapped()) {$this->app->bootstrapWith($this->bootstrappers());}}protected function bootstrappers(){return $this->bootstrappers;} ...最終還是要看 Illuminate\Foundation\Application 的 bootstrapWith() 方法究竟如何來(lái)啟動(dòng)這些引導(dǎo)程序的。
/*** Run the given array of bootstrap classes.* * @class Illuminate\Foundation\Application APP 容器* @param array $bootstrappers* @return void*/public function bootstrapWith(array $bootstrappers){$this->hasBeenBootstrapped = true;foreach ($bootstrappers as $bootstrapper) {$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);$this->make($bootstrapper)->bootstrap($this);$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);}}我們看到在 APP 容器內(nèi),會(huì)先解析對(duì)應(yīng)的「引導(dǎo)程序」(即實(shí)例化),隨后調(diào)用「引導(dǎo)程序」的 bootstrap() 完成的「引導(dǎo)程序」的啟動(dòng)操作。
作為示例我們隨便挑一個(gè)「引導(dǎo)程序」來(lái)看看其內(nèi)部的啟動(dòng)原理。
這邊我們選 Illuminate\Foundation\Bootstrap\LoadConfiguration::class,它的功能是加載配置文件。
還記得我們講解「2.2 創(chuàng)建 Laravel 應(yīng)用實(shí)例」章節(jié)的時(shí)候有「注冊(cè)應(yīng)用的基礎(chǔ)路徑并將路徑綁定到 APP 容器」。此時(shí),LoadConfiguration 類就是將 config 目錄下的所有配置文件讀取到一個(gè)集合中,這樣我們就可以項(xiàng)目里通過(guò) config() 輔助函數(shù)獲取配置數(shù)據(jù)。
<?php class LoadConfiguration {/*** Bootstrap the given application.** @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php* @param \Illuminate\Contracts\Foundation\Application $app* @return void*/public function bootstrap(Application $app){$items = [];if (file_exists($cached = $app->getCachedConfigPath())) {$items = require $cached;$loadedFromCache = true;}$app->instance('config', $config = new Repository($items));if (! isset($loadedFromCache)) {$this->loadConfigurationFiles($app, $config);}$app->detectEnvironment(function () use ($config) {return $config->get('app.env', 'production');});date_default_timezone_set($config->get('app.timezone', 'UTC'));mb_internal_encoding('UTF-8');}/*** Load the configuration items from all of the files.** @param \Illuminate\Contracts\Foundation\Application $app* @param \Illuminate\Contracts\Config\Repository $repository* @return void* @throws \Exception*/protected function loadConfigurationFiles(Application $app, RepositoryContract $repository){$files = $this->getConfigurationFiles($app);if (! isset($files['app'])) {throw new Exception('Unable to load the "app" configuration file.');}foreach ($files as $key => $path) {$repository->set($key, require $path);}}... }所有 「引導(dǎo)程序」列表功能如下:
- IlluminateFoundationBootstrapLoadEnvironmentVariables : 環(huán)境檢測(cè),通過(guò) DOTENV 組件將 .env 配置文件載入到 $_ENV 變量中;
- IlluminateFoundationBootstrapLoadConfiguration : 加載配置文件,這個(gè)我們剛剛分析過(guò);
- IlluminateFoundationBootstrapHandleExceptions : 異常處理;
- IlluminateFoundationBootstrapRegisterFacades : 注冊(cè) Facades,注冊(cè)完成后可以以別名的方式訪問(wèn)具體的類;
- IlluminateFoundationBootstrapRegisterProviders : 注冊(cè)服務(wù)提供者,我們?cè)?「2.2.1 創(chuàng)建應(yīng)用實(shí)例」已經(jīng)將基礎(chǔ)服務(wù)提供者注冊(cè)到 APP 容器。在這里我們會(huì)將配置在 app.php 文件夾下 providers 節(jié)點(diǎn)的服務(wù)器提供者注冊(cè)到 APP 容器,供請(qǐng)求處理階段使用;
- IlluminateFoundationBootstrapBootProviders : 啟動(dòng)服務(wù)
2.3.2.2.2 發(fā)送請(qǐng)求至路由
完成「引導(dǎo)程序」啟動(dòng)操作后,隨機(jī)進(jìn)入到請(qǐng)求處理階段。
/*** Send the given request through the middleware / router.** @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php* @param \Illuminate\Http\Request $request* @return \Illuminate\Http\Response*/protected function sendRequestThroughRouter($request){...// 發(fā)送請(qǐng)求至路由return (new Pipeline($this->app))->send($request)->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)->then($this->dispatchToRouter());}在 「發(fā)送請(qǐng)求至路由」這行代碼中,完成了:管道(pipeline)創(chuàng)建、將 $request 傳入管道、對(duì) $request 執(zhí)行「中間件」處理和實(shí)際的請(qǐng)求處理四個(gè)不同的操作。
在開(kāi)始前我們需要知道在 Laravel 中有個(gè)「中間件」 的概念,即使你還不知道,也沒(méi)關(guān)系,僅需知道它的功能是在處理請(qǐng)求操作之前,對(duì)請(qǐng)求進(jìn)行過(guò)濾處理即可,僅當(dāng)請(qǐng)求符合「中間件」的驗(yàn)證規(guī)則時(shí)才會(huì)繼續(xù)執(zhí)行后續(xù)處理。
有關(guān) 「管道」的相關(guān)知識(shí)不在本文講解范圍內(nèi)。
那么,究竟一個(gè)請(qǐng)求是如何被處理的呢?
我們來(lái)看看 $this->dispatchToRouter() 這句代碼,它的方法聲明如下:
/*** Get the route dispatcher callback. 獲取一個(gè)路由分發(fā)器匿名函數(shù)** @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php* @return \Closure*/protected function dispatchToRouter(){return function ($request) {$this->app->instance('request', $request);return $this->router->dispatch($request);};}回顧下「2.3.1 解析內(nèi)核實(shí)例」章節(jié),可知我們已經(jīng)將 Illuminate\Routing\Router 對(duì)象賦值給 $this->router 屬性。
通過(guò) router 實(shí)例的 disptach() 方法去執(zhí)行 HTTP 請(qǐng)求,在它的內(nèi)部會(huì)完成如下處理:
執(zhí)行 $route->run() 的方法定義在 Illuminate\Routing\Route 類中,最終執(zhí)行「在 routes/web.php 配置的匹配到的控制器或匿名函數(shù)」:
/*** Run the route action and return the response.* * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Routing/Route.php* @return mixed*/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();}}這部分如果路由的實(shí)現(xiàn)是一個(gè)控制器,會(huì)完成控制器實(shí)例化并執(zhí)行指定方法;如果是一個(gè)匿名函數(shù)則直接調(diào)用這個(gè)匿名函數(shù)。
其執(zhí)行結(jié)果會(huì)通過(guò) Illuminate\Routing\Router::prepareResponse($request, $response) 生一個(gè)響應(yīng)實(shí)例并返回。
至此,Laravel 就完成了一個(gè) HTTP 請(qǐng)求的請(qǐng)求處理。
2.4 發(fā)送響應(yīng)
經(jīng)過(guò)一系列漫長(zhǎng)的操作,HTTP 請(qǐng)求進(jìn)入的最終章 - 發(fā)送響應(yīng)值客戶端 $response->send()。
<?php // @see https://github.com/laravel/laravel/blob/master/public/index.php// 階段一 require __DIR__.'/../vendor/autoload.php';// 階段二 $app = require_once __DIR__.'/../bootstrap/app.php';// 階段三 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle($request = Illuminate\Http\Request::capture() );// 發(fā)送響應(yīng) $response->send();// 其它 $kernel->terminate($request, $response);發(fā)送響應(yīng)由 Illuminate\Http\Response 父類 Symfony\Component\HttpFoundation\Response 中的 send() 方法完成。
/*** Sends HTTP headers and content.* * @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Response.php* @return $this*/public function send(){$this->sendHeaders();// 發(fā)送響應(yīng)頭部信息$this->sendContent();// 發(fā)送報(bào)文主題if (function_exists('fastcgi_finish_request')) {fastcgi_finish_request();} elseif (!\in_array(PHP_SAPI, array('cli', 'phpdbg'), true)) {static::closeOutputBuffers(0, true);}return $this;}2.5 終止程序
程序終止,完成終止中間件的調(diào)用 // @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.phppublic function terminate($request, $response) {$this->terminateMiddleware($request, $response);$this->app->terminate(); }// 終止中間件 protected function terminateMiddleware($request, $response) {$middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge($this->gatherRouteMiddleware($request),$this->middleware);foreach ($middlewares as $middleware) {if (! is_string($middleware)) {continue;}list($name, $parameters) = $this->parseMiddleware($middleware);$instance = $this->app->make($name);if (method_exists($instance, 'terminate')) {$instance->terminate($request, $response);}} }以上便是 Laravel 的請(qǐng)求生命周期的始末。
三 總結(jié)
在 「創(chuàng)建 Laravel 應(yīng)用實(shí)例」時(shí)不僅會(huì)注冊(cè)項(xiàng)目基礎(chǔ)服務(wù)、注冊(cè)項(xiàng)目服務(wù)提供者別名、注冊(cè)目錄路徑等在內(nèi)的一系列注冊(cè)工作;還會(huì)綁定 HTTP 內(nèi)核及 Console 內(nèi)核到 APP 容器, 同時(shí)在 HTTP 內(nèi)核里配置中間件和引導(dǎo)程序。
進(jìn)入 「接收請(qǐng)求并響應(yīng)」里,會(huì)依據(jù)運(yùn)行環(huán)境從 APP 容器 解析出 HTTP 內(nèi)核或 Console 內(nèi)核。如果是 HTTP 內(nèi)核,還將把「中間件」及「引導(dǎo)程序」注冊(cè)到 APP 容器。
所有初始化工作完成后便進(jìn)入「處理 HTTP 請(qǐng)求」階段。
一個(gè) Http 請(qǐng)求實(shí)例會(huì)被注冊(cè)到 APP 容器,通過(guò)啟動(dòng)「引導(dǎo)程序」來(lái)設(shè)置環(huán)境變量、加載配置文件等等系統(tǒng)環(huán)境配置;
隨后請(qǐng)求被分發(fā)到匹配的路由,在路由中執(zhí)行「中間件」以過(guò)濾不滿足校驗(yàn)規(guī)則的請(qǐng)求,只有通過(guò)「中間件」處理的請(qǐng)求才最終處理實(shí)際的控制器或匿名函數(shù)生成響應(yīng)結(jié)果。
最后發(fā)送響應(yīng)給用戶,清理項(xiàng)目中的中間件,完成一個(gè) 「請(qǐng)求」 - 「響應(yīng)」 的生命周期,之后我們的 Web 服務(wù)器將等待下一輪用戶請(qǐng)求。
參考資料
感謝下列優(yōu)秀的 Laravel 研究資料:
- http://blog.mallow-tech.com/2...
- http://laravel-recipes.com/re...
- http://www.cnblogs.com/sweng/...
- https://www.dyike.com/2017/04...
- http://www.cnblogs.com/wxw16/...
- http://www.php.cn/php-weiziji...
- https://segmentfault.com/a/11...
- https://segmentfault.com/a/11...
- https://blog.csdn.net/cDonDon...
- https://segmentfault.com/a/11...
總結(jié)
以上是生活随笔為你收集整理的深度挖掘 Laravel 生命周期的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: commons-lang3:DateUt
- 下一篇: Hyper-V数据文件丢失解决方案(有图