Laravel 启动流程

0. Laravel生命周期

说明Laravel生命周期前,先简单回忆一下PHP的生命周期。

当我们请求一个PHP文件时,PHP为了完成这次请求,会发生5个阶段的生命周期切换。

PHP生命周期

MINIT: 模块初始化,即调用 php.ini 中指明的扩展的初始化函数进行初始化工作,如 mysql 扩展。
RINIT: 请求初始化,即初始化为执行本次脚本所需要的变量名称和变量值内容的符号表,如 $_SESSION变量。
HANDLE: 执行该PHP脚本。
RSHUTDOWN: 请求处理完成(Request Shutdown),按顺序调用各个模块的 RSHUTDOWN 方法,对每个变量调用 unset函数,如 unset $_SESSION 变量。
MSHUTDOWN: 关闭模块(Module Shutdown),PHP调用每个扩展的 MSHUTDOWN 方法,这是各个模块最后一次释放内存的机会, 这意味着没有下一个请求了。

小结: PHP是一种脚本语言,所有的变量只会在这一次请求中生效,下次请求之时已被重置,而不像Java静态变量拥有全局作用。

Laravel 的生命周期从public\index.php开始,从public\index.php结束。具体来讲可以分成4个步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 1. 文件载入composer生成的自动加载设置,包括所有你 composer require的依赖。
**/
require __DIR__.'/../vendor/autoload.php';

/**
* 2. 生成容器Container,Application实例,并向容器注册核心组件(HttpKernel,ConsoleKernel ,ExceptionHandler)
**/
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

/**
* 3.处理请求,生成并发送响应(毫不夸张的说,你99%的代码都运行在这个小小的handle 方法里面)
**/
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();

/**
* 4.请求结束,进行回调(还记得可终止中间件吗?没错,就是在这里回调的)
**/
$kernel->terminate($request, $response);

过程如下:

Laravel生命周期

1. 程序启动准备

程序入口在 index.php 中

1
2
3
4
5
6
7
8
9
10
11
12
13
require __DIR__.'/../vendor/autoload.php';

$app = require_once __DIR__.'/../bootstrap/app.php'; # 获取服务容器实例

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

$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);

创建服务容器实例

服务容器的创建在 bootstrap\app.php 中进行.

1
2
3
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);

1.1 容器基础配置

容器 Application 的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}

$this->registerBaseBindings();

$this->registerBaseServiceProviders();

$this->registerCoreContainerAliases();
}

构造函数 主要完成以下基本配置:

  • 目录路径(绑定到容器中, 并提供类方法获取子目录)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '\/');

$this->bindPathsInContainer();

return $this;
}

protected function bindPathsInContainer()
{
$this->instance('path', $this->path());
$this->instance('path.base', $this->basePath());
$this->instance('path.lang', $this->langPath());
$this->instance('path.config', $this->configPath());
$this->instance('path.public', $this->publicPath());
$this->instance('path.storage', $this->storagePath());
$this->instance('path.database', $this->databasePath());
$this->instance('path.resources', $this->resourcePath());
$this->instance('path.bootstrap', $this->bootstrapPath());
}
  • 绑定容器自身
1
2
3
4
5
6
7
8
9
10
11
12
protected function registerBaseBindings()
{
static::setInstance($this);

$this->instance('app', $this);

$this->instance(Container::class, $this);

$this->instance(PackageManifest::class, new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
}
  • 基础服务注册( Event, Log, Route)
1
2
3
4
5
6
7
8
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));

$this->register(new LogServiceProvider($this));

$this->register(new RoutingServiceProvider($this));
}
  • 别名注册

