Laravelのリクエスト開始からコントローラにたどり着くまで

# Laravelの 0 -> 1 を理解してみよう☆のコーナー

社内勉強会用に昔書いたやつをコピペっておく。
L55くらいの時代に書いたやつ。

## STEP 1 Web request has come

ブラウザ -> なんやかんや -> ELB -> EC2 -> Nginx -> php-fpm ここまではみんな一億%くらい挙動を理解してると思うのでスキップ

何はともあれ `public/index.php` が必ず最初に呼び出されます
https://github.com/laravel/laravel/blob/v5.5.28/public/index.php

## STEP 2 Composer autoloader

`require __DIR__.'/../vendor/autoload.php';`
https://github.com/laravel/laravel/blob/v5.5.28/public/index.php#L24

Composerのautoloaderが呼び出されます

`composer.json` のオートロード設定はこんな感じ

```
"autoload": {
    "classmap": [
        "database"
    ],
    "psr-4": {
        "App\\": "app/"
    },
    "files": [
        "app/helpers.php"
    ]
},
```

ちなみに autoload の登場以前のPHPは何をしていたかというと、 `require_once __DIR__ . '/app/App.php'` みたいなのを毎回かいてたよ。ヤバイよね。

### autoload classmap database

`classmap` で定義されているのはディレクトリパスで、あとは何かいい感じにしてくれるよ。

`vendor/composer/autoload_classmap.php` が生成されて、その中身みるとこんな感じ。

```php
return array(
    'AddXxx' => $baseDir . '/database/migrations/2015_12_29_113041_add_xxx.php',
    'AddYyy' => $baseDir . '/database/migrations/2016_11_11_211153_add_yyy.php',
```

`database` ディレクトリ以下にあるPHPファイルをパースして、クラス名とファイルパスのマッピングを作ってくれるんだね！
だからマイグレーションファイルのクラス名が重複するとこの辺で死ぬよ！

### autoload psr-4 App

