Http Kernel是Laravel中用來串聯框架的各個核心組件來網絡請求的,簡單的說只要是經過public/index.php
來啓動框架的都會用到Http Kernel,而另外的相似經過artisan
命令、計劃任務、隊列啓動框架進行處理的都會用到Console Kernel, 今天咱們先梳理一下Http Kernel作的事情。php
既然Http Kernel是Laravel中用來串聯框架的各個部分處理網絡請求的,咱們來看一下內核是怎麼加載到Laravel中應用實例中來的,在public/index.php
中咱們就會看見首先就會經過bootstrap/app.php
這個腳手架文件來初始化應用程序:laravel
下面是 bootstrap/app.php
的代碼,包含兩個主要部分建立應用實例和綁定內核至 APP 服務容器git
<?php // 第一部分: 建立應用實例 $app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') ); // 第二部分: 完成內核綁定 $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;
HTTP 內核繼承自 IlluminateFoundationHttpKernel類,在 HTTP 內核中 內它定義了中間件相關數組, 中間件提供了一種方便的機制來過濾進入應用的 HTTP 請求和加工流出應用的HTTP響應。github
<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { /** * The application's global HTTP middleware stack. * * These middleware are run during every request to your application. * * @var array */ protected $middleware = [ \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, \App\Http\Middleware\TrustProxies::class, ]; /** * The application's route middleware groups. * * @var array */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:60,1', 'bindings', ], ]; /** * The application's route middleware. * * These middleware may be assigned to groups or used individually. * * @var array */ protected $routeMiddleware = [ 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, ]; }
在其父類 「IlluminateFoundationHttpKernel」 內部定義了屬性名爲 「bootstrappers」 的 引導程序 數組:web
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, ];
引導程序組中 包括完成環境檢測、配置加載、異常處理、Facades 註冊、服務提供者註冊、啓動服務這六個引導程序。bootstrap
有關中間件和引導程序相關內容的講解能夠瀏覽咱們以前相關章節的內容。api
在將應用初始化階段將Http內核綁定至應用的服務容器後,緊接着在public/index.php
中咱們能夠看到使用了服務容器的make
方法將Http內核實例解析了出來:數組
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
在實例化內核時,將在 HTTP 內核中定義的中間件註冊到了 路由器,註冊完後就能夠在實際處理 HTTP 請求前調用路由上應用的中間件實現過濾請求的目的:瀏覽器
namespace Illuminate\Foundation\Http; ... class Kernel implements KernelContract { /** * Create a new HTTP kernel instance. * * @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); } } } namespace Illuminate/Routing; class Router implements RegistrarContract, BindingRegistrar { /** * Register a group of middleware. * * @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. * * @param string $name * @param string $class * @return $this */ public function aliasMiddleware($name, $class) { $this->middleware[$name] = $class; return $this; } }
經過服務解析完成Http內核實例的建立後就能夠用HTTP內核實例來處理HTTP請求了網絡
//public/index.php $response = $kernel->handle( $request = Illuminate\Http\Request::capture() );
在處理請求以前會先經過Illuminate\Http\Request
的 capture()
方法以進入應用的HTTP請求的信息爲基礎建立出一個 Laravel Request請求實例,在後續應用剩餘的生命週期中Request
請求實例就是對本次HTTP請求的抽象,關於Laravel Request請求實例的講解能夠參考之前的章節。
將HTTP請求抽象成Laravel Request請求實例
後,請求實例會被傳導進入到HTTP內核的handle
方法內部,請求的處理就是由handle
方法來完成的。
namespace Illuminate\Foundation\Http; class Kernel implements KernelContract { /** * Handle an incoming HTTP request. * * @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
方法接收一個請求對象,並最終生成一個響應對象。其實handle
方法咱們已經很熟悉了在講解不少模塊的時候都是以它爲出發點逐步深刻到模塊的內部去講解模塊內的邏輯的,其中sendRequestThroughRouter
方法在服務提供者和中間件都提到過,它會加載在內核中定義的引導程序來引導啓動應用而後會將使用Pipeline
對象傳輸HTTP請求對象流經框架中定義的HTTP中間件們和路由中間件們來完成過濾請求最終將請求傳遞給處理程序(控制器方法或者路由中的閉包)由處理程序返回相應的響應。關於handle
方法的註解我直接引用之前章節的講解放在這裏,具體更詳細的分析具體是如何引導啓動應用以及如何將傳輸流經各個中間件併到達處理程序的內容請查看服務提供器、中間件還有路由這三個章節。
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()); } /*引導啓動Laravel應用程序 1. DetectEnvironment 檢查環境 2. LoadConfiguration 加載應用配置 3. ConfigureLogging 配置日至 4. HandleException 註冊異常處理的Handler 5. RegisterFacades 註冊Facades 6. RegisterProviders 註冊Providers 7. BootProviders 啓動Providers */ public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { /**依次執行$bootstrappers中每個bootstrapper的bootstrap()函數 $bootstrappers = [ 'Illuminate\Foundation\Bootstrap\DetectEnvironment', 'Illuminate\Foundation\Bootstrap\LoadConfiguration', 'Illuminate\Foundation\Bootstrap\ConfigureLogging', 'Illuminate\Foundation\Bootstrap\HandleExceptions', 'Illuminate\Foundation\Bootstrap\RegisterFacades', 'Illuminate\Foundation\Bootstrap\RegisterProviders', 'Illuminate\Foundation\Bootstrap\BootProviders', ];*/ $this->app->bootstrapWith($this->bootstrappers()); } }
通過上面的幾個階段後咱們最終拿到了要返回的響應,接下來就是發送響應了。
//public/index.php $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); // 發送響應 $response->send();
發送響應由 Illuminate\Http\Response
的send()
方法完成父類其定義在父類Symfony\Component\HttpFoundation\Response
中。
public function send() { $this->sendHeaders();// 發送響應頭部信息 $this->sendContent();// 發送報文主題 if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } elseif (!\in_array(PHP_SAPI, array('cli', 'phpdbg'), true)) { static::closeOutputBuffers(0, true); } return $this; }
關於Response對象的詳細分析能夠參看咱們以前講解Laravel Response對象的章節。
響應發送後,HTTP內核會調用terminable
中間件作一些後續的處理工做。好比,Laravel 內置的「session」中間件會在響應發送到瀏覽器以後將會話數據寫入存儲器中。
// public/index.php // 終止程序 $kernel->terminate($request, $response);
//Illuminate\Foundation\Http\Kernel public 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); } } }
Http內核的terminate
方法會調用teminable
中間件的terminate
方法,調用完成後從HTTP請求進來到返回響應整個應用程序的生命週期就結束了。
本節介紹的HTTP內核起到的主要是串聯做用,其中設計到的初始化應用、引導應用、將HTTP請求抽象成Request對象、傳遞Request對象經過中間件到達處理程序生成響應以及響應發送給客戶端。這些東西在以前的章節裏都有講過,並無什麼新的東西,但願經過這篇文章能讓你們把以前文章裏講到的每一個點串成一條線,這樣對Laravel總體是怎麼工做的會有更清晰的概念。
本文已經收錄在系列文章Laravel源碼學習裏。
也歡迎關注個人公衆號 網管叨bi叨 ,最近正在籌備準備分享一些平常工做裏學到和總結的技術知識,也會分享一些見聞和學習英語的方法。