多个接口名 对应一个简短别名, 后续在注册服务时只需绑定到别名上即可 (而不必绑定到具体接口名)

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
public function registerCoreContainerAliases()
{
foreach ([
'app' => [\Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
'auth.driver' => [\Illuminate\Contracts\Auth\Guard::class],
'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class],
'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
'cache.store' => [\Illuminate\Cache\Repository::class, \Illuminate\Contracts\Cache\Repository::class],
'config' => [\Illuminate\Config\Repository::class, \Illuminate\Contracts\Config\Repository::class],
'cookie' => [\Illuminate\Cookie\CookieJar::class, \Illuminate\Contracts\Cookie\Factory::class, \Illuminate\Contracts\Cookie\QueueingFactory::class],
'encrypter' => [\Illuminate\Encryption\Encrypter::class, \Illuminate\Contracts\Encryption\Encrypter::class],
'db' => [\Illuminate\Database\DatabaseManager::class],
'db.connection' => [\Illuminate\Database\Connection::class, \Illuminate\Database\ConnectionInterface::class],
'events' => [\Illuminate\Events\Dispatcher::class, \Illuminate\Contracts\Events\Dispatcher::class],
'files' => [\Illuminate\Filesystem\Filesystem::class],
'filesystem' => [\Illuminate\Filesystem\FilesystemManager::class, \Illuminate\Contracts\Filesystem\Factory::class],
'filesystem.disk' => [\Illuminate\Contracts\Filesystem\Filesystem::class],
'filesystem.cloud' => [\Illuminate\Contracts\Filesystem\Cloud::class],
'hash' => [\Illuminate\Contracts\Hashing\Hasher::class],
'translator' => [\Illuminate\Translation\Translator::class, \Illuminate\Contracts\Translation\Translator::class],
'log' => [\Illuminate\Log\Writer::class, \Illuminate\Contracts\Logging\Log::class, \Psr\Log\LoggerInterface::class],
'mailer' => [\Illuminate\Mail\Mailer::class, \Illuminate\Contracts\Mail\Mailer::class, \Illuminate\Contracts\Mail\MailQueue::class],
'auth.password' => [\Illuminate\Auth\Passwords\PasswordBrokerManager::class, \Illuminate\Contracts\Auth\PasswordBrokerFactory::class],
'auth.password.broker' => [\Illuminate\Auth\Passwords\PasswordBroker::class, \Illuminate\Contracts\Auth\PasswordBroker::class],
'queue' => [\Illuminate\Queue\QueueManager::class, \Illuminate\Contracts\Queue\Factory::class, \Illuminate\Contracts\Queue\Monitor::class],
'queue.connection' => [\Illuminate\Contracts\Queue\Queue::class],
'queue.failer' => [\Illuminate\Queue\Failed\FailedJobProviderInterface::class],
'redirect' => [\Illuminate\Routing\Redirector::class],
'redis' => [\Illuminate\Redis\RedisManager::class, \Illuminate\Contracts\Redis\Factory::class],
'request' => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
'router' => [\Illuminate\Routing\Router::class, \Illuminate\Contracts\Routing\Registrar::class, \Illuminate\Contracts\Routing\BindingRegistrar::class],
'session' => [\Illuminate\Session\SessionManager::class],
'session.store' => [\Illuminate\Session\Store::class, \Illuminate\Contracts\Session\Session::class],
'url' => [\Illuminate\Routing\UrlGenerator::class, \Illuminate\Contracts\Routing\UrlGenerator::class],
'validator' => [\Illuminate\Validation\Factory::class, \Illuminate\Contracts\Validation\Factory::class],
'view' => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],
] as $key => $aliases) {
foreach ($aliases as $alias) {
$this->alias($key, $alias);
}
}
}

1.2 核心类绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$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 核心类
  • 命令行 核心类
  • 异常处理类

1.3 实例化Http核心类

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

Http 核心类的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}

上述过程主要做的事是将中间件赋值给路由

  • 中间件顺序优先级列表
  • 中间件组
  • 中间件别名

核心类 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
81
82
83
84
85
86
87
88
89
90
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
// 全局中间件,最先调用
protected $middleware = [

// 检测是否应用是否进入『维护模式』
// 见:https://d.laravel-china.org/docs/5.5/configuration#maintenance-mode
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,

// 检测请求的数据是否过大
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,

// 对提交的请求参数进行 PHP 函数 `trim()` 处理
\App\Http\Middleware\TrimStrings::class,

// 将提交请求参数中空子串转换为 null
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,

// 修正代理服务器后的服务器参数
\App\Http\Middleware\TrustProxies::class,
];

