Masayan tech blog.

  1. ブログ記事一覧>
  2. 【重要】Laravelのライフサイクルをざっくりと解説してみる

【重要】Laravelのライフサイクルをざっくりと解説してみる

公開日

前置き

Laravelはとても便利で、色々な複雑な処理を処理をいい感じに行ってくれる分、内部処理をあまり理解できていなかったので、今回はLaravelのライフサイクルについて紹介できればと思います。
公式ドキュメントにも記載がありますので、Laravelを使用してアプリケーション開発される方(特に駆け出しの方は参考にされると良いのではないかと思います。)

全体の流れ

1.ブラウザからリクエスト

2.webサーバーがpublic/index.phpにリダイレクト

3.index.phpでプロジェクト初期化処理等実施後、レスポンスを返す

4.リクエスト結果に応じたレスポンス内容がブラウザに返却され、画面表示

処理内容

1.ブラウザからリクエスト

ブラウザからWebサーバーへHttpリクエストを実行( = URLを入力する)

2.webサーバーがpublic/index.phpにリダイレクト

Laravelアプリケーションへのすべてのリクエストのエントリポイントはpublic/index.phpファイルです。すべてのリクエストは、Webサーバ(Apache・Nginx等)によってこのファイルに送信されます

<Apacheの場合>
public/.htaccess

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

<Nginxの場合>
default.conf

location / {
    try_files $uri $uri/ /index.php$is_args$args;
}

3.index.phpでプロジェクト初期化等実施後、レスポンスを返す

大きく以下①〜⑦の処理が実行されます。

順番に見ていきます。

require __DIR__.'/../vendor/autoload.php'; // 【①】autoloadの読み込み

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

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

$response = $kernel->handle( // 【⑤】HttpKernelがリクエストを処理してResponse取得
    $request = Illuminate\Http\Request::capture() // 【④】Requestインスタンス作成
);

$response->send();  // 【⑥】レスポンス送信

$kernel->terminate($request, $response); // 【⑦】Middlewareのterminateを実行

【①】autoloadの読み込み

require __DIR__.'/../vendor/autoload.php'; // 【①】autoloadの読み込み

composerによるオートロードの仕組みを利用し、ファイル「vendor/autoload.php」を一度だけrequireすることで、vendor配下のライブラリをすべて自動的にロードできるようになります( = composer dump-autoload で生成されたオートローダー(外部にあるPHPファイルを自動的に読み込む仕組み)を読み込む)

PHPでは別ファイルのクラスを読み込む際はrequireを使ってファイルを読み込む必要がありますが、
このautoloadを最初に読み込むことで毎回requireでファイルを読み込むことなく別ファイルのクラスを利用できるようになります。

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

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

次に、bootstrapフォルダのapp.phpが読み込まれることにより、下記のように/bootstrap/app.phpにてApplicationクラス(Illuminate\Foundation\Application)がインスタンス化されます。(通称「サービスコンテナ」と呼ばれているもの)
※サービスコンテナ = クラス(サービス)のインスタンス化を管理する仕組み

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

app.phpのIlluminate\Foundation\Application.phpを追うと、下記のようにregisterConfiguredProvidersメソッドがあり、/config/app.phpからprovidersのリストを呼び出しています

Laravelはこのプロバイダのリストを繰り返し処理し、それぞれをインスタンス化します。プロバイダをインスタンス化した後、すべてのプロバイダのregisterメソッドを呼び出します。次に、すべてのプロバイダを登録し、各プロバイダでbootメソッドを呼び出します。

