メインコンテンツまでスキップ

ライフサイクル

ライフサイクルとは

プログラムやソフトウェアの開始から終わりまでの一連の段階やプロセスを指します。

Laravelにおけるライフサイクル

エントリポイント

エントリポイントとは、プログラムの実行を開始する場所のことです。

WEBサーバーの設定によりブラウザからのリクエストを受けると/public/index.phpにリダイレクトします。

エントリポイントはこのindex.phpになります。

index.phpの中

/public/index.php
require __DIR__.'/../vendor/autoload.php'; // 【1】autoloadの読み込み

$app = require_once __DIR__.'/../bootstrap/app.php'; // 【2】Applicationインスタンス作成

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); // 【3】HttpKernelインスタンス作成

$response = $kernel->handle( // 【5】Response取得
$request = Illuminate\Http\Request::capture() // 【4】Requestインスタンス作成
)->send(); // 【6】Response送信

$kernel->terminate($request, $response); // 【7】terminate()

このindex.phpファイルが、Laravelの処理全体フローそのものになります。

これらの【1】~【7】について説明していきます。

【1】autoloadの読み込み

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

/vendor/autoload.phpを読み込むことでオートロード機能1を使用できるようになります。

オートロードとはファイルを自動で読み込む仕組みのことで、他のファイルでのrequireが不要になります。

作成されたファイルは/vendor/composer/autoload_classmap.phpに記述されます。

【2】Applicationインスタンス作成

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

ここではapp.phpファイルを読み込んでいます。

/bootstrap/app.php
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

return $app;

app.phpはApplicationクラスをインスタンス化して返却しています。

このApplicationインスタンスが、通称「サービスコンテナ」と呼ばれるものです。

Laravelではサービスコンテナに登録されているサービスを利用してアプリケーションの開発を行なっていきます。

サービスコンテナはサービスを入れる入れ物の役割をもっており、サービスを利用するためには、サービスコンテナに事前にサービスを登録しておく必要があります。

サービスを登録する役目を持つものが「サービスプロバイダー」です。

インスタンス化したApplicationクラスのConstructorは/config/app.phpのproviders配列に記述されているサービスプロバイダーを参照して必要な時だけ呼び出せすための準備をしているということになります。

/config/app.php
'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,

【3】HttpKernelインスタンス作成

/public/index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

ここではサービスコンテナのmake関数を利用しHttpKernelクラスをインスタンス化しています。

HttpKernelクラスはイベントを使って処理を実行しています。

発行する主なイベントは以下の通りです。

イベント名タイミング目的
kernel.requestリクエストを受け取った時リクエストオブジェクトに追加情報をつける(ルーター情報の取得等)
kernel.controllerコントローラ実行前コントローラーの実行に必要な初期処理、コントローラーの実行を行う
kernel.viewコントローラーからのレスポンス受け取り時レスポンスがレスポンスオブジェクトではない場合、結果からレスポンスオブジェクトを生成する
kernel.responseレスポンスを返す直前結果からレスポンスオブジェクトを生成する
kernel.terminatingレスポンスを返した後レスポンス出力後に必要な処理を実行する
kernel.exception例外が発生した時例外発生後の処理とレスポンスオブジェクトの作成

【4】Requestインスタンス作成

/public/index.php
$request = Illuminate\Http\Request::capture()

Requestクラスのcapture関数を呼び出しています。

/vendor/laravel/framework/src/Illuminate/Http/Request.php
public static function capture()
{
static::enableHttpMethodParameterOverride();

return static::createFromBase(SymfonyRequest::createFromGlobals());
}

capture関数はSymfony2のRequestクラスのcreateFromGlobals()を呼び出しています。

/vendor/symfony/http-foundation/Request.php
public static function createFromGlobals()
{
$request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);

return $request;
}

ここでPHPのグローバル変数である$_GET $_POST $_COOKIE $_FILES $_SERVERが出てきました。

これらをcreateRequestFromFactory関数の引数に渡しています。

/vendor/symfony/http-foundation/Request.php
private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null): self
{
return new static($query, $request, $attributes, $cookies, $files, $server, $content);
}

createRequestFromFactory関数でようやくRequestクラスのインスタンス化され返却されました。