// 定义中间件组
protected $middlewareGroups = [

// Web 中间件组,应用于 routes/web.php 路由文件
'web' => [
// Cookie 加密解密
\App\Http\Middleware\EncryptCookies::class,

// 将 Cookie 添加到响应中
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,

// 开启会话
\Illuminate\Session\Middleware\StartSession::class,

// 认证用户,此中间件以后 Auth 类才能生效
// 见:https://d.laravel-china.org/docs/5.5/authentication
\Illuminate\Session\Middleware\AuthenticateSession::class,

// 将系统的错误数据注入到视图变量 $errors 中
\Illuminate\View\Middleware\ShareErrorsFromSession::class,

// 检验 CSRF ,防止跨站请求伪造的安全威胁
// 见:https://d.laravel-china.org/docs/5.5/csrf
\App\Http\Middleware\VerifyCsrfToken::class,

// 处理路由绑定
// 见:https://d.laravel-china.org/docs/5.5/routing#route-model-binding
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],

// API 中间件组,应用于 routes/api.php 路由文件
'api' => [
// 使用别名来调用中间件
// 请见:https://d.laravel-china.org/docs/5.5/middleware#为路由分配中间件
'throttle:60,1',
'bindings',
],
];

// 中间件别名设置,允许你使用别名调用中间件,例如上面的 api 中间件组调用
protected $routeMiddleware = [

// 只有登录用户才能访问,我们在控制器的构造方法中大量使用
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,

// HTTP Basic Auth 认证
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,

// 处理路由绑定
// 见:https://d.laravel-china.org/docs/5.5/routing#route-model-binding
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,

// 用户授权功能
'can' => \Illuminate\Auth\Middleware\Authorize::class,

// 只有游客才能访问,在 register 和 login 请求中使用,只有未登录用户才能访问这些页面
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,

// 访问节流,类似于 『1 分钟只能请求 10 次』的需求,一般在 API 中使用
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
}

2. 请求实例化

以处理 Http 请求为例

index.php 入口文件

1
2
3
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);

请求是通过 Illuminate\Http\Request::capture() 实例化的, 主要是将请求信息以对象形式表现出来

3.请求处理

入口文件:

1
2
3
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);

$kernel->handle(...) 处理请求过程

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
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;
}


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());
}


protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);

return $this->router->dispatch($request);
};
}

实际处理请求逻辑主要在 sendRequestThroughRouter 方法中, 它主要做了:

  • 核心类的初始化

  • 经由中间件过滤后将请求最终交由 Router 处理

对于 Http 请求处理, 中间件包括:

1
2
3
4
5
6
7
8
9
10
11
12
13
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,

];

该中间件数组定义在 Http 核心类中, 同时在核心类的构造函数中传递给 Router 类

3.1 请求处理环境初始化

核心类的初始化 bootstrap()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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,
];

# 初始化
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}

protected function bootstrappers()
{
return $this->bootstrappers;
}

在服务容器 Application 类中

1
2
3
4
5
6
7
8
9
10
11
12
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;

foreach ($bootstrappers as $bootstrapper) {
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

$this->make($bootstrapper)->bootstrap($this);

$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
}

该步骤主要是主要是对核心类中定义的 $bootstrappers 数组元素(引导类)初始化.

bootstrap 过程具体是在服务容器来中进行, 由核心类调用并传入待初始化的类

Http 核心类默认包含以下 6 个启动服务:

  • 环境监测 \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class

.env 文件中解析环境变量到 getevn(), $_ENV, $_SERVER, 依赖 vlucas/phpdotenv 扩展包

  • 配置加载 \Illuminate\Foundation\Bootstrap\LoadConfiguration::class

载入 config 目录下所有 php 配置文件, 并将生成的配置存储类绑定到服务容器 $app['config']

同时配置时区及 多字节格式(utf8)

  • 异常处理 \Illuminate\Foundation\Bootstrap\HandleExceptions::class

报告所有错误 error_report(E_ALL)

提供对未捕获的异常, 错误的全局处理 set_error_handler, set_exception_handler, register_shutdown_function

  • 外观注册 \Illuminate\Foundation\Bootstrap\RegisterFacades::class

