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

use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Check If The Application Is Under Maintenance
|--------------------------------------------------------------------------
|
| If the application is in maintenance / demo mode via the "down" command
| we will load this file so that any pre-rendered content can be shown
| instead of starting the framework, which could cause an exception.
|
*/

if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
    require $maintenance;
}

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| this application. We just need to utilize it! We'll simply require it
| into the script here so we don't need to manually load our classes.
|
*/

require __DIR__.'/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request using
| the application's HTTP kernel. Then, we will send the response back
| to this client's browser, allowing them to enjoy our application.
|
*/

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

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

$response = $kernel->handle(
    $request = Request::capture()
)->send();

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

整体逻辑

众所周知,代码是从上而下执行的,我们先大体了解下 laravel 的入口文件都做了什么事情,作者也是第一次分析源码,如有不足的地方敬请谅解。

首先定义了一个 LARAVEL_START 的常量,值是浮点类型的当前 Unix 时间戳的微秒数->接着检查应用程序是否在维护,也就是检查 storage/framework/maintenance.php 这个路径的文件是否存在->接着加载 autoload、引入 bootstrap/app.php 直到跑起来;

看到这里我们会有很多疑问,最起码我有很多疑问,比如一开始定义的常量有什么用?检查是否维护为什么要看 maintenance.php 存不存在?程序跑起来是怎么跑起来的?具体做了什么?

我们往下看。

LARAVEL_START 的意义

作为第一行代码,它的意义其实没有想象的那么高深,它的作用仅仅是为了记录程序开始时间,从而统计程序的总执行时间。(用截止时间 - 启动时间 = 程序执行时间),我们通常会说程序运行慢,不够快,那么是怎么得出来的?就是这么得出来的。

laravel 的维护模式

回到我们上面说的问题:检查是否维护为什么要看 maintenance.php 存不存在?我们运行 php artisan down 命令后会发现,在 storage/framework 下生成了两个文件,一个是 down,一个是 maintenance.php,这就是结果,你可以理解到这里,也可以继续往下深入,我想说的是,重要的不是结果,而是过程,同理,我主要会写我思考的过程,而不是我思考后发现的结果。

首先,我们通过阅读官方文档后得知,laravel 的维护模式是通过 php artisan down 命令开始的,通过 php artisan up 命令结束的。然后,我们去找 php artisan down 这个命令做了什么东西,可以全局搜索 DownCommand 来找到我们需要的文件;

  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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<?php

namespace Illuminate\Foundation\Console;

use App\Http\Middleware\PreventRequestsDuringMaintenance;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Foundation\Events\MaintenanceModeEnabled;
use Illuminate\Foundation\Exceptions\RegisterErrorViewPaths;
use Throwable;

class DownCommand extends Command
{
    /**
     * 定义控制台命令
     *
     * @var string
     */
    protected $signature = 'down {--redirect= : The path that users should be redirected to}
                                 {--render= : The view that should be prerendered for display during maintenance mode}
                                 {--retry= : The number of seconds after which the request may be retried}
                                 {--refresh= : The number of seconds after which the browser may refresh}
                                 {--secret= : The secret phrase that may be used to bypass maintenance mode}
                                 {--status=503 : The status code that should be used when returning the maintenance mode response}';

    /**
     * 命令标识
     *
     * @var string|null
     */
    protected static $defaultName = 'down';

    /**
     * 说明
     *
     * @var string
     */
    protected $description = 'Put the application into maintenance / demo mode';

    /**
     * 具体执行了那些内容
     *
     * @return int
     */
    public function handle()
    {
        try {
          //这里判断是否已经处于维护模式,如果是返回Application is already down.
            if ($this->laravel->maintenanceMode()->active()) {
                $this->comment('Application is already down.');

                return 0;
            }
						//创建down文件,写入getDownFilePayload()方法里面返回的数据
            $this->laravel->maintenanceMode()->activate($this->getDownFilePayload());
						//写入数据,就是创建maintenance.php文件,写入maintenance-mode.stub里面的数据
            file_put_contents(
                storage_path('framework/maintenance.php'),
                file_get_contents(__DIR__.'/stubs/maintenance-mode.stub')
            );
						//开启事件
            $this->laravel->get('events')->dispatch(MaintenanceModeEnabled::class);
						//输出提示语
            $this->comment('Application is now in maintenance mode.');
        } catch (Exception $e) {
            $this->error('Failed to enter maintenance mode.');

            $this->error($e->getMessage());

            return 1;
        }
    }

    /**
     * Get the payload to be placed in the "down" file.
     *
     * @return array
     */
    protected function getDownFilePayload()
    {
        return [
            'except' => $this->excludedPaths(),
            'redirect' => $this->redirectPath(),
            'retry' => $this->getRetryTime(),
            'refresh' => $this->option('refresh'),
            'secret' => $this->option('secret'),
            'status' => (int) $this->option('status', 503),
            'template' => $this->option('render') ? $this->prerenderView() : null,
        ];
    }

    /**
     * Get the paths that should be excluded from maintenance mode.
     *
     * @return array
     */
    protected function excludedPaths()
    {
        try {
            return $this->laravel->make(PreventRequestsDuringMaintenance::class)->getExcludedPaths();
        } catch (Throwable $e) {
            return [];
        }
    }

    /**
     * Get the path that users should be redirected to.
     *
     * @return string
     */
    protected function redirectPath()
    {
        if ($this->option('redirect') && $this->option('redirect') !== '/') {
            return '/'.trim($this->option('redirect'), '/');
        }

        return $this->option('redirect');
    }

    /**
     * Prerender the specified view so that it can be rendered even before loading Composer.
     *
     * @return string
     */
    protected function prerenderView()
    {
        (new RegisterErrorViewPaths)();

        return view($this->option('render'), [
            'retryAfter' => $this->option('retry'),
        ])->render();
    }

    /**
     * Get the number of seconds the client should wait before retrying their request.
     *
     * @return int|null
     */
    protected function getRetryTime()
    {
        $retry = $this->option('retry');

        return is_numeric($retry) && $retry > 0 ? (int) $retry : null;
    }
}

跑起来

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//引入Composer注册自动加载程序, Laravel的自动类文件自动加载等功能都是通过Composer来实现的
require __DIR__.'/../vendor/autoload.php';
//引入核心应用类, 主要是实现核心类库加载以及Laravel框架中核心的服务容器注册加载等
$app = require_once __DIR__.'/../bootstrap/app.php';
//获取在app.php中已经注册的Kernel
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//容器中绑定的Kernel是App\Http下的,该类继承了Illuminate\Foundation\Http下的Kernel,这里调用的就是父类的handle方法
//主要实现的功能是通过管道实现中间件及路由分发执行
$response = $kernel->handle(
  	//创建request实例
    $request = Request::capture()
)->send();//响应请求
//响应中间件
$kernel->terminate($request, $response);