Laravel的生命周期

目录

  • 简介

  • 一、Composer 自动加载项目依赖

  • 二、创建应用实例

    • 创建容器

    • 绑定内核

      HTTP 内核类
      Console 内核
      绑定异常处理

  • 三、接收请求并响应

    • 解析内核
    • 处理 HTTP 请求
    • 发送响应
  • 四、终止应用程序

  • 五、总结

  • References

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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(Illuminate\Contracts\Http\Kernel::class);

// 获取一个 Request ,返回一个 Response。以把该内核想象作一个代表整个应用的大黑盒子,输入 HTTP 请求,返回 HTTP 响应
// 处理请求
$response = $kernel->handle(
// 创建请求实例
$request = Illuminate\Http\Request::capture()
);

// 发送响应,就是把我们服务器的结果返回给浏览器。
$response->send();

// 终止程序,这个就是执行我们比较耗时的请求,
$kernel->terminate($request, $response);

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

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

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

一、Composer 自动加载项目依赖

1
2
// composer自动加载机制
require __DIR__.'/../vendor/autoload.php';

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

autoload.php 文件代码:

1
2
3
4
5
// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInit101671ca9bbc2f62f8335eb842637291::getLoader();

二、创建应用实例

1
$app = require_once __DIR__.'/../bootstrap/app.php';

bootstrap/app.php 文件代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 创建Laravel应用的ioc容器,
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__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;

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

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

  • 1、创建容器

  • 2、绑定内核

创建容器

1
2
3
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

我们进入 Illuminate\Foundation\Application 容器中,以下是构造函数的分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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

绑定内核

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 完成内核的绑定
// HTTP内核
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
// Console内核
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
// 绑定异常处理
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);

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

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

HTTP 内核类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?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 = [
\App\Http\Middleware\TrustProxies::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::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' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

/**
* The priority-sorted list of middleware.
*
* This forces non-global middleware to always be in the given order.
*
* @var array
*/
protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\Authenticate::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* The bootstrap classes for the application.
*
* @var array
*/
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,
];

Console 内核:

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

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

绑定异常处理

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

三、接收请求并响应

解析内核

1
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

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

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

Illuminate\Foundation\Http\Kernel 的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Illuminate\Routing\Router 类中

/**
* 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 请求

1
2
3
4
5
// handle 方法处理请求
$response = $kernel->handle(
// 创建请求实例
$request = Illuminate\Http\Request::capture()
);

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

1
2
3
4
5
6
7
8
9
10
11
/**
* Create a new Illuminate HTTP request from server variables.
*
* @return static
*/
public static function capture()
{
static::enableHttpMethodParameterOverride();

return static::createFromBase(SymfonyRequest::createFromGlobals());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Creates a new request with values from PHP's super globals.
*
* @return static
*/
public static function createFromGlobals()
{
$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 = new ParameterBag($data);
}

return $request;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 方法接收了一个请求,并在最后返回了一个 HTTP响应。

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

该方法的程序执行如下:

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Send the given request through the middleware / router.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
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());
}

bootstrap 引导程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Bootstrap the application for HTTP requests.
*
* @return void
*/
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
/**
* Get the bootstrap classes for the application.
*
* @return array
*/
protected function bootstrappers()
{
return $this->bootstrappers;
}

/**
* The bootstrap classes for the application.
*
* @var array
*/
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,
];
/*引导启动Laravel应用程序
1. DetectEnvironment 检查环境
2. LoadConfiguration 加载应用配置
3. ConfigureLogging 配置日至
4. HandleException 注册异常处理的Handler
5. RegisterFacades 注册Facades
6. RegisterProviders 注册Providers
7. BootProviders 启动Providers
*/

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

发送响应

1
$response->send();

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

程序由在 Illuminate\Http\Response 内部由其父类 Symfony\Component\HttpFoundation\Response 的 send() 方法实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Sends HTTP headers and content.
*
* @return $this
*/
public function send()
{
$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 方法,调用完成后,整个生命周期结束。

1
$kernel->terminate($request, $response);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Call the terminate method on any terminable middleware.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Response $response
* @return void
*/
public function terminate($request, $response)
{
$this->terminateMiddleware($request, $response);

$this->app->terminate();
}
/**
* Call the terminate method on any terminable middleware.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Response $response
* @return void
*/
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;
}

[$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 生命周期: https://learnku.com/articles/10421/depth-mining-of-laravel-life-cycle

[2] Laravel 的生命周期: https://learnku.com/articles/10421/depth-mining-of-laravel-life-cycle

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

作者

Fahsa

发布于

2021-02-25

更新于

2021-02-25

许可协议

评论