app.aliases 中读取外观配置数组

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
'aliases' => [

'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class,
'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
'Bus' => Illuminate\Support\Facades\Bus::class,
'Cache' => Illuminate\Support\Facades\Cache::class,
'Config' => Illuminate\Support\Facades\Config::class,
'Cookie' => Illuminate\Support\Facades\Cookie::class,
'Crypt' => Illuminate\Support\Facades\Crypt::class,
'DB' => Illuminate\Support\Facades\DB::class,
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
'Event' => Illuminate\Support\Facades\Event::class,
'File' => Illuminate\Support\Facades\File::class,
'Gate' => Illuminate\Support\Facades\Gate::class,
'Hash' => Illuminate\Support\Facades\Hash::class,
'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class,
'Notification' => Illuminate\Support\Facades\Notification::class,
'Password' => Illuminate\Support\Facades\Password::class,
'Queue' => Illuminate\Support\Facades\Queue::class,
'Redirect' => Illuminate\Support\Facades\Redirect::class,
'Redis' => Illuminate\Support\Facades\Redis::class,
'Request' => Illuminate\Support\Facades\Request::class,
'Response' => Illuminate\Support\Facades\Response::class,
'Route' => Illuminate\Support\Facades\Route::class,
'Schema' => Illuminate\Support\Facades\Schema::class,
'Session' => Illuminate\Support\Facades\Session::class,
'Storage' => Illuminate\Support\Facades\Storage::class,
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,

],

使用 spl_autoload_register(...) 处理类加载, 配合 class_alias() 提供类的别名调用