※この時点ではまだSymfonyのRequestクラスです。

返却されたSymfonyのRequestクラスを先ほどのcreateFromBase関数に渡すことでLaravelのRequestクラスに変換されます。

return static::createFromBase(SymfonyRequest::createFromGlobals());

【5】Response取得

/public/index.php
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
)->send();

ここで先ほどインスタンス化したHttpKernelクラスのhandle関数にRequestを渡してResponseを受け取っています。 handle()の中を見てみます。

/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
public function handle($request)
{
$response = $this->sendRequestThroughRouter($request);

return $response;
}

さらに$this->sendRequestThroughRouter()の中を見てみます。

/vendor/laravel/framework/src/Illuminate/Routing/Router.php
protected function sendRequestThroughRouter($request)
{
$this->bootstrap();

return (new Pipeline($this->app))
->send($request)
->through($this->middleware)
->then($this->dispatchToRouter());
}

Pipelineクラスの仕様解説は割愛しますが、

->send($request)(Requestインスタンスを渡して)

->through($this->middleware)(ミドルウェア3を通して)

->then($this->dispatchToRouter());(ルーターの処理を実行)

という処理になっています。

/vendor/laravel/framework/src/Illuminate/Routing/Route.php
public function dispatchToRoute(Request $request)
{
return $this->runRoute($request, $this->findRoute($request));
}

/routes/web.phpに定義したルーティングリストの中から今回のリクエストに合致するものを取得してrunRoute()に渡して処理を続行します。

途中の処理は省略しますが、最終的にコントローラーの処理にたどり着きます。

/vendor/laravel/framework/src/Illuminate/Routing/Route.php
protected function runController()
{
return $this->controllerDispatcher()->dispatch(
$this, $this->getController(), $this->getControllerMethod()
);
}

$this->getController()の中を見てみます。

/vendor/laravel/framework/src/Illuminate/Routing/Router.php
public function getController()
{
$class = $this->parseControllerCallback()[0]; // 「@」で分割してコントローラクラス名を取得
$this->controller = $this->container->make(ltrim($class, '\\')); // サービスコンテナのmake()でインスタンス化

return $this->controller; // コントローラーインスタンスを返却
}

やっていることは/routes/web.phpに書かれていたコントローラークラス名@アクションメソッド名

例:UserController@index

の文字列を取得して@で文字列分割し、コントローラークラス名でコントローラーをインスタンス化しています。

$this->getControllerMethod()の中ではメソッド(UserController@indexのindexにあたる)のメソッド名の文字列を取得しています。

return $this->controllerDispatcher()->dispatch(
$this, $this->getController(), $this->getControllerMethod()
);

先ほどのrunController()を見ると取得したコントローラークラスとメソッド名をdispatch()に渡しています。

更にdispatch()の中を見てみます。

/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php
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));
}

ここでようやくコントローラのアクションメソッドが実行されます。

【6】Response送信

/public/index.php
$response->send();

ここで実際にレスポンスを送信します。

$response->send()の中を見てみます。

/vendor/laravel/framework/src/Illuminate/Http/Response.php
public function send()
{
$this->sendHeaders();

$this->sendContent();
}

sendHeaders()ではHttpレスポンスヘッダーを送信し、 sendContent()ではレスポンス内容(素のPHPのheader()メソッドを使って、Content-TypeやCookie、HTTPステータスなどのHTTPヘッダー情報)を送信しています。 これで実際にHttpレスポンスを送信完了しました。

【7】terminate()

/public/index.php
$kernel->terminate($request, $response);

ミドルウェアにterminateメソッドを定義している場合、レスポンスがブラウザに送信された後にこのterminateメソッドが呼び出され実行されます。

以上がLaravelのライフサイクルの流れになります。

Footnotes

  1. オートロード機能を使用するにはcomposerを使います、composerを使用してライブラリをインストールすると/vendor/autoload.phpというファイルが生成されます。

  2. Laravelは、Symfonyという別のPHPフレームワークをベースに作られています。

  3. webアプリは「HTTPリクエストをコントローラに渡し、必要な処理を行ってレスポンスを返す」というのが基本のフローです。Laravelにおいてはリクエストの前後に挟まる処理をmiddlewareとして実装します。