public function registerConfiguredProviders()
{
  $providers = Collection::make($this->config['app.providers'])
  ->partition(function ($provider) {
    return strpos($provider, 'Illuminate\\') === 0;
  });

  $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

  (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
  ->load($providers->collapse()->toArray());
}

<サービスプロバイダー> ※色々と書くと長くなりそうなので、本記事最下部にもう少し具体的な説明を書いています

サービスプロバイダは、データベース、キュー、バリデーション、ルーティングコンポーネントなど、フレームワークのさまざまなコンポーネントすべてを初期起動する責務を持っています。Laravelが提供する基本的なすべての主機能は、サービスプロバイダによって初期起動および設定されます

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

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

$app->make()メソッドで、サービスコンテナを使用して、Kernelクラスをnewしてインスタンス化しています。(kernelは、以降で行われるHttp処理全体の核になる部分のイメージ)です。

【④】Requestインスタンス作成し、⑤HttpKernelがリクエストを処理してResponse取得

$response = $kernel->handle( // 【⑤】HttpKernelがリクエストを処理    
  $request = Illuminate\Http\Request::capture() // 【④】Requestインスタンス作成
);

作成された$requestをHttpカーネル($kernel->handle()の引数として)に渡し、$responseにリクエストに対するレスポンス内容が返却されます。④のさらに深部の処理内容が気になる方は、Request::capture()をたどってみてください。

⑤にて$kernel->handleにより、いくつかの内部処理(ミドルウェア・ルーティング処理等)が実行されたのち、コントローラのアクションメソッド(「UserController@index」など)が実行され、その内容に応じたresponseが格納されます。

【⑥】レスポンス送信

$response->send();  // 【⑥】レスポンス送信

sendメソッドは/vendor/symfony/http-foundation/Response.phpに定義されています。
sendHeaders()ではHttpレスポンスヘッダーを送信し、
sendContent()ではレスポンス内容(HTMLなど)を送信しています。
つまり、この部分で、返却するhttpレスポンス(最終的なHTML)の内容を生成しているわけです。

public function send()
{
  $this->sendHeaders();
  $this->sendContent();
  if (\function_exists('fastcgi_finish_request')) {
    fastcgi_finish_request();
  } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
    static::closeOutputBuffers(0, true);
  }
  return $this;
}

【⑦】terminate

$kernel->terminate($request, $response); // 【⑦】Middlewareのterminateを実行

最後に、各ミドルウェアに定義されているterminateメソッドの処理を実行しています。
ミドルウェアのterminateメソッドは以下の通りです

公式ドキュメントより引用

ミドルウェアは場合により、HTTPレスポンスが準備できた後に、何か作業する必要が起きます。たとえば、"session"ミドルウェアには、レスポンスの準備が完了したあとで、ストレージにセッションデータを書き込む処理が含まれています。ミドルウェアにterminateメソッドを定義すると、レスポンスがブラウザへ送られる準備ができた後に自動的に呼び出されます。

【補足】サービスプロバイダ

アプリケーションのすべてのサービスプロバイダは、config/app.php構成ファイルのproviders配列で構成されます。

'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,
  Illuminate\Encryption\EncryptionServiceProvider::class,
  Illuminate\Filesystem\FilesystemServiceProvider::class,
  省略
],

Laravelはこのプロバイダのリストを繰り返し処理し、それぞれをインスタンス化します。プロバイダをインスタンス化した後、すべてのプロバイダのregister(登録)メソッドを呼び出します。次に、すべてのプロバイダを登録し、各プロバイダでboot(起動)メソッドを呼び出します。

サービスプロバイダは、データベース、キュー、バリデーション、ルーティングコンポーネントなど、フレームワークのさまざまなコンポーネントすべてを初期起動する責務を持っています。Laravelが提供する基本的なすべての主機能は、サービスプロバイダによって初期起動および設定されます。

サービスプロバイダが登録後、ミドルウェアは、アプリケーションに入るHTTPリクエストをフィルタリングまたは検査してアプリケーションのユーザーが認証されているかを確認するミドルウェアが含まれています。ユーザーが認証されていない場合、このミドルウェアはユーザーをログイン画面にリダイレクトします。そしてユーザーが認証されている場合、ミドルウェアはリクエストをアプリケーションへ進めることを許可します

HTTPカーネルの$middlewareプロパティで定義されているもののように、アプリケーション内のすべてのルートで適用されるミドルウェアもあれば、特定のルートまたはルートグループにのみ割り当てられるミドルウェアもあります。

リクエストが一致したルートへ割り当てられたすべてのミドルウェアをパスした場合は、レスポンスはルートのミドルウェアを介して外側に戻り、アプリケーションに送信レスポンスを変更または検査する機会を与えます。

まとめ

いかがでしたでしょうか。Laravelはとても便利で、色々な複雑な処理を処理をいい感じに行ってくれる分、内部処理をあまり理解できていなかったので、今回はLaravelのライフサイクルについて紹介できればと思います。

公式ドキュメントにも記載がありますので、Laravelを使用してアプリケーション開発される方(特に駆け出しの方は参考にされると良いのではないかと思います。)