由于 PHP 可以处理 WEB 和 CLI 两种接口请求,所以 Laravel中设计 HttpKernel 和 ConsoleKernel 来处理这两种类型的请求,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 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 内核继承自 Illuminate\Foundation\Http\Kernel类,在 HTTP 内核中 内它定义了中间件相关数组, 中间件提供了一种方便的机制来过滤进入应用的 HTTP 请求和加工流出应用的HTTP响应。

<?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,];
}

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

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 注册、服务提供者注册、启动服务这六个引导程序。

有关中间件和引导程序相关内容的讲解可以浏览我们之前相关章节的内容。

应用解析内核

在将应用初始化阶段将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内核实例来处理HTTP请求了

//public/index.php
$response = $kernel->handle($request = Illuminate\Http\Request::capture()
);

在处理请求之前会先通过 Illuminate\Http\Requestcapture() 方法以进入应用的HTTP请求的信息为基础创建出一个 Laravel Request请求实例,在后续应用剩余的生命周期中 Request请求实例就是对本次HTTP请求的抽象,关于Laravel Request请求实例的讲解可以参考以前的章节。

将HTTP请求抽象成 LaravelRequest请求实例后,请求实例会被传导进入到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\Responsesend()方法完成父类其定义在父类 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框架的核心,这篇文章讲讲 LaravelHTTP内核有的人可能会问到底哪个才是 Laravel的核心,实际上服务容器是一切的基础,框架中每时每刻都在用到它提供的依赖注入和控制反转的能力。 Laravel 刚刚启动时先启动容器对象 Application,然后加载配置、通过 ServiceProvider往容器对象里填充一些对象为接下来处理请求做准备,但是真正干活的是 KernelApplication 会把工作转给 Kernel 来干 $output=Kernel::handle($input);,对于 WEB请求,输入是 Request输出是 Response,对于CLI请求,输入是 argument+option 命令行变量构成的 Input 对象,输出则是展示在终端的 Output 对象。

所以,依赖注入(IoC 容器) 是 Laravel 的基石,真正干活的是 Kernel。

Laravel源码解析之HTTP Kernel相关推荐

  1. Laravel源码解析之从入口开始

    前言 提升能力的方法并非使用更多工具,而是解刨自己所使用的工具.今天我们从Laravel启动的第一步开始讲起. 入口文件 laravel是单入口框架,所有请求必将经过index.php define( ...

  2. Laravel源码解析之Console内核

    上一篇文章我们介绍了Laravel的HTTP内核,详细概述了网络请求从进入应用到应用处理完请求返回HTTP响应整个生命周期中HTTP内核是如何调动Laravel各个核心组件来完成任务的.除了处理HTT ...

  3. Laravel源码解析之中间件

    中间件(Middleware)在Laravel中起着过滤进入应用的HTTP请求对象(Request)和完善离开应用的HTTP响应对象(Reponse)的作用, 而且可以通过应用多个中间件来层层过滤请求 ...

  4. Laravel源码解析之Request

    很多框架都会将来自客户端的请求抽象成类方便应用程序使用,在Laravel中也不例外. Illuminate\Http\Request类在Laravel框架中就是对客户端请求的抽象,它是构建在 Symf ...

  5. Laravel源码解析之ENV配置

    Laravel在启动时会加载项目中的 .env文件.对于应用程序运行的环境来说,不同的环境有不同的配置通常是很有用的. 例如,你可能希望在本地使用测试的 Mysql数据库而在上线后希望项目能够自动切换 ...

  6. Laravel源码解析之事件系统

    Laravel 的事件提供了一个简单的观察者实现,能够订阅和监听应用中发生的各种事件.事件机制是一种很好的应用解耦方式,因为一个事件可以拥有多个互不依赖的监听器. laravel 中事件系统由两部分构 ...

  7. Laravel源码解析之Eloquent Model

    上篇文章我们讲了Database的查询构建器Query Builder, 学习了Query Builder为构建生成SQL语句而提供的Fluent Api的代码实现.这篇文章我们来学习Laravel ...

  8. Laravel源码解析之QueryBuilder

    Database 查询构建器 上文我们说到执行 DB::table('users')->get()是由Connection对象执行table方法返回了一个QueryBuilder对象,Query ...

  9. Laravel源码解析之Response

    之前两篇文章分别讲了Laravel的控制器和Request对象,在讲Request对象的那一节我们看了Request对象是如何被创建出来的以及它支持的方法都定义在哪里,讲控制器时我们详细地描述了如何找 ...

最新文章

  1. Python使用matplotlib可视化绘制并导出可视化结果图表到PDF文件中
  2. 笔记“SQL与Access”
  3. 强化学习(八)价值函数的近似表示与Deep Q-Learning
  4. 一加7pro运动计步功能_测血压、心率、血氧、运动计步,来电微信消息等提醒,多种模式可选,这款智能手环功能实在是太全了吧!...
  5. 终于完全弄懂了KMP(个人理解篇)
  6. gRPC-go源码(2):ClientConn
  7. TensorFlow.js:零基础在小程序上实现机器学习
  8. java中的递归算法_java递归算法详解
  9. MATLAB风玫瑰图WindRose绘制记录(已知风的u、v分量如何计算风向。)
  10. VUE移动端案例整合
  11. 【在linux系统中使用绘王HC16数位板绘画】
  12. java 众数算法_众数的算法分析
  13. 团队管理,领导的“无为”就是最大“有为”
  14. The puzzle
  15. 小游戏--三子棋——N子棋(实现)
  16. 课程设计:公交线路管理系统
  17. 皕杰报表自定义扩展~自定义数据集
  18. 基于单片机的超市储物柜设计_一种基于at89c51单片机的超市寄存装置的制造方法...
  19. ICOM IC-F26 使用MDC信令“蛙叫”及SQL设置
  20. ROYcms从新调整后的结构

热门文章

  1. C# 调用其他的动态库开发应注意的问题
  2. C标准函数库中获取时间与日期、对时间与日期数据操作及格式化
  3. Spring学习笔记002 - AOP
  4. #if、#ifdef、#if defined之间的区别【转】
  5. Mark To Market - MTM
  6. Yii防注入***笔记
  7. string replaceAll
  8. Java之Set接口
  9. Thread.join的作用和原理
  10. Redis之跳跃表实现