深度挖掘 Laravel 生命週期

liuqing_hu發表於2018-04-26

本文首發於個人部落格 深度挖掘 Laravel 生命週期,轉載請註明出處。

這篇文章我們來聊聊 「Laravel 生命週期」 這個主題。雖然網路上已經有很多關於這個主題的探討,但這個主題依然值得我們去研究和學習。

我想說的是當我們在決定使用某項技術的時候,除了需要了解它能「做什麼」,其實還應當研究它是「怎麼做的」。

Laravel 框架或者說任何一個 Web 專案,我們都需要理解它究竟是如何接收到使用者發起的 HTTP 請求的;又是如何響應結果給使用者的;在處理請求和響應的過程中都存在哪些處理值得深入學習。

所有這些內容其實都包含在 「Laravel 生命週期」 這個主題裡面。

本文較長建議使用合適的 IDE 進行程式碼查閱;或者通過文中的連結,或是程式碼註釋的 「@see」部分直接在 Github 暢讀程式碼。

目錄結構

  • 一 摘要
  • 二 生命週期之始末
    • 2.1 載入專案依賴
    • 2.2 建立 Laravel 應用例項
      • 2.2.1 建立應用例項
      • 2.2.2 核心繫結
      • 2.2.3 註冊異常處理
      • 2.2.4 小結
    • 2.3 接收請求並響應
      • 2.3.1 解析核心例項
      • 2.3.2 處理 HTTP 請求
        • 2.3.2.1 建立請求例項
        • 2.3.2.2 處理請求
          • 2.3.2.2.1 啟動「載入程式」
          • 2.3.2.2.2 傳送請求至路由
    • 2.4 傳送響應
    • 2.5 終止程式
  • 三 總結
  • 參考資料

一 摘要

Laravel 生命週期(或者說請求生命週期)概括起來主要分為 3 個主要階段:

  • 載入專案依賴
  • 建立 Laravel 應用例項
  • 接收請求並響應

而這 3 個階段的處理都發生在入口檔案 public/index.php 檔案內(public/index.php 是一個新安裝的 Laravel 專案預設入口檔案)。

然而 index.php 檔案僅包含極少的程式碼,但卻出色的完成了一個 HTTP 請求從接收到響應的全部過程,邏輯組織的幾近完美。

我們來看下入口檔案實現的程式碼:

<?php
// 階段一
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);

二 生命週期之始末

2.1 載入專案依賴

現代 PHP 依賴於 Composer 包管理器,入口檔案通過引入由 Composer 包管理器自動生成的類載入程式,可以輕鬆註冊並載入專案所依賴的第三方元件庫。

所有元件的載入工作,僅需一行程式碼即可完成:

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

2.2 建立 Laravel 應用例項

建立應用例項(或稱服務容器),由位於 bootstrap/app.php 檔案裡的載入程式完成,建立服務容器的過程即為應用初始化的過程,專案初始化時將完成包括:註冊專案基礎服務、註冊專案服務提供者別名、註冊目錄路徑等在內的一些列註冊工作。

下面是 bootstrap/app.php 的程式碼,包含兩個主要部分「建立應用例項」和「繫結核心至 APP 服務容器」:

<?php
// 第一部分: 建立應用例項
$app = new Illuminate\Foundation\Application(
    realpath(__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;

2.2.1 建立應用例項

建立應用例項即例項化 Illuminate\Foundation\Application 這個服務容器,後續我們稱其為 APP 容器。在建立 APP 容器主要會完成:註冊應用的基礎路徑並將路徑繫結到 APP 容器 、註冊基礎服務提供者至 APP 容器 、註冊核心容器別名至 APP 容器 等基礎服務的註冊工作。

    /**
     * Create a new Illuminate application instance.
     *
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Application.php#L162:27
     * @param  string|null  $basePath
     * @return void
     */
    public function __construct($basePath = null)
    {
        if ($basePath) {
            $this->setBasePath($basePath);
        }
        $this->registerBaseBindings();
        $this->registerBaseServiceProviders();
        $this->registerCoreContainerAliases();
    }

2.2.2 核心繫結

接著將關注的焦點轉移到繫結核心部分。

Laravel 會依據 HTTP 請求的執行環境的不同,將請求傳送至相應的核心: HTTP 核心Console 核心。無論 HTTP 核心還是 Console 核心,它們的作用都是是接收一個 HTTP 請求,隨後返回一個響應,就是這麼簡單。

這篇文章主要研究 HTTP 核心,HTTP 核心繼承自 Illuminate\Foundation\Http\Kernel 類.

在 「HTTP 核心」 內它定義了 [中介軟體]((https://github.com/laravel/laravel/blob/master/app/Http/Kernel.php) 相關陣列;在 「Illuminate\Foundation\Http\Kernel」 類內部定義了屬性名為 「bootstrappers」 的 載入程式 陣列。

  • 中介軟體 提供了一種方便的機制來過濾進入應用的 HTTP 請求。
  • 「載入程式」 包括完成環境檢測、配置載入、異常處理、Facades 註冊、服務提供者註冊、啟動服務這六個載入程式。

至於 「中介軟體」 和 「載入程式」如何被使用的,會在後面的章節講解。

2.2.3 註冊異常處理

專案的異常處理由 App\Exceptions\Handler::class 類完成,這邊也不做深入的講解。

2.2.4 本節小結

通過上面的分析我們可以發現在「建立 Laravel 應用例項」這個階段它做了很多的基礎工作,包括但不限於:建立 APP 容器、註冊應用路徑、註冊基礎服務提供者、配置中介軟體和載入程式等。

2.3 接收請求並響應

在完成建立 APP 容器後即進入了第三個階段 「接收請求並響應」。

「接收請求並響應」有關程式碼如下:

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

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

$response->send();

我們需要逐行分析上面的程式碼,才能窺探其中的原貌。

2.3.1 解析核心例項

在第二階段我們已經將 HTTP 核心Console 核心 繫結到了 APP 容器,使用時通過 APP 容器make() 方法將核心解析出來,解析的過程就是核心例項化的過程。

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

核心例項化時它的內部究竟又做了哪些操作呢?

進一步挖掘 Illuminate\Foundation\Http\Kernel 核心的 __construct(Illuminate\Contracts\Foundation\Application $app, \Illuminate\Routing\Router $router) 構造方法,它接收 APP 容器路由器 兩個引數。

在例項化核心時,建構函式內將在 HTTP 核心定義的「中介軟體組」註冊到 路由器,註冊完後就可以在實際處理 HTTP 請求前呼叫這些「中介軟體」實現 過濾 請求的目的。

...
    /**
     * Create a new HTTP kernel instance. 建立 HTTP 核心例項
     * 
     * @class Illuminate\Foundation\Http\Kernel
     * @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);
        }
    }
...
...
    /**
     * Register a group of middleware. 註冊中介軟體組
     *
     * @class \Illuminate\Routing\Router
     * @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. 註冊中介軟體別名
     *
     * @class \Illuminate\Routing\Router
     * @param  string  $name
     * @param  string  $class
     * @return $this
     */
    public function aliasMiddleware($name, $class)
    {
        $this->middleware[$name] = $class;

        return $this;
    }
...

2.3.2 處理 HTTP 請求

之前的所有處理,基本都是圍繞在配置變數、註冊服務等執行環境的構建上,構建完成後才是真刀真槍的來處理一個「HTTP 請求」。

處理請求實際包含兩個階段:

  • 建立請求例項
  • 處理請求
// 處理請求
$response = $kernel->handle(
    // 建立請求例項
    $request = Illuminate\Http\Request::capture()
);
2.3.2.1 建立請求例項

請求例項 Illuminate\Http\Requestcapture() 方法內部通過 Symfony 例項建立一個 Laravel 請求例項。這樣我們就可以獲取到使用者請求報文的相關資訊了。

    /**
     * Create a new Illuminate HTTP request from server variables.
     * 
     * @class Illuminate\Http\Request
     * @return static
     */
    public static function capture()
    {
        static::enableHttpMethodParameterOverride();
        return static::createFromBase(SymfonyRequest::createFromGlobals());
    }

    /**
     * Create an Illuminate request from a Symfony instance.
     *
     * @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Request.php
     * @param  \Symfony\Component\HttpFoundation\Request  $request
     * @return \Illuminate\Http\Request
     */
    public static function createFromBase(SymfonyRequest $request)
    {
        if ($request instanceof static) {
            return $request;
        }

        $content = $request->content;

        $request = (new static)->duplicate(
            $request->query->all(), $request->request->all(), $request->attributes->all(),
            $request->cookies->all(), $request->files->all(), $request->server->all()
        );

        $request->content = $content;

        $request->request = $request->getInputSource();

        return $request;
    }
2.3.2.2 處理請求

請求處理髮生在 HTTP 核心handle() 方法內。

    /**
     * Handle an incoming HTTP request.
     *
     * @class Illuminate\Foundation\Http\Kernel
     * @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 請求,並最終生成一個 HTTP 響應。

繼續深入到處理 HTTP 請求的方法 $this->sendRequestThroughRouter($request) 內部。

    /**
     * Send the given request through the middleware / router.
     *
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php
     * @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());
    }

將發現這段程式碼沒有一行廢話,它完成了大量的邏輯處理:

  • 首先,將 $request 例項註冊到 APP 容器 供後續使用;
  • 之後,清除之前 $request 例項快取;
  • 然後,啟動「載入程式」;
  • 最後,傳送請求至路由。
2.3.2.2.1 啟動「載入程式」

記得我們在之前「2.2.2 核心繫結」章節,有介紹在「HTTP 核心」中有把「載入程式(bootstrappers)」繫結到了 APP 容器,以及這些載入程式的具體功能。

但是沒有聊如何呼叫這些「載入程式」。

    /**
     * Send the given request through the middleware / router.
     *
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        ...

        // 啟動 「載入程式」
        $this->bootstrap();

        ...
    }

上面的程式碼塊說明在 $this->bootstrap(); 方法內部有實際呼叫「載入程式」,而 bootstrap() 實際呼叫的是 APP 容器的 bootstrapWith(),來看看

... 
    /**
     * 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,
    ];

    /**
     * Bootstrap the application for HTTP requests.
     * 
     * @class Illuminate\Foundation\Http\Kernel
     * @return void
     */
    public function bootstrap()
    {
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }

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

最終還是要看 Illuminate\Foundation\ApplicationbootstrapWith() 方法究竟如何來啟動這些載入程式的。

    /**
     * Run the given array of bootstrap classes.
     * 
     * @class  Illuminate\Foundation\Application APP 容器
     * @param  array  $bootstrappers
     * @return void
     */
    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]);
        }
    }

