魔法のようなLaravelも素のPHPに始まり素のPHPに終わる。Laravelのライフサイクルを簡単に解説。

# はじめに

![Untitled Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297693/a570d4b1-3c35-6284-5ed9-cf8f6e68d564.png)


普段Laravelでアプリケーションを実装するときは
コントローラでリクエストを受け取り、
コントローラでレスポンスを返却するまでの範囲しか
あまり意識していませんでした。

ですが、実際には
WEBサーバがHTTPリクエストを受け取ってから
コントローラに処理が渡されるまでの間や、
コントローラからレスポンスを返却してから
WEBサーバがHTTPレスポンスを送信するまでの間
にはいろいろと処理が行われているはずです。

・`/public`ディレクトリに実際のHTMLファイルはないけどなぜルーティング処理されるんだ？
・どこの誰がどうやって処理対象のコントローラを特定してくれているんだ？
・あの便利なRequestインスタンスはいつ誰が作って渡してくれているんだ？
などなど
普段意識していなかった
**コントローラより外の世界** が気になり、
Laravelのコードを追いかけてみましたので解説したいと思います。

今回はLaravelのソースコードの細かい分岐処理などは全て省略し、
メインフローのみピックアップして解説していくので
難しい複雑なコードはあまり登場しません。

Laravel6.2を元に解説を進めますが、
ほかのバージョンでも大した差はないと思います。

# 全体の流れ
全体の流れとしてはこのようになっています。

* WEBサーバがindex.phpにリダイレクト
* index.phpの中
 * autoload読み込み
 * Applicationインスタンス作成
 * HttpKernelインスタンス作成
 * Requestインスタンス作成
 * HttpKernelがリクエストを処理してResponse取得
 * レスポンス送信
 * terminate()で後片付け

この１つ１つをもう少し詳しく解説していきます。

# WEBサーバがindex.phpにリダイレクト
Laravelのドキュメントルートは`/public/`です。

例えば`https://example.com/test/`のURLにリクエストされたとして
通常であれば
`/public/test/index.html`を取得することになります。

しかし、Laravelではすべての処理の始まりが`/public/index.php`のファイルになるので、
WEBサーバの設定で
どのURLに対するリクエストでも、
全て`/public/index.php`にリダイレクトするようになっています。[^1]
[^1]: 実際は、CSSやJSなどpublicディレクトリ配下に実際に存在するファイルの場合はリダイレクトされない

　
WEBサーバがApacheの場合は.htaccessファイルに
この様にリダイレクト処理を書きます。

```apache:.htaccess
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
```
（この.htaccess設定はデフォルトで`/public/.htaccess`に配置されています）

WEBサーバがNginxの場合はconfファイルに
この様にリダイレクト処理を書きます。

```nginx:default.conf
location / {
    try_files $uri $uri/ /index.php$is_args$args;
}
```

これらのWEBサーバ設定によって、
まずは全ての入り口となる`/public/index.php`に入っていきます。

# index.phpの中
実際のファイルがこちら。

```php:/public/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); // 【７】terminate()で後片付け
```

このindex.phpファイルが、Laravelの処理全体フローそのものになります。
【１】～【７】までをざっくりと追いかけていきます。

その中でも
> 【５】HttpKernelがリクエストを処理してResponse取得

は一番のメイン処理で、
僕らの知るコントローラにたどり着くまで
コードを深く深く見ていくことになります。



## 【１】autoload読み込み
まず初めに

```php:/public/index.php
require __DIR__.'/../vendor/autoload.php'; // 【１】autoload読み込み
```

でautoloadファイルを読み込みます。

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

autoloadの詳しい仕様については割愛します。
参考：https://laraweb.net/surrounding/1642/


## 【２】Applicationインスタンス作成
次に、

```php:/public/index.php
$app = require_once __DIR__.'/../bootstrap/app.php'; // 【２】Applicationインスタンス作成
```

