0x00 前言

闲来无事看看 Laravel 源码,初步了解一下其生命周期方便理解和使用它。

0x01 从入口文件开始

因为 Laravel 是单一入口的框架,所以我们第一步从 public/index.php 这个文件读起。

以下代码均基于 Laravel 5.7,原注释被本人替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
// 定义常量,记录框架开始运行的时间
define('LARAVEL_START', microtime(true));

// 引入 composer 的 autoload.php
require __DIR__.'/../vendor/autoload.php';

// 引入 bootstrap/app.php 获得 Application 的实例
$app = require_once __DIR__.'/../bootstrap/app.php';

// 获得 app 容器中 Illuminate\Contracts\Http\Kernel 接口绑定的类的实例
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// 通过 kernel 处理请求并返回响应
$response = $kernel->handle(
// 获取请求
$request = Illuminate\Http\Request::capture()
);

// 发送响应
$response->send();

// 调用 terminate 类型的中间件
$kernel->terminate($request, $response);

上面的代码简明清晰的表达了 Laravel 的处理流程,但是我们还是想要了解其中细节,比如 $app$kernel到底是什么?$kernel->handle 发生了什么?所以我们接着看吧!

0x02 Application 实例,从 bootstrap/app.php 开始

上面的 index.php 代码里通过 require 引入了 bootstrap/app.php 文件获得了 Application 实例,下面我们看看这个文件内容。

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
<?php

// 实例化 Application 类
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

// 容器通过単例的方式
// 绑定 Illuminate\Contracts\Http\Kernel 接口
// 给 App\Http\Kernel 类
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);

// 容器通过単例的方式
// 绑定 Illuminate\Contracts\Console\Kernel 接口
// 给 App\Console\Kernel::class 类
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);

// 容器通过単例的方式
// 绑定 Illuminate\Contracts\Debug\ExceptionHandler 接口
// 给 App\Exceptions\Handler 类
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);

// 返回 app
return $app;

如果你熟悉容器这个概念那么阅读上面的代码应该是没有障碍的,因为 Application 类继承了 Container,所以 $app 也是一个容器而已,然后 $app->singleton(...) 只是绑定了一些接口对应的类。因为将 Illuminate\Contracts\Http\Kerne 接口绑定到了 App\Http\Kernel,所以在 index.php 的这行代码:

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

$kernel 就是 App\Http\Kernel 的实例。

现在我们看看 Illuminate\Foundation\Application 是什么,由于该类有 1000 多行所以代码就只贴声明和构造函数了喔~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use Illuminate\Container\Container;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class Application extends Container implements ApplicationContract, HttpKernelInterface
{
public function __construct($basePath = null)
{
// 设置根路径
if ($basePath) {
$this->setBasePath($basePath);
}
// 注册基本绑定 app、Container::class、PackageManifest::class
$this->registerBaseBindings();
// 注册基本服务提供者 EventServiceProvider、LogServiceProvider、RoutingServiceProvider
$this->registerBaseServiceProviders();
// 注册类别名
$this->registerCoreContainerAliases();
}
// ... 其它代码省略
}

首先看声明 Application 继承了 Container 类,说明它也是一个容器。然后它还实现了 ApplicationContract 这个自定义的接口和 HttpKernelInterface 这个 Symfony 提供的接口。而在构造函数中它向自己这个容器注册了一些基本的绑定(binding)、服务提供者(service provider)和别名(alias)。

0x04 App\Http\Kernel 是如何 handle 请求的?

上面我们知道 $kernel 其实就是 App\Http\Kernel 的实例,下面我们来看看这个类,打开 app/Http/Kernel.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
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
<?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\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' => \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,
];
}

看完如上代码发现其实真正的重点在父类 Illuminate\Foundation\Http\Kernel,因为 App\Http\Kernel 类只是覆盖了父类的中间件相关的属性而已。那我们来看看 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
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
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;
// 下面的代码都只是将 kernel 的中间件添加到 $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);
}
}

/**
* Handle an incoming HTTP request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function handle($request)
{
try {
// [不重要]开启请求重载,就是将 _method 参数值覆盖原始请求的方法
$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;
}
/**
* Send the given request through the middleware / router.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
protected function sendRequestThroughRouter($request)
{
// 容器绑定 request 实例
$this->app->instance('request', $request);
// 清除旧的 request 实例
Facade::clearResolvedInstance('request');
// 执行引导程序
$this->bootstrap();
// 通过 Pipeline 将所有中间件打包后执行,返回响应
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
// ... 省略其它代码
}

看完上面的代码也许你最为疑惑的可能是 Pipeline 的作用,但由于篇幅限制本文不会介绍其作用。但是在这里推荐一篇很好的文章 Laravel Pipeline 组件的实现原理,想要了解可以看看。

0x05 生命周期图

laravel 生命周期图

原谅我的懒惰,直接从网上找了一张。图片来源:Laravel 的生命周期

0x06 参考链接

深度挖掘 Laravel 生命周期

Laravel 的生命周期

Laravel Pipeline 组件的实现原理