[PSR-4](https://www.php-fig.org/psr/psr-4/) で定義されたルールにのっとってロードしてくれるよ！

`App\\` から始まるネームスペースのクラスは `app/` ディレクトリをルートとしているよ！って感じ

`App\Hoge\Fuga` をロードしようとすると `app/Hoge/Fuga.php` があるはず・・・！という気持ちでオートロードしてくれるよ。

### autoload files

`app/helpers.php` を必ず事前にロードしてくれるよ。
他のは「このクラス名がきたらこのファイルをrequire_onceする」っていう遅延ロードだけど、これは同期ロードだよ。すごいね。
だからどこにも `require_once __DIR__ . '/app/helpers.php';` なんて書いてないのヘルパー関数が動くなだね。すごいね！！


## STEP 3: bootstrap

`$app = require_once __DIR__.'/../bootstrap/app.php';`
https://github.com/laravel/laravel/blob/v5.5.28/public/index.php#L38

ついにLaravelが作られるね・・・！
Bootstrapファイルを読み込むよ。

### Create Application

```php
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);
```
https://github.com/laravel/laravel/blob/v5.5.28/bootstrap/app.php

これがLaravelの全ての起点だよ！！！

https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Application.php

```php
public function __construct($basePath = null)
{
    if ($basePath) {
        $this->setBasePath($basePath);
    }
    $this->registerBaseBindings();
    $this->registerBaseServiceProviders();
    $this->registerCoreContainerAliases();
}

protected function registerBaseBindings()
{
    static::setInstance($this);
    
    $this->instance('app', $this);
    $this->instance(Container::class, $this);
    $this->instance(PackageManifest::class, new PackageManifest(
        new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
    ));
}

protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));
    $this->register(new LogServiceProvider($this));
    $this->register(new RoutingServiceProvider($this));
}

public function registerCoreContainerAliases()
{
    foreach ([
        'app'                  => [\Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class,  \Psr\Container\ContainerInterface::class],
        'auth'                 => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
        'auth.driver'          => [\Illuminate\Contracts\Auth\Guard::class],
        'blade.compiler'       => [\Illuminate\View\Compilers\BladeCompiler::class],
        'cache'                => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
//  略
        'view'                 => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],
    ] as $key => $aliases) {
        foreach ($aliases as $alias) {
            $this->alias($key, $alias);
        }
    }
}
```
https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Application.php#L138-L149

割と大事なのが `class Application extends Container implements ApplicationContract, HttpKernelInterface` のとこで、
`Application` は `Container` を継承しているやね

つまり、 `Application` 自信がコンテナとして動くので、 `app(XxxClass::class)` みたいにしたときに、 `Application` がいろんな解決をすることができるんだね。すごいね。

細いことはいろいろあるけど、基本的にはこの段階で「Laravelの起動に最低限必要なクラス群を読み込めるようにする準備」として「Application自身にいろんなものを設定する」ことをしているよ。

https://github.com/laravel/laravel/blob/v5.5.28/bootstrap/app.php#L29-L37

その他↑この辺はきっと何か似たような登録処理をしているイメージ。
このファイルはフレームワーク側じゃないので、何かいろいろ初期化処理したかったらここに書くのはワンチャンあるよ。
ほぼほぼほぼほぼのケースでは、ちゃんと書くべき場所が別にあるからレアケース。


```php
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);
```

ここは比較的重要で、「WEBリクエスト用のKernel」と「Console（Workerとかコマンドとか）用のKernel」がそれぞれ別にあるんだね

### Step4: Create Kernel

`$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);`
https://github.com/laravel/laravel/blob/v5.5.28/public/index.php#L52

ここでは `bootstrap/app.php` で

```php
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
```

されているので `App\Http\Kernel.php` が呼び出される。 `

```php
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);
    }
}
 ```
https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Http/Kernel.php#L89-L103

HTTPのKernelにおいては、 Middlewareの登録処理が行われてることがわかりますね

どういうMiddlewareが登録されているかはApplication側で定義されている感じ

https://github.com/laravel/laravel/blob/v5.5.28/app/Http/Kernel.php

## Handle Request

```php
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
```
https://github.com/laravel/laravel/blob/v5.5.28/public/index.php#L54-L56

`Illuminate\Http\Request::capture()` の中身をみると、
`$_GET` とか `$_POST` とかのリクエスト系の処理をもとにSymfony\Requestを作られて、それをラップしたIlluminate\Http\Requestを作る感じ！

そうやって作った `Request` オブジェクトを　`$kernel->handle()` する感じ。

### WHAT IS KERNEL HANDLE



```php
    /**
     * Handle an incoming HTTP request.
     *
     * @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;
    }
```
https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Http/Kernel.php#L111-L132

肝は　`$response = $this->sendRequestThroughRouter($request);`

中身をみると

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

大事なのは

```php
$this->bootstrap();
```

と

```php
return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
```

### bootstrap

```php
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される

bootstrap処理の実行コードは

```php
foreach ($bootstrappers as $bootstrapper) {
    $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

    $this->make($bootstrapper)->bootstrap($this);

    $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
```

みたいな感じ。イベント周りはちょいちょいいあるけど、基本的には各Bootstrapクラスの　`public function bootstrap()` が実行される

雑な説明すると

* \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class
    * envの読み込みとか〜
* \Illuminate\Foundation\Bootstrap\LoadConfiguration::class
    * configの読み込みとか〜
* \Illuminate\Foundation\Bootstrap\HandleExceptions::class
    * エラーハンドリング
    * https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php#L28-L43
* \Illuminate\Foundation\Bootstrap\RegisterFacades::class
    * Facade周り〜
* \Illuminate\Foundation\Bootstrap\RegisterProviders::class
    * ServiceProvider読み込み〜
* \Illuminate\Foundation\Bootstrap\BootProviders::class
    * AppのServciceProviderまわり〜〜

### Pipeline

```php
return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
```

`Pipeline` 自体はLaravelでいろんなところで使われてるUtilityみたいなやつ。
登録しておいた処理を順番にやってくれるサムシングってだけ。tapとかそういうやつに近い雰囲気（適当）

`dispatchToRoute` が何してるかっつーと、雑にいえば「マッチするControllerのアクション見つけてそれ実行する」感じ。

つまり、「PipelineをMiddleware通しつつルートが一致するControllerのMethodを実行する」ことをやってる。

めっちゃシンプルで、これがすべて。

## まとめ

意外と超シンプルなので読みやすい。

## 余談 Worker

https://github.com/laravel/framework/blob/5.7/src/Illuminate/Queue/Console/WorkCommand.php

↑をおっかければいいよ。これがエントリーファイル

大事なのは

https://github.com/laravel/framework/blob/5.7/src/Illuminate/Queue/Worker.php#L85-L128

このへん