Laravelを知らない中級（中年）プログラマーがマイグレーションファイルの仕組みを調べてみたけど全くわからない！その９「Kernel::handle()」

### INDEX
Laravelを知らない中級（中年）プログラマーがマイグレーションファイルの仕組みを調べてみたけど全くわからない！

- [その１「マイグレーションファイルを見てみよう」](https://qiita.com/xioncc/items/1dcec2fbe3485b7e769a)
- [その２「$app['db']って何者？」](https://qiita.com/xioncc/items/cd4a46e5aca16b13174d)
- [その３「Repositoryクラス」](https://qiita.com/xioncc/items/1ce2132594c4596a73ae)
- [その４「Larabelアプリケーションの初期化の流れ」](https://qiita.com/xioncc/items/a17d7f905d4a5abf8103)
- [その５「リポジトリの読込」](https://qiita.com/xioncc/items/dc7df63c2487b02052f3)
- [その６「データベース接続」](https://qiita.com/xioncc/items/3cf9336eecd5f6905831)
- [その７「スキーマビルダー」](https://qiita.com/xioncc/items/04c95ce9ff6d0b7c9d44)
- [その８「migrate コマンドの実行」](https://qiita.com/xioncc/items/4a5166a0963991e33eb5)
- [その９「Kernel::handle()」](https://qiita.com/xioncc/items/07c1f0ba9b40bd230608)


## Kernel::handle()
前回は``Kernel::handle()``がコールされた際に渡される引数

- ``Symfony\Component\Console\Input\ArgvInput``
- ``Symfony\Component\Console\Output\ConsoleOutput``

を軽く見てみました。
では、``handle()``メソッドを見てみましょう。

```php:Illuminate\Foundation\Console\Kernel&#x3a;&#x3a;handle()
    /**
     * Run the console application.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @param  \Symfony\Component\Console\Output\OutputInterface|null  $output
     * @return int
     */
    public function handle($input, $output = null)
    {
        try {
            $this->bootstrap();
            return $this->getArtisan()->run($input, $output);
        } catch (Throwable $e) {
            $this->reportException($e);
            $this->renderException($output, $e);
            return 1;
        }
    }
    /**
     * Get the Artisan application instance.
     *
     * @return \Illuminate\Console\Application
     */
    protected function getArtisan()
    {
        if (is_null($this->artisan)) {
            return $this->artisan = (new Artisan($this->app, $this->events, $this->app->version()))
                                ->resolveCommands($this->commands);
        }
        return $this->artisan;
    }
```

#### Illuminate\Foundation\Console\Kernel::handle()
> 第一引数は``Symfony\Component\Console\Input\ArgvInput``インスタンスです。
第二引数は``Symfony\Component\Console\Output\ConsoleOutput``インスタンスです。
戻り値は整数型です。

まず``bootstrap()``メソッドがコールされます。ここは以前読みましたのでもう理解できています。
次に``getArtisan()``メソッドがコールされます。
``getArtisan()``メソッドの戻り値は``Illuminate\Console\Application``インスタンスです。
``getArtisan()``メソッドは``$this->artisan``が``null``かどうか検証します。``null``でなければそれを返します。``null``ならば、アプリケーション、イベント、バージョンを渡し``Artisan``インスタンスを生成し、``resolveCommands()``を実行した戻り値を返します。
``Artisan``の実体は``Illuminate\Console\Application``です。見てみましょう。

```php:Illuminate\Console\Application&#x3a;&#x3a;__construct()
    /**
     * Create a new Artisan console application.
     *
     * @param  \Illuminate\Contracts\Container\Container  $laravel
     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
     * @param  string  $version
     * @return void
     */
    public function __construct(Container $laravel, Dispatcher $events, $version)
    {
        parent::__construct('Laravel Framework', $version);
        $this->laravel = $laravel;
        $this->events = $events;
        $this->setAutoExit(false);
        $this->setCatchExceptions(false);
        $this->events->dispatch(new ArtisanStarting($this));
        $this->bootstrap();
    }
    /**
     * Bootstrap the console application.
     *
     * @return void
     */
    protected function bootstrap()
    {
        foreach (static::$bootstrappers as $bootstrapper) {
            $bootstrapper($this);
        }
    }
```

```php:Symfony\Component\Console\Application&#x3a;&#x3a;__construct()|関連メソッド
    public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN')
    {
        $this->name = $name;
        $this->version = $version;
        $this->terminal = new Terminal();
        $this->defaultCommand = 'list';
    }
    /**
     * Sets whether to automatically exit after a command execution or not.
     */
    public function setAutoExit(bool $boolean)
    {
        $this->autoExit = $boolean;
    }
    /**
     * Sets whether to catch exceptions or not during commands execution.
     */
    public function setCatchExceptions(bool $boolean)
    {
        $this->catchExceptions = $boolean;
    }
```

#### Illuminate\Console\Application::__construct()
> 第一引数はアプリケーションコンテナです。
第二引数はイベントディスパッチャーです。
第三引数はストリング型でバージョン番号です。
戻り値はありません。

まず、スーパークラス``Symfony\Component\Console\Application``のコンストラクタをアプリケーション名「``Laravel Framework``」とバージョン情報を引数にコールします。
``Symfony\Component\Console\Application``のコンストラクタは受け取ったアプリケーション名とバージョン名を変数に代入し、``$this->terminal``に``Terminal``インスタンスを生成したものを代入し、デフォルトコマンドをセットします。``Terminal``にコンストラクタはありません。保持ファンクションの一覧を見てみましょう。

```php:Symfony\Component\Console\Terminal変数|ファンクション一覧
    private static $width;
    private static $height;
    private static $stty;
    public function getWidth()
    public function getHeight()
    private static function initDimensions()
    private static function hasVt100Support(): bool
    private static function initDimensionsUsingStty()
    private static function getConsoleMode(): ?array
    private static function getSttyColumns(): ?string
    private static function readFromProcess(string $command): ?string
```

#### Symfony\Component\Console\Terminal
縦幅横幅の取得、``Stty``が利用可能か、サイズの初期化、``VT100``をサポートしているか、``Stty``利用時のサイズ初期化、``mode CON``が使用可能か（コマンドプロンプトウィンドウの変更）。``Stty``の行数の取得、コマンドの実行、等のメソッドを実装しているようです。端末に関することを担うクラスのようです。

``Illuminate\Console\Application::__construct()``に戻ります。
アプリケーションコンテナとイベントディスパッチャーを変数に代入します。
``setAutoExit()``メソッドを``false``を渡しコールします。
``setAutoExit()``メソッドは``$this->autoExit``をセットします。
コマンド実行後に自動的に終了するかどうかを設定するパラメータのようです。
``setCatchExceptions()``メソッドを``false``を渡しコールします。
``setCatchExceptions()``メソッドは``$this->catchExceptions``をセットします。
コマンドの実行中に例外をキャッチするかどうかを設定するパラメータのようです。
次にイベントディスパッチャーに``ArtisanStarting``を引数に``$this``を渡して生成したものを登録します。
``ArtisanStarting``クラスは非常に小さな定義のクラスです。

```php:Illuminate\Console\Events\ArtisanStarting
<?php
namespace Illuminate\Console\Events;
class ArtisanStarting
{
    /**
     * The Artisan application instance.
     *
     * @var \Illuminate\Console\Application
     */
    public $artisan;
    /**
     * Create a new event instance.
     *
     * @param  \Illuminate\Console\Application  $artisan
     * @return void
     */
    public function __construct($artisan)
    {
        $this->artisan = $artisan;
    }
}
```

コンストラクタでコンソールアプリケーションを受け取り``$this->artisan``に代入します。
``Illuminate\Console\Application::__construct()``の続きです。
``bootstrap()``メソッドをコールしています。
``bootstrap()``メソッドは、``foreach``で``static::$bootstrappers``を回して``$bootstrapper()``を引数に自身を渡してコールしています。``static::$bootstrappers``は生成の過程で仕込まれている様子はありませんでした。おそらく``Kernel``が初期化される工程で準備されるのでしょう。

``Kernel``初期化の流れの中で``Application::registerConfiguredProviders()``がコールされます。そこでは``$this->config['app.providers']``つまり、``PROJECT_ROOT/config/app.php``で定義されている配列のキー``providers``つまりプロバイダーのリストを引数に``ProviderRepository::load()``をコールしていました。その処理の中で、アプリケーションコンテナの``register()``に引数としてプロバイダーを一つずつ渡し、そこでプロバダー自体の``register()``がコールされます。
``$this->config['app.providers']``の中には``Illuminate\Foundation\Providers\ConsoleSupportServiceProvider``が含まれます。
こちらを見てみましょう。

```php:Illuminate\Foundation\Providers\ConsoleSupportServiceProvider
<?php
namespace Illuminate\Foundation\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Database\MigrationServiceProvider;
use Illuminate\Support\AggregateServiceProvider;
class ConsoleSupportServiceProvider extends AggregateServiceProvider implements DeferrableProvider
{
    /**
     * The provider class names.
     *
     * @var array
     */
    protected $providers = [
        ArtisanServiceProvider::class,
        MigrationServiceProvider::class,
        ComposerServiceProvider::class,
    ];
}
```

コンストラクタも``register()``も見当たりません。
``$providers``が定義されているだけです。その中に``ArtisanServiceProvider``とそれっぽいクラスが記述されています。
``ConsoleSupportServiceProvider``クラスは``Illuminate\Support\AggregateServiceProvider``クラスを継承しています。こちらを見てみましょう。

```php:Illuminate\Support\AggregateServiceProviderL&#x3a;&#x3a;register()
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->instances = [];
        foreach ($this->providers as $provider) {
            $this->instances[] = $this->app->register($provider);
        }
    }
```

``register()``メソッドがありました。
``ConsoleSupportServiceProvider``でオーバーライドした``$providers``を``foreach``で回してアプリケーションコンテナの``register()``でプロバイダーを登録して``$this->instances[]``に入れいていく処理です。
ということは、``ArtisanServiceProvider``インスタンスがアプリケーションコンテナにプロバイダー登録され、自身の``register()``メソッドがコールされるはずです。
見てみましょう。

```php:Illuminate\Foundation\Providers\ArtisanServiceProvider&#x3a;&#x3a;register()|関連メソッド
    protected $commands = [
        'CacheClear' => 'command.cache.clear',
        'CacheForget' => 'command.cache.forget',
        'ClearCompiled' => 'command.clear-compiled',
        'ClearResets' => 'command.auth.resets.clear',
        'ConfigCache' => 'command.config.cache',
        'ConfigClear' => 'command.config.clear',
        'DbWipe' => 'command.db.wipe',
        'Down' => 'command.down',
        'Environment' => 'command.environment',
        'EventCache' => 'command.event.cache',
        'EventClear' => 'command.event.clear',
        'EventList' => 'command.event.list',
        'KeyGenerate' => 'command.key.generate',
        'Optimize' => 'command.optimize',
        'OptimizeClear' => 'command.optimize.clear',
        'PackageDiscover' => 'command.package.discover',
        'QueueFailed' => 'command.queue.failed',
        'QueueFlush' => 'command.queue.flush',
        'QueueForget' => 'command.queue.forget',
        'QueueListen' => 'command.queue.listen',
        'QueueRestart' => 'command.queue.restart',
        'QueueRetry' => 'command.queue.retry',
        'QueueWork' => 'command.queue.work',
        'RouteCache' => 'command.route.cache',
        'RouteClear' => 'command.route.clear',
        'RouteList' => 'command.route.list',
        'Seed' => 'command.seed',
        'ScheduleFinish' => ScheduleFinishCommand::class,
        'ScheduleRun' => ScheduleRunCommand::class,
        'StorageLink' => 'command.storage.link',
        'Up' => 'command.up',
        'ViewCache' => 'command.view.cache',
        'ViewClear' => 'command.view.clear',
    ];
    /**
     * The commands to be registered.
     *
     * @var array
     */
    protected $devCommands = [
        'CacheTable' => 'command.cache.table',
        'ChannelMake' => 'command.channel.make',
        'ComponentMake' => 'command.component.make',
        'ConsoleMake' => 'command.console.make',
        'ControllerMake' => 'command.controller.make',
        'EventGenerate' => 'command.event.generate',
        'EventMake' => 'command.event.make',
        'ExceptionMake' => 'command.exception.make',
        'FactoryMake' => 'command.factory.make',
        'JobMake' => 'command.job.make',
        'ListenerMake' => 'command.listener.make',
        'MailMake' => 'command.mail.make',
        'MiddlewareMake' => 'command.middleware.make',
        'ModelMake' => 'command.model.make',
        'NotificationMake' => 'command.notification.make',
        'NotificationTable' => 'command.notification.table',
        'ObserverMake' => 'command.observer.make',
        'PolicyMake' => 'command.policy.make',
        'ProviderMake' => 'command.provider.make',
        'QueueFailedTable' => 'command.queue.failed-table',
        'QueueTable' => 'command.queue.table',
        'RequestMake' => 'command.request.make',
        'ResourceMake' => 'command.resource.make',
        'RuleMake' => 'command.rule.make',
        'SeederMake' => 'command.seeder.make',
        'SessionTable' => 'command.session.table',
        'Serve' => 'command.serve',
        'StubPublish' => 'command.stub.publish',
        'TestMake' => 'command.test.make',
        'VendorPublish' => 'command.vendor.publish',
    ];
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerCommands(array_merge(
            $this->commands, $this->devCommands
        ));
    }
    /**
     * Register the given commands.
     *
     * @param  array  $commands
     * @return void
     */
    protected function registerCommands(array $commands)
    {
        foreach (array_keys($commands) as $command) {
            call_user_func_array([$this, "register{$command}Command"], []);
        }
        $this->commands(array_values($commands));
    }
```

```php:Illuminate\Support\ServiceProvider&#x3a;&#x3a;commands()
    /**
     * Register the package's custom Artisan commands.
     *
     * @param  array|mixed  $commands
     * @return void
     */
    public function commands($commands)
    {
        $commands = is_array($commands) ? $commands : func_get_args();
        Artisan::starting(function ($artisan) use ($commands) {
            $artisan->resolveCommands($commands);
        });
    }
```

``$commands``と``$devCommands``に連想配列が沢山代入されています。
``register()``メソッドは``registerCommands()``に``$commands``と``$devCommands``を``array_merge``したものを渡してコールしています。
``registerCommands()``メソッドは受け取った配列を``array_keys``でキーのみ配列で取り出し、それを``foreach``で回し、``call_user_func_array``でコールバック関数を実行します。実行するコールバックは自身に定義してある関数で、関数名は``register{$command}Command``で生成されたものです。引数は空の配列となっています。
このクラスにはクロージャーをアプリケーションコンテナにシングルトンで結合するメソッドが沢山定義されてます。
以下は例です。

```php:定義例
    /**
     * Register the command.
     *
     * @return void
     */
    protected function registerUpCommand()
    {
        $this->app->singleton('command.up', function () {
            return new UpCommand;
        });
    }
```

アプリケーションコンテナに``command.up``という名前で``UpCommand``インスタンスを生成して返すクロージャーをシングルトン結合しています。
``$commands``と``$devCommands``に代入されている沢山のコマンドがシングルトン結合されます。
次に``commands()``メソッドを``$commands``を``array_values``で値のみの配列にしたものを引数にコールします。
``Artisan::starting()``をクロージャーを引数に渡してコールしています。``Artisan``は``Illuminate\Console\Application``のことです。見てみましょう。

```php:Illuminate\Console\Application&#x3a;&#x3a;starting()
    /**
     * Register a console "starting" bootstrapper.
     *
     * @param  \Closure  $callback
     * @return void
     */
    public static function starting(Closure $callback)
    {
        static::$bootstrappers[] = $callback;
    }
```

#### Illuminate\Console\Application::starting()
> 第一引数はクロージャーです。
戻り値はありません。

``static::$bootstrappers[]``配列に受け取ったクロージャーを登録しています。
先程疑問だった、``static::$bootstrappers``に格納されている中身の正体がわかりました。
``static::$bootstrappers[]``に登録されたクロージャー全てに``$this``を渡したものをアプリケーションコンテナにシングルトン結合するという手順になります。

せっかくここまで読んだので、``Artisan::starting()``メソッドに引数として渡されているクロージャーも読んでみましょう。

```php
        Artisan::starting(function ($artisan) use ($commands) {
            $artisan->resolveCommands($commands);
        });
```

このクロージャーをコールする``Artisan::bootstrap()``では引数として``$this``を渡しています。つまり、``$this->resolveCommands(コマンド名)``が実行されるクロージャーです。``resolveCommands()``を見てみましょう。

```php:Illuminate\Console\Application&#x3a;&#x3a;resolveCommands()|関連メソッド
    /**
     * Resolve an array of commands through the application.
     *
     * @param  array|mixed  $commands
     * @return $this
     */
    public function resolveCommands($commands)
    {
        $commands = is_array($commands) ? $commands : func_get_args();
        foreach ($commands as $command) {
            $this->resolve($command);
        }
        return $this;
    }
    /**
     * Add a command, resolving through the application.
     *
     * @param  string  $command
     * @return \Symfony\Component\Console\Command\Command
     */
    public function resolve($command)
    {
        return $this->add($this->laravel->make($command));
    }
    /**
     * Add a command to the console.
     *
     * @param  \Symfony\Component\Console\Command\Command  $command
     * @return \Symfony\Component\Console\Command\Command
     */
    public function add(SymfonyCommand $command)
    {
        if ($command instanceof Command) {
            $command->setLaravel($this->laravel);
        }
        return $this->addToParent($command);
    }
    /**
     * Set the Laravel application instance.
     *
     * @param  \Illuminate\Contracts\Container\Container  $laravel
     * @return void
     */
    public function setLaravel($laravel)
    {
        $this->laravel = $laravel;
    }
    /**
     * Add the command to the parent instance.
     *
     * @param  \Symfony\Component\Console\Command\Command  $command
     * @return \Symfony\Component\Console\Command\Command
     */
    protected function addToParent(SymfonyCommand $command)
    {
        return parent::add($command);
    }
```

#### Illuminate\Console\Application::resolveCommands()
> 第一引数は コマンドです。
戻り値は $this です。

受け取ったコマンドを``foreach``で回し、``resolve()``メソッドを引数にコマンド名を渡してコールし、自身を返しています。
``resolve()``はコマンド名を受け取りそれをアプリケーションコンテナで``make()``した戻り値を``add()``しています。
``add()``メソッドでは、引数として渡されたコマンドが``Command``インスタンス か検証し、そうであった場合は``setLaravel()``メソッドにアプリケションコンテナを引数に渡しコールします。
そうでない場合は``addToParent()``メソッドを通し、スーパークラスの``add()``メソッドをコールします。
``setLaravel()``メソッドはコマンドの変数``$laravel``にアプリケーションコンテナを代入します。
スーパークラスの``add()``メソッドとは``Symfony\Component\Console\Application::add()``の事です。
``Symfony\Component\Console\Application::add()``が引数として受け取るのは``Symfony\Component\Console\Command\Command``インスタンスで、これは``Illuminate\Console\Command``のスーパークラスです。つまり ``Illuminate\Console\Command``は``Symfony\Component\Console\Command\Command``をLaravel用に拡張したコマンドクラスなのでしょう。
``Symfony\Component\Console\Application``を読みたいところですが、少しボリュームがあるので、まずは本筋に戻ります。

-----

``Illuminate\Foundation\Console\Kernel::handle()``から``$this->getArtisan()``がコールされその処理の途中でした。

```php:Illuminate\Foundation\Console\Kernel&#x3a;&#x3a;getArtisan()
    /**
     * Get the Artisan application instance.
     *
     * @return \Illuminate\Console\Application
     */
    protected function getArtisan()
    {
        if (is_null($this->artisan)) {
            return $this->artisan = (new Artisan($this->app, $this->events, $this->app->version()))
                                ->resolveCommands($this->commands);
        }
        return $this->artisan;
    }
```

生成した``Artisan``インスタンスの``resolveCommands``に``$this->commands``を引数に渡しコールします。おそらく``$this->commands``は空の配列なのでただ戻り値に自身が返ってくるだけでしょう。
そのまま生成された``Artisan``インスタンスが戻されます。

``Illuminate\Foundation\Console\Kernel::handle()``に戻ります。

```php:Illuminate\Foundation\Console\Kernel&#x3a;&#x3a;handle()
    public function handle($input, $output = null)
    {
        try {
            $this->bootstrap();
            return $this->getArtisan()->run($input, $output);
        } catch (Throwable $e) {
            $this->reportException($e);
            $this->renderException($output, $e);
            return 1;
        }
```

``$this->getArtisan()``の戻り値に``run()``しています。引数に入力と出力を渡しています。
次はこちらを見ていきましょう。

## 次回
今回は``artisan``インスタンスの生成の流れを読んでみました。次回は``Console\Application::run()``を追ってみたいと思います！

続く☆