Laravel中HTTP内核的详细解析

2020-08-13 20:10:42

本篇文章给大家带来的内容是关于Laravel中HTTP内核的详细解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

Http Kernel

Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而另外的类似通过artisan命令、计划任务、队列启动框架进行处理的都会用到Console Kernel, 今天我们先梳理一下Http Kernel做的事情。

内核绑定

既然Http Kernel是Laravel中用来串联框架的各个部分处理网络请求的,我们来看一下内核是怎么加载到Laravel中应用实例中来的,在public/index.php中我们就会看见首先就会通过bootstrap/app.php这个脚手架文件来初始化应用程序:

下面是 bootstrap/app.php 的代码,包含两个主要部分创建应用实例和绑定内核至 APP 服务容器

<?php// 第一部分: 创建应用实例$app = new IlluminateFoundationApplication(    realpath(__DIR__.'/../'));// 第二部分: 完成内核绑定$app->singleton(    IlluminateContractsHttpKernel::class,    AppHttpKernel::class);$app->singleton(    IlluminateContractsConsoleKernel::class,    AppConsoleKernel::class);$app->singleton(    IlluminateContractsDebugExceptionHandler::class,    AppExceptionsHandler::class);return $app;

HTTP 内核继承自 IlluminateFoundationHttpKernel类,在 HTTP 内核中 内它定义了中间件相关数组, 中间件提供了一种方便的机制来过滤进入应用的 HTTP 请求和加工流出应用的HTTP响应。

<?phpnamespace AppHttp;use IlluminateFoundationHttpKernel 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 = [        IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class,        IlluminateFoundationHttpMiddlewareValidatePostSize::class,        AppHttpMiddlewareTrimStrings::class,        IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class,        AppHttpMiddlewareTrustProxies::class,    ];    /**     * The application's route middleware groups.     *     * @var array     */    protected $middlewareGroups = [        'web' => [            AppHttpMiddlewareEncryptCookies::class,            IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class,            IlluminateSessionMiddlewareStartSession::class,            // IlluminateSessionMiddlewareAuthenticateSession::class,            IlluminateViewMiddlewareShareErrorsFromSession::class,            AppHttpMiddlewareVerifyCsrfToken::class,            IlluminateRoutingMiddlewareSubstituteBindings::class,        ],        'api' => [            'throttle:60,1',            'bindings',        ],    ];    /**     * The application's route middleware.     *     * These middleware may be assigned to groups or used inpidually.     *     * @var array     */    protected $routeMiddleware = [        'auth' => IlluminateAuthMiddlewareAuthenticate::class,        'auth.basic' => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class,        'bindings' => IlluminateRoutingMiddlewareSubstituteBindings::class,        'can' => IlluminateAuthMiddlewareAuthorize::class,        'guest' => AppHttpMiddlewareRedirectIfAuthenticated::class,        'throttle' => IlluminateRoutingMiddlewareThrottleRequests::class,    ];}

在其父类 「IlluminateFoundationHttpKernel」 内部定义了属性名为 「bootstrappers」 的 引导程序 数组:

protected $bootstrappers = [    IlluminateFoundationBootstrapLoadEnvironmentVariables::class,    IlluminateFoundationBootstrapLoadConfiguration::class,    IlluminateFoundationBootstrapHandleExceptions::class,    IlluminateFoundationBootstrapRegisterFacades::class,    IlluminateFoundationBootstrapRegisterProviders::class,    IlluminateFoundationBootstrapBootProviders::class,];

引导程序组中 包括完成环境检测、配置加载、异常处理、Facades 注册、服务提供者注册、启动服务这六个引导程序。

应用解析内核

在将应用初始化阶段将Http内核绑定至应用的服务容器后,紧接着在public/index.php中我们可以看到使用了服务容器的make方法将Http内核实例解析了出来:

$kernel = $app->make(IlluminateContractsHttpKernel::class);

在实例化内核时,将在 HTTP 内核中定义的中间件注册到了 路由器,注册完后就可以在实际处理 HTTP 请求前调用路由上应用的中间件实现过滤请求的目的:

namespace IlluminateFoundationHttp;...class Kernel implements KernelContract{    /**     * Create a new HTTP kernel instance.     *     * @param  IlluminateContractsFoundationApplication  $app     * @param  IlluminateRoutingRouter  $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内核实例来处理HTTP请求了

//public/index.php$response = $kernel->handle(    $request = IlluminateHttpRequest::capture());

在处理请求之前会先通过IlluminateHttpRequest的 capture() 方法以进入应用的HTTP请求的信息为基础创建出一个 Laravel Request请求实例,在后续应用剩余的生命周期中Request请求实例就是对本次HTTP请求的抽象

将HTTP请求抽象成Laravel Request请求实例后,请求实例会被传导进入到HTTP内核的handle方法内部,请求的处理就是由handle方法来完成的。

namespace IlluminateFoundationHttp;class Kernel implements KernelContract{    /**     * Handle an incoming HTTP request.     *     * @param  IlluminateHttpRequest  $request     * @return IlluminateHttpResponse     */    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 EventsRequestHandled($request, $response)        );        return $response;    }}

handle 方法接收一个请求对象,并最终生成一个响应对象。其实handle方法我们已经很熟悉了在讲解很多模块的时候都是以它为出发点逐步深入到模块的内部去讲解模块内的逻辑的,其中sendRequestThroughRouter方法在服务提供者和中间件都提到过,它会加载在内核中定义的引导程序来引导启动应用然后会将使用Pipeline对象传输HTTP请求对象流经框架中定义的HTTP中间件们和路由中间件们来完成过滤请求最终将请求传递给处理程序(控制器方法或者路由中的闭包)由处理程序返回相应的响应。

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    注册异常处理的Handler5. RegisterFacades    注册Facades 6. RegisterProviders  注册Providers 7. BootProviders      启动Providers*/public function bootstrap(){    if (! $this->app->hasBeenBootstrapped()) {    /**依次执行$bootstrappers中每一个bootstrapper的bootstrap()函数        $bootstrappers = [             'IlluminateFoundationBootstrapDetectEnvironment',             'IlluminateFoundationBootstrapLoadConfiguration',             'IlluminateFoundationBootstrapConfigureLogging',             'IlluminateFoundationBootstrapHandleExceptions',             'IlluminateFoundationBootstrapRegisterFacades',             'IlluminateFoundationBootstrapRegisterProviders',             'IlluminateFoundationBootstrapBootProviders',            ];*/            $this->app->bootstrapWith($this->bootstrappers());    }}

发送响应

经过上面的几个阶段后我们最终拿到了要返回的响应,接下来就是发送响应了。

//public/index.php$response = $kernel->handle(    $request = IlluminateHttpRequest::capture());// 发送响应$response->send();

发送响应由 IlluminateHttpResponsesend()方法完成父类其定义在父类SymfonyComponentHttpFoundationResponse中。

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);
//IlluminateFoundationHttpKernelpublic 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对象通过中间件到达处理程序生成响应以及响应发送给客户端。