關於Laravel的二、三事(1)一次請求的生命週期

AlanJager發表於2020-04-07

最初,一次請求將會到達public/index.php,這個檔案的內容非常簡單:

require __DIR__.'/../bootstrap/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/autoload.php和bootstrap/app.php,首先來看autoload.php的內容:

define('LARAVEL_START', microtime(true));
require __DIR__.'/../vendor/autoload.php';
$compiledPath = __DIR__.'/cache/compiled.php';

if (file_exists($compiledPath)) {
    require $compiledPath;
}
/vendor/autoload.php檔案是composer提供的一個載入檔案,引入這個檔案之後我們就可以方便的使用其他類了。

之後是/cache/compile.php檔案,包含了常用的類,以提升處理請求的效能。

再來看bootstrap/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;

主要是返回一些環境配置資訊和各種中介軟體和簡寫的資訊。


之後index.php建立了一個Kernel例項,開啟Illuminate\Contracts\Http\Kernel::class,程式碼如下:

class Kernel extends HttpKernel
{
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
    ];

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
        ],

        'api' => [
            'throttle:60,1',
        ],
    ];

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ];
}

定義了一系列中介軟體的陣列,這些中介軟體將會在處理請求時被執行

EncryptCookies::class用於處理對於cookies的加密解密;

AddQueuedCookiesToResponse::class用於給Response的header新增cookies資訊;

StartSession::class用於首次請求時建立session資訊,處理請求時驗證session的有效性,在請求結束時儲存session資訊;

ShareErrorsFromSession::class返回session的錯誤資訊;

VerifyCsrfToken::class驗證CSRF令牌;


路由中介軟體:

Illuminate\Support\Facades\Auth\Authenticate::class用於驗證發出的請求型別;

Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class用基本的Auth進行驗證;

Illuminate\Foundation\Http\Middleware\Authorize::class將請求與模型繫結;

App\Http\Middleware\RedirectIfAuthenticated::class驗證後重定向;

Illuminate\Routing\Middleware\ThrottleRequests::class用於請求頻率的限制。


然後是對請求的捕捉,捕捉之後交給kernel::handle():

 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']->fire('kernel.handled', [$request, $response]);

        return $response;
    }

首先是接收引數,然後通過路由轉發請求。


最後執行kernel::terminate():

public function terminate($request, $response)
    {
        $middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge(
            $this->gatherRouteMiddlewares($request),
            $this->middleware
        );

        foreach ($middlewares as $middleware) {
            list($name, $parameters) = $this->parseMiddleware($middleware);

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

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

        $this->app->terminate();
    }

通過kernel::gatherRouteMiddlewares()獲取獲取所有路由,並且對應要使用的中介軟體,然後對所有的中介軟體進行解析並進行處理,執行完之後終結請求。


相關文章