Facade外观类基类依赖__callStatic` 调用方法( 使用服务容器实例化对应类)

  • 服务提供者注册 \Illuminate\Foundation\Bootstrap\RegisterProviders::class

app.providers 中读取所有服务提供者

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
'providers' => [

/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,

/*
* Package Service Providers...
*/

/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class, # 路由表生成
],

服务提供者经过解析后分为 3 种类型的服务提供者:

- eager 类型

马上调用 register 注册

- deferred 类型

记录下来, 当服务容器解析对应服务时, 才注册对应的服务提供者

- when 类型

记录下来, 当对应 event 触发时在注册对应服务提供者

  • 启动提供者 \Illuminate\Foundation\Bootstrap\BootProviders::class

调用服务容器的 boot() 方法, 依次调用在服务容器中 register 的所有服务提供者的 boot() 方法

3.2 路由处理请求

在内核处理请求, 将请求实例通过中间件处理后, 将请求的处理交给路由 Router 进行控制器的分发.

Http Kernel

1
2
3
4
5
6
7
8
protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);

return $this->router->dispatch($request);
};
}

路由表存储结构说明

Illuminate\Routing\Route 存储单条路由

Illuminate\Routing\RouteCollection 保存所有 Route 实例, 形成路由表

Illuminate\Routing\Router 类实例持有 RouteCollection 路由表实例.

即, 一个 Router 持有一个 RouteCollection, 而 RouteCollection 拥有 N 个 Route

Router 中对请求的处理同样经过一系列的 路由中间件

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
# 路由处理请求的入库
public function dispatchToRoute(Request $request)
{
return $this->runRoute($request, $this->findRoute($request));
}

# 根据请求的 url 和 method 查找对应的 route
protected function findRoute($request)
{
$this->current = $route = $this->routes->match($request);

$this->container->instance(Route::class, $route);

return $route;
}

# 根据对应的请求和路由条目, 返回相应的 $response
protected function runRoute(Request $request, Route $route)
{
$request->setRouteResolver(function () use ($route) {
return $route;
});

$this->events->dispatch(new Events\RouteMatched($route, $request));

return $this->prepareResponse($request,
$this->runRouteWithinStack($route, $request)
);
}

# 请求经过路由中间件过滤后, 交由 route 的 run() 方法处理
protected function runRouteWithinStack(Route $route, Request $request)
{
$shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
$this->container->make('middleware.disable') === true;

$middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);

return (new Pipeline($this->container))
->send($request)
->through($middleware)
->then(function ($request) use ($route) {
return $this->prepareResponse(
$request, $route->run()
);
});
}

routerun() 方法最终将请求转给 Illuminate\Routing\ControllerDispatcher::dispatch 处理

1
2
3
4
5
6
7
8
9
10
11
12
public function dispatch(Route $route, $controller, $method)
{
$parameters = $this->resolveClassMethodDependencies(
$route->parametersWithoutNulls(), $controller, $method
);

if (method_exists($controller, 'callAction')) {
return $controller->callAction($method, $parameters);
}

return $controller->{$method}(...array_values($parameters));
}

剩下的事情就是 Controller控制器 的事了.

3.3 处理返回的 Response

Router 中有一个方法, 用于对返回的 $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
public function prepareResponse($request, $response)
{
return static::toResponse($request, $response);
}

/**
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public static function toResponse($request, $response)
{
if ($response instanceof Responsable) {
$response = $response->toResponse($request);
}

if ($response instanceof PsrResponseInterface) {
$response = (new HttpFoundationFactory)->createResponse($response);
} elseif (! $response instanceof SymfonyResponse &&
($response instanceof Arrayable ||
$response instanceof Jsonable ||
$response instanceof ArrayObject ||
$response instanceof JsonSerializable ||
is_array($response))) {
$response = new JsonResponse($response);
} elseif (! $response instanceof SymfonyResponse) {
$response = new Response($response);
}

if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
$response->setNotModified();
}

return $response->prepare($request); # 最后的处理
}

上述过程中, 在返回 $response 之前进行了最后的处理 $response->prepare($request)

该过程是在 Symfony\Component\HttpFoundation\Response::prepare() 中进行

对响应的封装是通过 Illuminate\Http\Response 类完成, 该类底层是 Symfony 框架的 Response 类

即, Symfony\Component\HttpFoundation\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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public function prepare(Request $request)
{
$headers = $this->headers;

if ($this->isInformational() || $this->isEmpty()) {
$this->setContent(null);
$headers->remove('Content-Type');
$headers->remove('Content-Length');
} else {
// Content-type based on the Request
if (!$headers->has('Content-Type')) {
$format = $request->getRequestFormat();
if (null !== $format && $mimeType = $request->getMimeType($format)) {
$headers->set('Content-Type', $mimeType);
}
}

// Fix Content-Type
$charset = $this->charset ?: 'UTF-8';
if (!$headers->has('Content-Type')) {
$headers->set('Content-Type', 'text/html; charset='.$charset);
} elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
// add the charset
$headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
}

// Fix Content-Length
if ($headers->has('Transfer-Encoding')) {
$headers->remove('Content-Length');
}

if ($request->isMethod('HEAD')) {
// cf. RFC2616 14.13
$length = $headers->get('Content-Length');
$this->setContent(null);
if ($length) {
$headers->set('Content-Length', $length);
}
}
}

// Fix protocol
if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
$this->setProtocolVersion('1.1');
}

// Check if we need to send extra expire info headers
if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) {
$this->headers->set('pragma', 'no-cache');
$this->headers->set('expires', -1);
}

$this->ensureIEOverSSLCompatibility($request);

return $this;
}

4. 响应发送和程序终止

4.1 响应的发送

index.php 入口文件的最后是将响应返回给客户端

$response->send();

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
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;
}

public function sendHeaders()
{
// headers have already been sent by the developer
if (headers_sent()) {
return $this;
}

// headers
foreach ($this->headers->allPreserveCase() as $name => $values) {
foreach ($values as $value) {
header($name.': '.$value, false, $this->statusCode);
}
}

// status
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);

return $this;
}

public function sendContent()
{
echo $this->content;

return $this;
}

4.2 请求中止

index.php 入口文件的最后:

\$kernel->terminate( \$request, \$response );

依旧以 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
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) = $this->parseMiddleware($middleware);

$instance = $this->app->make($name);

if (method_exists($instance, 'terminate')) {
$instance->terminate($request, $response);
}
}
}

此处的中间件指的是定义在 Kernel 中的 $middleware 中间件数组列表, 不包含 路由中间件.

Laravel 5.1 注: 默认只有会话中间件包含 terminate() 函数

Application 服务容器的中止处理函数

1
2
3
4
5
6
public function terminate()
{
foreach ($this->terminatingCallbacks as $terminating) {
$this->call($terminating);
}
}
关注作者公众号,获取更多资源!
赏作者一杯咖啡~