我們看到在 APP 容器內,會先解析對應的「載入程式」(即例項化),隨後呼叫「載入程式」的 bootstrap() 完成的「載入程式」的啟動操作。

作為示例我們隨便挑一個「載入程式」來看看其內部的啟動原理。

這邊我們選 Illuminate\Foundation\Bootstrap\LoadConfiguration::class,它的功能是載入配置檔案。

還記得我們講解「2.2 建立 Laravel 應用例項」章節的時候有「註冊應用的基礎路徑並將路徑繫結到 APP 容器」。此時,LoadConfiguration 類就是將 config 目錄下的所有配置檔案讀取到一個集合中,這樣我們就可以專案裡通過 config() 輔助函式獲取配置資料。

<?php
class LoadConfiguration
{
    /**
     * Bootstrap the given application.
     *
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        $items = [];

        if (file_exists($cached = $app->getCachedConfigPath())) {
            $items = require $cached;
            $loadedFromCache = true;
        }

        $app->instance('config', $config = new Repository($items));

        if (! isset($loadedFromCache)) {
            $this->loadConfigurationFiles($app, $config);
        }

        $app->detectEnvironment(function () use ($config) {
            return $config->get('app.env', 'production');
        });

        date_default_timezone_set($config->get('app.timezone', 'UTC'));
        mb_internal_encoding('UTF-8');
    }

    /**
     * Load the configuration items from all of the files.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Contracts\Config\Repository  $repository
     * @return void
     * @throws \Exception
     */
    protected function loadConfigurationFiles(Application $app, RepositoryContract $repository)
    {
        $files = $this->getConfigurationFiles($app);

        if (! isset($files['app'])) {
            throw new Exception('Unable to load the "app" configuration file.');
        }

        foreach ($files as $key => $path) {
            $repository->set($key, require $path);
        }
    }

    ...
}

所有 「載入程式」列表功能如下:

2.3.2.2.2 傳送請求至路由

完成「載入程式」啟動操作後,隨機進入到請求處理階段。

    /**
     * Send the given request through the middleware / router.
     *
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        ...

        // 傳送請求至路由
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

在 「傳送請求至路由」這行程式碼中,完成了:管道(pipeline)建立、將 $request 傳入管道、對 $request 執行「中介軟體」處理和實際的請求處理四個不同的操作。

在開始前我們需要知道在 Laravel 中有個「中介軟體」 的概念,即使你還不知道,也沒關係,僅需知道它的功能是在處理請求操作之前,對請求進行過濾處理即可,僅當請求符合「中介軟體」的驗證規則時才會繼續執行後續處理。

有關 「管道」的相關知識不在本文講解範圍內。

那麼,究竟一個請求是如何被處理的呢?

我們來看看 $this->dispatchToRouter() 這句程式碼,它的方法宣告如下:


    /**
     * Get the route dispatcher callback. 獲取一個路由分發器匿名函式
     *
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php
     * @return \Closure
     */
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);

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

回顧下「2.3.1 解析核心例項」章節,可知我們已經將 Illuminate\Routing\Router 物件賦值給 $this->router 屬性。