ここでApplicationインスタンスを作成しています。
`/bootstrap/app.php`の中も見てみます。

```php:/bootstrap/app.php（重要箇所のみ抜粋）
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

return $app;
```

`Illuminate\Foundation\Application`クラスをインスタンス化して返却しています。
このApplicationインスタンスが、通称「サービスコンテナ」と呼ばれるものです。

このサービスコンテナについては
こちらの記事で解説しているので
興味のある方はご覧ください。
[【Laravel】サービスコンテナとは？２つの強力な武器を持ったインスタンス化マシーン。簡単に解説。](https://qiita.com/minato-naka/items/afa4b930a2afac23261b)


全体処理の流れをざっくり見る
という目的なので、今は気にせず次に進んでいいかと思います。


## 【３】HttpKernelインスタンス作成
次に、

```php:/public/index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); // 【３】HttpKernelインスタンス作成
```

でHttpKernelクラスをインスタンス化しています。

この`$app->make()`メソッドは、
単純にKernelクラスをnewしているだけと思って大丈夫です。
※詳しく知りたい方はこちらの記事でサービスコンテナについて解説しています
[【Laravel】サービスコンテナとは？２つの強力な武器を持ったインスタンス化マシーン。簡単に解説。](https://qiita.com/minato-naka/items/afa4b930a2afac23261b)


`kernel`とは「核」とか「核心」という意味で、
ソフトウェアの世界では
「司令塔となる中心の機能」
というイメージで僕はとらえています。

今回のHttpKernelの場合は
「Http処理全体の司令塔となるクラス」というイメージ。


## 【４】Requestインスタンス作成
次に、

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

この`$kernel->handle()`の引数として渡されている

```php:/public/index.php
$request = Illuminate\Http\Request::capture() // 【４】Requestインスタンス作成
```

ここの処理でRequestインスタンスを作成しています。

この`capture()`の中を見てみます。

```php:/vendor/laravel/framework/src/Illuminate/Http/Request.php（重要箇所のみ抜粋）
public static function capture()
{
    return static::createFromBase(SymfonyRequest::createFromGlobals());
}
```

さらに`SymfonyRequest::createFromGlobals()`の中を見てみます。
※ここから先はSymfonyのコードになります[^2]
[^2]: Laravelは、Symfonyという別のPHPフレームワークをベースに作られており、コードを深くたどるとSymfonyになっています 
　

```php:/vendor/symfony/http-foundation/Request.php（重要箇所のみ抜粋）
public static function createFromGlobals()
{
    $request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);

    return $request;
}
```


ここで、PHPのスーパーグローバル変数である
`$_GET`、`$_POST`、`$_COOKIE`、`$_SERVER`
などを使っていますね。

素のPHPの機能で、
GET・POSTリクエスト内容や受け取ったCookie、リクエストのURLやHTTPメソッドなど
様々なHTTPリクエストの情報を取得することができます。
https://www.php.net/manual/ja/reserved.variables.server.php

　
　
さらに、
そのPHPのスーパーグローバル変数で取得した
リクエスト情報を渡している`self::createRequestFromFactory`の中を見てみます。

```php:/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);
    }
```

受け取った様々なリクエスト情報を引数に渡して、
ここでやっとRequestクラスをインスタンス化しました。
（この時点ではLaravelのRequestクラスではなくSymfonyのRequestクラスです）

　

そして、そのSymfonyRequestインスタンスを返却しています。


このSymfonyのRequestインスタンスが返却される途中、
先ほど見たこの処理の`static::createFromBase`で
LaravelのRequestクラスインスタンスに変換されています。

```php:/vendor/laravel/framework/src/Illuminate/Http/Request.php（重要箇所のみ抜粋）
public static function capture()
{
    return static::createFromBase(SymfonyRequest::createFromGlobals());
}
```
　
　
これでようやく
この`$request`にRequestクラスのインスタンスを入れるところまで完了しました。

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

## 【５】HttpKernelがリクエストを処理してResponse取得
次は

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

ここで`HttpKernel`の`handle()`メソッドにRequestを渡して、
Responseを受け取っています。

この`handle()`の中を見てみます。

```php:/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php（重要箇所のみ抜粋）
public function handle($request)
{
    $response = $this->sendRequestThroughRouter($request);

    return $response;
}
```

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

```php:/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php（重要箇所のみ抜粋）
    protected function sendRequestThroughRouter($request)
    {
        $this->bootstrap();

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

`$this->bootstrap();`
では実際のリクエストの処理の前に、もろもろ設定の読み込みなどを行っています。
具体的には、.envの環境変数の読み込みや、
各サービスプロバイダの実行などです。
（メイン処理の流れとは外れるため詳細は割愛）


こちらがメインのリクエスト処理フローです。

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

このPipelineクラスの仕様解説は割愛しますが、
見たまんま、
`->send($request)`（Requestインスタンスを渡して）
`->through($this->middleware)`（ミドルウェアを通して）
`->then($this->dispatchToRouter());`（ルーターの処理を実行）
という処理です。

この
>`->through($this->middleware)`（ミドルウェアを通して）

ではグローバルミドルウェアの処理が実行されます。

そしてメイン処理の続きは
`->then($this->dispatchToRouter());`（ルーターの処理を実行）
ですね。

この`dispatchToRouter()`の先では、
・対象のルーティングを取得する
・そのルーティングに紐づいたコントローラとコントローラアクションメソッドを判定する
・そのコントローラアクションメソッドを実行する
・コントローラから返されたレスポンスをResponseクラスのインスタンスに変換する
・Responseインスタンスを返却する
という感じで処理が進みます。

この先はさらにコードの深いところに入っていくので、
現時点で疲れていれば上記の概要だけ把握して
次の「【６】レスポンス送信」まで飛ばしちゃってもいいと思います。
　
　
では`dispatchToRouter()`の先を見ていきます。

```php:/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php（重要箇所のみ抜粋）
protected function dispatchToRouter()
{
    return $this->router->dispatch($request);
}
```

HttpKernelクラスから、Routerクラスに処理が渡されました。

さらに`dispatch()`を見ていきます。

```php:/vendor/laravel/framework/src/Illuminate/Routing/Router.php（重要箇所のみ抜粋）
public function dispatch(Request $request)
{
    return $this->dispatchToRoute($request);
}
```

さらに`dispatchToRoute()`の中。

```php:/vendor/laravel/framework/src/Illuminate/Routing/Router.php（重要箇所のみ抜粋）
public function dispatchToRoute(Request $request)
{
    return $this->runRoute($request, $this->findRoute($request));
}
```

`$this->findRoute($request)`
で、`/routes/web.php`に定義したルーティングリストの中から
今回のリクエストに合致するものを取得してきます。

そしてその特定したルートを渡して
`runRoute()`で処理を続行。

```php:/vendor/laravel/framework/src/Illuminate/Routing/Router.php（重要箇所のみ抜粋）
protected function runRoute(Request $request, Route $route)
{
    return $this->prepareResponse($request,
        $this->runRouteWithinStack($route, $request)
    );
}
```

さらに`runRouteWithinStack()`の中。

```php:/vendor/laravel/framework/src/Illuminate/Routing/Router.php（重要箇所のみ抜粋）
protected function runRouteWithinStack(Route $route, Request $request)
{
    return (new Pipeline($this->container))
                    ->send($request)
                    ->through($middleware)
                    ->then($route->run());
}
```

先ほどの見覚えのある処理が出てきました。

特定したルート（$route）をもとに、
`->send($request)`（Requestインスタンスを渡して）
`->through($middleware)`（ミドルウェアを通して）
`->then($route->run())`（ルートの処理を実行）
となっています。

先ほどはグローバルミドルウェアの処理が実行されていましたが、
今回の
`->through($middleware)`では
ルートミドルウェアを実行しています。

そして、
`$route->run()`でRouterクラスからさらにRouteクラスに処理が渡されました。

`$route->run()`の中。

```php:/vendor/laravel/framework/src/Illuminate/Routing/Route.php（重要箇所のみ抜粋）
public function run()
{
    return $this->runController();
}
```

やっとコントローラにたどり着けそうな感じがしてきました。

`runController()`の中。

```php:/vendor/laravel/framework/src/Illuminate/Routing/Route.php（重要箇所のみ抜粋）
protected function runController()
{
    return $this->controllerDispatcher()->dispatch(
        $this, $this->getController(), $this->getControllerMethod()
    );
}
```

`$this->getController()`では、
`/routes/web.php`に書かれていた
`{コントローラクラス名}@{アクションメソッド名}`
の文字列を取得して@で文字列分割し、
`{コントローラクラス名}`でコントローラをインスタンス化しています。
（「UserController@index」とかっていつも書くやつですね）

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

    return $this->controller; // コントローラインスタンスを返却
}
```
「UserController@index」の例では、UserControllerクラスがインスタンス化されることになります。[^3]
[^3]: このmake()で依存解決が行われます。依存解決とは、コンストラクタの引数で型宣言されているクラスをインスタンス化して渡してくれること（詳細は[こちら](https://qiita.com/minato-naka/items/a4531797af611688db97)の記事で解説）
　　
　
`$this->getControllerMethod()`の方では、
同じように
`{コントローラクラス名}@{アクションメソッド名}`を「@」で分割して
`{アクションメソッド名}`を文字列で返却しています。
「UserController@index」の例では、「index」の文字列が取得されることになります。
　
　
そして改めて`runController()`の中を見ると、

```php:/vendor/laravel/framework/src/Illuminate/Routing/Route.php（重要箇所のみ抜粋）
protected function runController()
{
    return $this->controllerDispatcher()->dispatch(
        $this, $this->getController(), $this->getControllerMethod()
    );
}
```

`$this->controllerDispatcher()->dispatch()`に
先ほどとってきた`コントローラクラス`と`アクションメソッド名`を渡しています。

`dispatch()`の中。

```php:/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php（重要箇所のみ抜粋）
public function dispatch(Route $route, $controller, $method)
{
    return $controller->{$method}(...array_values($parameters));
}
```

ここでついに・・・。
コントローラのアクションメソッドが実行されます。

「UserController@index」の例では、
`$controller`にはUserControllerのインスタンスが、
`$method`には「index」の文字列が入っていますので、
`$controller->index()`の形でちゃんとメソッドが実行されていますね。

長かったですが、
WEBサーバでリダイレクトされて`/public/index.php`に入ったところから、
僕らがいつも意識しているコントローラの中の処理に入るまでに
この様な処理をたどっているわけです。

コントローラから返却されたレスポンスは
今まで来た道を帰っていき、
だいぶ前にみた

```php:/vendor/laravel/framework/src/Illuminate/Routing/Router.php（重要箇所のみ抜粋）
protected function runRoute(Request $request, Route $route)
{
    return $this->prepareResponse($request,
        $this->runRouteWithinStack($route, $request)
    );
}
```

この`prepareResponse()`の中で、
LaravelのResponseインスタンスに変換されます。

そして最終的に最初の`/public/index.php`にある

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

ここまで帰って`$response`に格納されます。


## 【６】レスポンス送信
次に、

```php:/public/index.php
$response->send();  // 【６】レスポンス送信
```

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

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

```php:/vendor/symfony/http-foundation/Response.php（重要箇所のみ抜粋）
public function send()
{
    $this->sendHeaders();
    $this->sendContent();
}
```

見た通り、
`sendHeaders()`ではHttpレスポンスヘッダーを送信し、
`sendContent()`ではレスポンス内容（HTMLなど）を送信しています。

`sendHeaders()`の中。

```php:/vendor/symfony/http-foundation/Response.php（重要箇所のみ抜粋）
public function sendHeaders()
{
    // headers
    foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
        $replace = 0 === strcasecmp($name, 'Content-Type');
        foreach ($values as $value) {
            header($name.': '.$value, $replace, $this->statusCode);
        }
    }

    // cookies
    foreach ($this->headers->getCookies() as $cookie) {
        header('Set-Cookie: '.$cookie, false, $this->statusCode);
    }

    // status
    header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
}
```

いろいろと書いてありますが、
`header()`とある３か所だけ見てみるといいです。

素のPHPの`header()`メソッドを使って、
`Content-Type`や`Cookie`、`HTTPステータス`などの
HTTPヘッダー情報を送信していますね。

続いて`sendContent()`の中。

```php:/vendor/symfony/http-foundation/Response.php（重要箇所のみ抜粋）
public function sendContent()
{
    echo $this->content;
}
```

`content`をechoしています。
この`content`には表示する画面のHTMLがそのまま入っています。

これで実際にHttpレスポンスを送信完了しました。


## 【７】terminate()で後片付け
`/public/index.php`の最後の処理です。

```php:/public/index.php
$kernel->terminate($request, $response); // 【７】terminate()で後片付け
```

ここでは、各ミドルウェアクラスに定義された`terminate()`処理などを実行しているだけです。

これで、ついにLaravelの処理がすべて終了しました！

# おわりに
普段意識しない、
コントローラより前の世界、後の世界を見ていきました。

個人的には、
・まず最初に素のPHPの`$_GET`や`$_SEVER`などを使ってRequestクラスを作っている
・最後は素のPHPの`header()`と`echo()`でレスポンスを送信している
というところを実際に見れたのが面白かったです。

すごく便利で魔法のようなフレームワークの機能たちに囲まれて開発していますが、
やっぱり一番最初と最後は僕らの知っている素のPHPの機能を使って処理をしているんだなと・・・。
　
　
今回は実際のLaravelソースコードから
重要処理のみを抜粋してできる限り読み進めやすいようにしています。

実際にはもっとたくさん分岐処理などがありますので、
自分で実物のコードを追いかけてみることをお勧めします。


Laravelのちょっと深いところを理解していきたいと思っている方、
これらの記事もおすすめですので是非読んでみてください。
（次に進む前に、LGTMしてもらえるとうれしいです）
[魔法のようなLaravelも素のPHPに始まり素のPHPに終わる。Laravelのライフサイクルを簡単に解説。](https://qiita.com/minato-naka/items/f05b023950ce544a8bec)
[【Laravel】サービスコンテナとは？２つの強力な武器を持ったインスタンス化マシーン。簡単に解説。](https://qiita.com/minato-naka/items/afa4b930a2afac23261b)
[【Laravel】引数でタイプヒントしただけでインスタンスがもらえるのはなぜ？Laravelの魔法を解明してみる。](https://qiita.com/minato-naka/items/a4531797af611688db97)
[【Laravel】ファサードとは？何が便利か？どういう仕組みか？](https://qiita.com/minato-naka/items/095f2a1beec1d09f423e)


# 参考
[【Laravel】サービスコンテナとは？２つの強力な武器を持ったインスタンス化マシーン。簡単に解説。](https://qiita.com/minato-naka/items/afa4b930a2afac23261b)
[【Laravel】引数でタイプヒントしただけでインスタンスがもらえるのはなぜ？Laravelの魔法を解明してみる。](https://qiita.com/minato-naka/items/a4531797af611688db97)
https://readouble.com/laravel/6.x/ja/lifecycle.html
https://qiita.com/yamaji_daisuke/items/e661ffd63eefdfd178cc
https://speakerdeck.com/namizatork/laravelwakanne-kara-wan-quan-nili-jie-sita-madesutetupuatupu