Laravel的生命周期

blank

Laravel的生命周期

目录

  • 简介
  • 一、Composer 自动加载项目依赖
  • 二、创建应用实例
  • 创建容器
  • 绑定内核
    HTTP 内核类 Console 内核 绑定异常处理
  • 三、接收请求并响应
  • 解析内核
  • 处理 HTTP 请求
  • 发送响应
  • 四、终止应用程序
  • 五、总结
  • References

Laravel的生命周期开始于 public/index.php,结束于 public/index.php。

客户端的所有请求都经由Web服务器引导到这个文件中。

以下是public/index.php 文件的源码和注释:

#public/index.php 文件
// 定义了laravel一个请求的开始时间
define('LARAVEL_START',microtime(true));// composer自动加载机制,加载项目依赖
require__DIR__.'/../vendor/autoload.php';// 这句话你就可以理解laravel,在最开始引入了一个ioc容器。
$app=require_once__DIR__.'/../bootstrap/app.php';// 这个相当于我们创建了Kernel::class的服务提供者
$kernel=$app->make(IlluminateContractsHttpKernel::class);// 获取一个 Request ,返回一个 Response。以把该内核想象作一个代表整个应用的大黑盒子,输入 HTTP 请求,返回 HTTP 响应
// 处理请求
$response=$kernel->handle(// 创建请求实例
$request=IlluminateHttpRequest::capture());// 发送响应,就是把我们服务器的结果返回给浏览器。
$response->send();// 终止程序,这个就是执行我们比较耗时的请求,
$kernel->terminate($request,$response);

由上可知 Laravel 的生命周期分为以下3个主要阶段:

  • 加载项目依赖。
  • 创建Laravel应用实例
  • 接收请求并响应。

接下来我们根据 public/index.php 文件,开始 Laravel 的生命周期之旅吧!

一、Composer 自动加载项目依赖

// composer自动加载机制
require__DIR__.'/../vendor/autoload.php';

Composer 是 PHP 的包管理器,用来管理项目依赖的工具,autoload.php 中引入了 Composer 自动生成的加载程序,实现加载第三方依赖。

autoload.php 文件代码:

// autoload.php @generated by Composer
require_once__DIR__.'/composer/autoload_real.php';returnComposerAutoloaderInit101671ca9bbc2f62f8335eb842637291::getLoader();

二、创建应用实例

$app=require_once__DIR__.'/../bootstrap/app.php';

bootstrap/app.php 文件代码:

// 创建Laravel应用的ioc容器,
$app=newIlluminateFoundationApplication($_ENV['APP_BASE_PATH']??dirname(__DIR__));// 完成内核的绑定
$app->singleton(IlluminateContractsHttpKernel::class,AppHttpKernel::class);$app->singleton(IlluminateContractsConsoleKernel::class,AppConsoleKernel::class);$app->singleton(IlluminateContractsDebugExceptionHandler::class,AppExceptionsHandler::class);// 返回实例
return$app;

如果你理解了 Ioc(控制反转),那么对于以上代码你一定很熟悉。

Ioc 容器(服务容器) 是 Laravel 的核心,这一阶段主要实现了 2大功能:

  • 1、创建容器
  • 2、绑定内核

创建容器

$app=newIlluminateFoundationApplication($_ENV['APP_BASE_PATH']??dirname(__DIR__));

我们进入 IlluminateFoundationApplication 容器中,以下是构造函数的分析:

/**
     * Create a new Illuminate application instance.
     *
     * @param  string|null  $basePath
     * @return void
     */publicfunction__construct($basePath=null){if($basePath){// 1、路径绑定
$this->setBasePath($basePath);}// 2、基础绑定
$this->registerBaseBindings();// 3、基础服务提供者绑定(事件,日志,路由)
$this->registerBaseServiceProviders();// 4、核心别名绑定,目的是给核心的类命名空间设置别名,以便后续使用
$this->registerCoreContainerAliases();}

绑定内核

// 完成内核的绑定
// HTTP内核
$app->singleton(IlluminateContractsHttpKernel::class,AppHttpKernel::class);// Console内核
$app->singleton(IlluminateContractsConsoleKernel::class,AppConsoleKernel::class);// 绑定异常处理
$app->singleton(IlluminateContractsDebugExceptionHandler::class,AppExceptionsHandler::class);

在 Laravel 中只要是通过 public/index.php 来启动框架的,都会用到 Http Kernel(主要作用就是接受请求并返回响应),而其他的例如通过 artisan 命令、计划任务、队列等启动框架进行处理的都会用到 Console 内核。

其中 HTTP 内核类继承 IlluminateFoundationHttpKernel ,HTTP内核类中定义了中间件相关数组,中间件主要是提供了一种方便机制用来过滤进入应用的请求和处理HTTP响应。

HTTP 内核类:

<?phpnamespaceAppHttp;useIlluminateFoundationHttpKernelasHttpKernel;classKernelextendsHttpKernel{/**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */protected$middleware=[AppHttpMiddlewareTrustProxies::class,AppHttpMiddlewareCheckForMaintenanceMode::class,IlluminateFoundationHttpMiddlewareValidatePostSize::class,AppHttpMiddlewareTrimStrings::class,IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::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 individually.
     *
     * @var array
     */protected$routeMiddleware=['auth'=>AppHttpMiddlewareAuthenticate::class,'auth.basic'=>IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class,'bindings'=>IlluminateRoutingMiddlewareSubstituteBindings::class,'cache.headers'=>IlluminateHttpMiddlewareSetCacheHeaders::class,'can'=>IlluminateAuthMiddlewareAuthorize::class,'guest'=>AppHttpMiddlewareRedirectIfAuthenticated::class,'signed'=>IlluminateRoutingMiddlewareValidateSignature::class,'throttle'=>IlluminateRoutingMiddlewareThrottleRequests::class,'verified'=>IlluminateAuthMiddlewareEnsureEmailIsVerified::class,];/**
     * The priority-sorted list of middleware.
     *
     * This forces non-global middleware to always be in the given order.
     *
     * @var array
     */protected$middlewarePriority=[IlluminateSessionMiddlewareStartSession::class,IlluminateViewMiddlewareShareErrorsFromSession::class,AppHttpMiddlewareAuthenticate::class,IlluminateSessionMiddlewareAuthenticateSession::class,IlluminateRoutingMiddlewareSubstituteBindings::class,IlluminateAuthMiddlewareAuthorize::class,];}

HTTP内核类的父类:IlluminateFoundationHttpKernel 提供了名为 $bootstrappers 的引导程序数组,包括了环境检测、加载配置、处理异常、Facades注册、服务提供者注册、启动服务这6个 程序。

/**
     * The bootstrap classes for the application.
     *
     * @var array
     */protected$bootstrappers=[IlluminateFoundationBootstrapLoadEnvironmentVariables::class,IlluminateFoundationBootstrapLoadConfiguration::class,IlluminateFoundationBootstrapHandleExceptions::class,IlluminateFoundationBootstrapRegisterFacades::class,IlluminateFoundationBootstrapRegisterProviders::class,IlluminateFoundationBootstrapBootProviders::class,];

Console 内核:

一个健壮的应用程序除了满足网络请求外,还应该包括执行计划任务、异步队列等,Laravel中通过artisan工具定义各种命令来完成非 HTTP 请求的各种场景。artisan命令通过 Laravel 的 Console 内核完成对应用核心组件的调度来完成任务。

Console 内核在这不做详细介绍。

绑定异常处理

异常处理由 AppExceptionsHandler 类完成,所有的异常处理都由其处理,在这里同样不做详细介绍。

三、接收请求并响应

解析内核

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

这里将 HTTP 内核解析出来,我们进一步查看IlluminateContractsHttpKernel::class 的内部代码,

构造函数中注入了app容器和路由器2个函数,在实例化内核的过程中,将内核中定义的中间件注册到路由器中,注册完成后便可以处理HTTP请求前调用中间件 实现过滤请求的目的。

IlluminateFoundationHttpKernel 的构造函数:

/**
     * Create a new HTTP kernel instance.
     *
     * @param  IlluminateContractsFoundationApplication  $app
     * @param  IlluminateRoutingRouter  $router
     * @return void
     */publicfunction__construct(Application$app,Router$router){$this->app=$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);}}// IlluminateRoutingRouter 类中
/**
     * Register a group of middleware.
     *
     * @param  string  $name
     * @param  array  $middleware
     * @return $this
     */publicfunctionmiddlewareGroup($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
     */publicfunctionaliasMiddleware($name,$class){$this->middleware[$name]=$class;return$this;}

处理 HTTP 请求

// handle 方法处理请求
$response=$kernel->handle(// 创建请求实例
$request=IlluminateHttpRequest::capture());

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

/**
     * Create a new Illuminate HTTP request from server variables.
     *
     * @return static
     */publicstaticfunctioncapture(){static::enableHttpMethodParameterOverride();returnstatic::createFromBase(SymfonyRequest::createFromGlobals());}/**
     * Creates a new request with values from PHP's super globals.
     *
     * @return static
     */publicstaticfunctioncreateFromGlobals(){$request=self::createRequestFromFactory($_GET,$_POST,[],$_COOKIE,$_FILES,$_SERVER);if(0===strpos($request->headers->get('CONTENT_TYPE'),'application/x-www-form-urlencoded')&&in_array(strtoupper($request->server->get('REQUEST_METHOD','GET')),['PUT','DELETE','PATCH'])){parse_str($request->getContent(),$data);$request->request=newParameterBag($data);}return$request;}

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

namespaceIlluminateFoundationHttp;classKernelimplementsKernelContract{/**
     * Handle an incoming HTTP request.
     *
     * @param  IlluminateHttpRequest  $request
     * @return IlluminateHttpResponse
     */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(newEventsRequestHandled($request,$response));return$response;}}

handle 方法接收了一个请求,并在最后返回了一个 HTTP响应。

接下来进入 sendRequestThroughRouter 方法,通过中间件/路由器发送给定的请求。

该方法的程序执行如下:

1、将 request 请求实例注册到 app 容器当中

2、清除之前的 request 实例缓存

