Laravel解析-入口文件

整体逻辑

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

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

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

贴出源码文件,我们往下看。

<?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_START 的意义

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

laravel 的维护模式

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

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

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

跑起来

//引入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);

c0825e54-7802-f453-f8f7-787858c731ad

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。