HttpKernelHttpKernel用于连接Laravel中框架的核心组件对网络请求。简单来说,只要通过public/index.php启动框架,就会使用HttpKernel。而其他像artisan命令、计划任务和队列启动框架等将使用ConsoleKernel。今天我们先来梳理一下HttpKernel是干什么的。KernelBinding既然Laravel中使用了HttpKernel来连接框架的各个部分来处理网络请求,那我们就来看看Laravel中是如何将内核加载到应用程序实例中的。在public/index.php中,我们首先会看到应用会通过脚手架文件bootstrap/app.php进行初始化:下面是bootstrap/app.php的代码,主要包括两个部分:创建应用实例和绑定内核到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);返回$应用程序;HTTP内核继承自IlluminateFoundationHttpKernel类,该类在HTTP内核中定义了与中间件相关的数组,中间件提供了一种方便的机制来过滤传入的HTTP请求和处理传出的HTTP响应。[\App\Http\Middleware\EncryptCookies::class,\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,\Illuminate\Session\Middleware\StartSession::类,//\Illuminate\Session\Middleware\AuthenticateSession::类,\Illuminate\View\Middleware\ShareErrorsFromSession::类,\App\Http\Middleware\VerifyCsrfToken::类,\Illuminate\Routing\Middleware\SubstituteBindings::class,],'api'=>['throttle:60,1','bindings',],];/***应用程序的路由中间件。**这些中间件可以分配给组或单独使用。**@var数组*/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”的引导程序数组:protected$bootstrappers=[\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::类,\Illuminate\Foundation\Bootstrap\LoadConfiguration::类,\Illuminate\Foundation\Bootstrap\HandleExceptions::类,\Illuminate\Foundation\Bootstrap\RegisterFacades::类,\Illuminate\Foundation\Bootstrap\RegisterProviders::类,\Illuminate\Foundation\Bootstrap\BootProviders::类,];bootstrap组包括六个bootstrap程序:环境检测、配置加载、异常处理、Facades注册、服务提供者注册、服务启动关于中间件和bootstrap相关内容的解释可以浏览我们之前相关章节的内容。应用解析内核在应用初始化阶段将Http内核绑定到应用的服务容器后,我们可以看到使用public/index.php中服务容器的make方法解析出了Http内核实例:$kernel=$app->make(Illuminate\Contracts\Http\Kernel::class);在实例化内核时,HTTP内核中定义的中间件被注册到路由器中。注册后,在真正处理HTTP请求之前可以调用路由上应用的中间件实现过滤请求的目的:namespaceIlluminate\Foundation\Http;...classKernelimplementsKernelContract{/***创建一个新的HTTP内核实例。**@param\Illuminate\Contracts\Foundation\Application$app*@param\Illuminate\Routing\Router$router*@returnvoid*/publicfunction__construct(Application$app,Router$router){$this->app=复制代码$应用程序;$this->router=$router;$router->middlewarePriority=$this->middlewarePriority;foreach($this->middlewareGroupsas$key=>$middleware){$router->middlewareGroup($key,$middleware);}foreach($this->routeMiddlewareas$key=>$middleware){$router->aliasMiddleware($key,$middleware);}}}namespaceIlluminate/Routing;classRouterimplementsRegistrarContract,BindingRegistrar{/***注册一组中间件。**@paramstring$name*@paramarray$middleware*@return$this*/publicfunctionmiddlewareGroup($name,array$middleware){$this->middlewareGroups[$name]=$middleware;返回$这个;}/***为中间件注册一个简称。**@paramstring$name*@paramstring$class*@return$this*/publicfunctionaliasMiddleware($name,$class){$this->middleware[$name]=$class;返回$这个;}}HandleHTTP通过服务解析请求完成Http内核实例的创建,可以使用HTTP内核实例处理HTTP请求//public/index.php$response=$kernel->handle($request=Illuminate\Http\Request::capture());在处理请求之前,会根据进入应用程序的HTTP请求的信息,通过Illuminate\Http\Request的capture()方法创建一个LaravelRequest请求实例。在后续应用的剩余生命周期中,Request请求求一个实例就是这个HTTP请求的抽象。LaravelRequest请求实例的解释请参考前面的章节。处理是通过handle方法完成的。namespaceIlluminate\Foundation\Http;classKernelimplementsKernelContract{/***处理传入的HTTP请求。**@param\Illuminate\Http\Request$request*@return\Illuminate\Http\Response*/publicfunctionhandle($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=newFatalThrowableError($e));$response=$this->renderException($request,$e);}$this->app['events']->dispatch(newEvents\RequestHandled($request,$response));返回$响应;}}handle方法接收一个请求对象,并最终生成一个响应对象。其实handle方法我们已经很熟悉了。在讲解很多模块的时候,我们都是以此为起点,逐步深入模块,讲解模块中的逻辑。服务提供者和中间件中都提到了sendRequestThroughRouter方法。将加载内核中定义的bootstrap程序启动应用程序,然后通过框架中定义的HTTP中间件和路由中间件使用Pipeline对象传输HTTP请求对象,完成过滤请求,最终将请求传递给处理程序(控制器方法或路由中的闭包)由具有相应响应的处理程序返回。关于handle方法的注释,我直接引用前面几章的解释放在这里。关于如何引导和启动应用程序以及如何通过各个中间件传递并到达处理程序的更详细的分析,请参阅服务提供者和中间件以及关于路由的三章。受保护的函数sendRequestThroughRouter($request){$this->app->instance('request',$request);Facade::clearResolvedInstance('request');$this->bootstrap();返回(新管道($this->app))->send($request)->through($this->app->shouldSkipMiddleware()?[]:$this->middleware)->then($this->dispatchToRouter());}/*启动Laravel应用1.DetectEnvironment检查环境2.LoadConfiguration加载应用配置3.ConfigureLogging配置日志到4.HandleException注册异常处理Handler5.RegisterFacades注册Facades6.RegisterProviders注册Providers7.BootProviders启动Providers*/publicfunctionbootstrap(){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中。publicfunctionsend(){$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对象的详细分析,可以参考我们之前对LaravelResponse对象的讲解章节。终止应用程序响应发出后,HTTP内核会调用terminable中间件做一些后续处理。例如,Laravel内置的“会话”中间件在响应发送到浏览器后将会话数据写入存储。//public/index.php//终止程序$kernel->terminate($request,$response);//Illuminate\Foundation\Http\Kernelpublicfunctionterminate($request,$response){$this->terminateMiddleware($请求,$响应);$this->app->terminate();}//终止中间件保护函数terminateMiddleware($request,$response){$middlewares=$this->app->shouldSkipMiddleware()?[]:array_merge($this->gatherRouteMiddleware($request),$this->middleware);foreach($middlewaresas$middleware){if(!is_string($middleware)){继续;}list($name,$parameters)=$this->parseMiddleware($middleware);$instance=$this->app->make($name);如果(method_exists($instance,'terminate')){$instance->terminate($request,$response);}}}Http核心的terminate方法会调用可终止中间件的terminate方法。调用完成后,整个应用从HTTP请求到响应返回的生命周期就结束了。小结本节介绍的HTTP内核主要起到串联作用,设计初始化应用程序,启动应用程序,将HTTP请求抽象为Request对象,通过中间件将Request对象传递给handler产生响应,发送响应给客户。这些东西前面几章都讲过,没有什么新意。我希望通过这篇文章,你可以将上一篇文章中提到的每个点连成一条线,从而影响Laravel整体的工作方式。会有更清晰的概念。本文已收录在Laravel源码学习系列文章中。也欢迎大家关注我的公众号网管闲聊。最近准备分享一些在日常工作中学习和总结的技术知识,也会分享一些学习英语的知识和方法。