3、启动引导程序:加载内核中定义的引导程序来引导启动应用

4、将请求发送到路由:通过 Pipeline 对象传输 HTTP请求对象流经框架中定义的HTTP中间件和路由器来完成过滤请求,最终将请求传递给处理程序(控制器方法或者路由中的闭包)由处理程序返回相应的响应。

/**
     * Send the given request through the middleware / router.
     *
     * @param  IlluminateHttpRequest  $request
     * @return IlluminateHttpResponse
     */protectedfunctionsendRequestThroughRouter($request){$this->app->instance('request',$request);Facade::clearResolvedInstance('request');$this->bootstrap();return(newPipeline($this->app))->send($request)->through($this->app->shouldSkipMiddleware()?[]:$this->middleware)->then($this->dispatchToRouter());}

bootstrap 引导程序

/**
     * Bootstrap the application for HTTP requests.
     *
     * @return void
     */publicfunctionbootstrap(){if(!$this->app->hasBeenBootstrapped()){$this->app->bootstrapWith($this->bootstrappers());}}/**
     * Get the bootstrap classes for the application.
     *
     * @return array
     */protectedfunctionbootstrappers(){return$this->bootstrappers;}/**
     * The bootstrap classes for the application.
     *
     * @var array
     */protected$bootstrappers=[IlluminateFoundationBootstrapLoadEnvironmentVariables::class,IlluminateFoundationBootstrapLoadConfiguration::class,IlluminateFoundationBootstrapHandleExceptions::class,IlluminateFoundationBootstrapRegisterFacades::class,IlluminateFoundationBootstrapRegisterProviders::class,IlluminateFoundationBootstrapBootProviders::class,];/*引导启动Laravel应用程序
1. DetectEnvironment  检查环境
2. LoadConfiguration  加载应用配置
3. ConfigureLogging   配置日至
4. HandleException    注册异常处理的Handler
5. RegisterFacades    注册Facades 
6. RegisterProviders  注册Providers 
7. BootProviders      启动Providers
*/

关于引导程序的启动原理,有时间我们再抽出来看看。

发送响应

$response->send();

经过了以上阶段,终于获取到了我们想要的数据,接下来是将数据响应到客户端。

程序由在 IlluminateHttpResponse 内部由其父类 SymfonyComponentHttpFoundationResponse 的 send() 方法实现。

/**
     * Sends HTTP headers and content.
     *
     * @return $this
     */publicfunctionsend(){$this->sendHeaders();// 发送头部訊息
$this->sendContent();// 发送报文主题
if(function_exists('fastcgi_finish_request')){fastcgi_finish_request();}elseif(!in_array(PHP_SAPI,['cli','phpdbg'],true)){static::closeOutputBuffers(0,true);}return$this;}

四、终止应用程序

该过程调用终止中间件。

响应完成后,HTTP 内核会调用 terminate 中间件做一些后续的处理工作。比如,Laravel 内置的 session 中间件会在响应发送到浏览器之后将会话数据写入存储器中。

HTTP 内核的 terminate 方法会调用 terminate 中间件的 terminate 方法,调用完成后,整个生命周期结束。

$kernel->terminate($request,$response);/**
     * Call the terminate method on any terminable middleware.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  IlluminateHttpResponse  $response
     * @return void
     */publicfunctionterminate($request,$response){$this->terminateMiddleware($request,$response);$this->app->terminate();}/**
     * Call the terminate method on any terminable middleware.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  IlluminateHttpResponse  $response
     * @return void
     */protectedfunctionterminateMiddleware($request,$response){$middlewares=$this->app->shouldSkipMiddleware()?[]:array_merge($this->gatherRouteMiddleware($request),$this->middleware);foreach($middlewaresas$middleware){if(!is_string($middleware)){continue;}[$name]=$this->parseMiddleware($middleware);$instance=$this->app->make($name);if(method_exists($instance,'terminate')){$instance->terminate($request,$response);}}}

五、总结

在Laravel 的整个生命周期中,加载项目依赖、创建应用实例、接收并响应请求,终止程序,内核都起到了串联作用。

首先,创建 Laravel 应用程序 阶段时,包含了注册项目基础服务、注册项目服务提供者别名、注册目录路径、异常类绑定等工作,同时在HTTP 内核中配置了引导程序。 接着,在 接收请求并响应 阶段时,会根据运行的环境解析出 HTTP 内核或 Console 内核。在 HTTP 内核中把中间件注册到路由器中。 然后,处理请求阶段的过程中,将请求的实例注册到app容器中,通过引导程序启动应用,最后发送请求到路由。 之后,通过Response类响应数据。 最后,通过terminate 方法终止程序。

以上就是关于 Laravel 生命周期的详细解析。

References

[1] 深度挖掘 Laravel 生命周期: learnku.com/articles/10

[2] Laravel 的生命周期: learnku.com/articles/10

ps:喜欢的朋友可以关注微信公众号(苏小怪的梦呓)和我一起成长。

What do you think?

Written by marketer

blank

【PHP】23 个你应该知道的 Laravel 面试问题

blank

关于内容种草营销的18个知识点