通過 router 例項的 disptach() 方法去執行 HTTP 請求,在它的內部會完成如下處理:

  1. 查詢對應的路由例項
  2. 通過一個例項棧執行給定的路由
  3. 執行在 routes/web.php 配置的匹配到的控制器或匿名函式
  4. 返回響應結果
<?php

...

//  @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Routing/Router.php
class Router implements RegistrarContract, BindingRegistrar
{    
    /**
     * Dispatch the request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     */
    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;

        return $this->dispatchToRoute($request);
    }

    /**
     * Dispatch the request to a route and return the response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    public function dispatchToRoute(Request $request)
    {
        return $this->runRoute($request, $this->findRoute($request));
    }

    /**
     * Find the route matching a given request. 1. 查詢對應的路由例項
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Routing\Route
     */
    protected function findRoute($request)
    {
        $this->current = $route = $this->routes->match($request);

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

        return $route;
    }

    /**
     * Return the response for the given route. 2. 通過一個例項棧執行給定的路由
     *
     * @param  Route  $route
     * @param  Request  $request
     * @return mixed
     */
    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)
        );
    }

    /**
     * Run the given route within a Stack "onion" instance. 通過一個例項棧執行給定的路由
     *
     * @param  \Illuminate\Routing\Route  $route
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    protected function runRouteWithinStack(Route $route, Request $request)
    {
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                                $this->container->make('middleware.disable') === true;

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

        // 4. 返回響應結果
        return (new Pipeline($this->container))
                        ->send($request)
                        ->through($middleware)
                        ->then(function ($request) use ($route) {
                            return $this->prepareResponse(
                                // 3. 執行在 routes/web.php 配置的匹配到的控制器或匿名函式
                                $request, $route->run()
                            );
                        });
    }

執行 $route->run() 的方法定義在 Illuminate\Routing\Route 類中,最終執行「在 routes/web.php 配置的匹配到的控制器或匿名函式」:

    /**
     * Run the route action and return the response.
     * 
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Routing/Route.php
     * @return mixed
     */
    public function run()
    {
        $this->container = $this->container ?: new Container;

        try {
            if ($this->isControllerAction()) {
                return $this->runController();
            }

            return $this->runCallable();
        } catch (HttpResponseException $e) {
            return $e->getResponse();
        }
    }

這部分如果路由的實現是一個控制器,會完成控制器例項化並執行指定方法;如果是一個匿名函式則直接呼叫這個匿名函式。

其執行結果會通過 Illuminate\Routing\Router::prepareResponse($request, $response) 生一個響應例項並返回。

至此,Laravel 就完成了一個 HTTP 請求的請求處理。

2.4 傳送響應

經過一系列漫長的操作,HTTP 請求進入的最終章 - 傳送響應值客戶端 $response->send()

<?php
// @see https://github.com/laravel/laravel/blob/master/public/index.php

// 階段一
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);

傳送響應由 Illuminate\Http\Response 父類 Symfony\Component\HttpFoundation\Response 中的 send() 方法完成。

    /**
     * Sends HTTP headers and content.
     * 
     * @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Response.php
     * @return $this
     */
    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;
    }

2.5 終止程式

程式終止,完成終止中介軟體的呼叫

// @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Http/Kernel.php

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, $parameters) = $this->parseMiddleware($middleware);
        $instance = $this->app->make($name);
        if (method_exists($instance, 'terminate')) {
            $instance->terminate($request, $response);
        }
    }
}

以上便是 Laravel 的請求生命週期的始末。

三 總結

在 「建立 Laravel 應用例項」時不僅會註冊專案基礎服務、註冊專案服務提供者別名、註冊目錄路徑等在內的一系列註冊工作;還會繫結 HTTP 核心及 Console 核心到 APP 容器, 同時在 HTTP 核心裡配置中介軟體和載入程式。

進入 「接收請求並響應」裡,會依據執行環境從 APP 容器 解析出 HTTP 核心或 Console 核心。如果是 HTTP 核心,還將把「中介軟體」及「載入程式」註冊到 APP 容器

所有初始化工作完成後便進入「處理 HTTP 請求」階段。

一個 Http 請求例項會被註冊到 APP 容器,通過啟動「載入程式」來設定環境變數、載入配置檔案等等系統環境配置;

隨後請求被分發到匹配的路由,在路由中執行「中介軟體」以過濾不滿足校驗規則的請求,只有通過「中介軟體」處理的請求才最終處理實際的控制器或匿名函式生成響應結果。

最後傳送響應給使用者,清理專案中的中介軟體,完成一個 「請求」 - 「響應」 的生命週期,之後我們的 Web 伺服器將等待下一輪使用者請求。

參考資料

感謝下列優秀的 Laravel 研究資料:

相關文章