LaravelでArtisanの開始イベントと終了イベントを発行する

LaravelでArtisanの開始イベントと終了イベントを発行させてみた。

## Laravel 4.2

`app/start/artisan.php`の最後に下記のコードを追加した。

```app/start/artisan.php
<?php

...

use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\EventDispatcher\EventDispatcher as SymfonyEventDispatcher;

// Artisanの開始イベントと終了イベントを発行するイベントディスパッチャーの登録
$dispatcher = new SymfonyEventDispatcher();
$dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) {
    Event::fire('artisan.begin ' . $event->getCommand()->getName(), $event);
});
$dispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event) {
    Event::fire('artisan.finish ' . $event->getCommand()->getName(), $event);
});
Artisan::setDispatcher($dispatcher);

// イベント購読クラスの登録
Event::subscribe(new HogeArtisanEventHandler());
```

Symfonyの[EventDispatcherコンポーネント](http://symfony.com/doc/current/components/event_dispatcher.html)を使用して、Artisanコマンドの開始時と終了時にそれぞれ`artisan.begin $command_name`と`artisan.finish $command_name`というイベントが発行されるようにしている。
また、イベント購読クラスの登録もしている。


`HogeArtisanEventHandler`クラスの実装は下記。

```HogeArtisanEventHandler.php
<?php

class HogeArtisanEventHandler
{
    /**
     * Artisan開始イベントの処理
     */
    public function onArtisanBegin($event)
    {
        //
    }

    /**
     * Artisan終了イベントの処理
     */
    public function onArtisanFinish($event)
    {
        //
    }

    /**
     * イベント購読リスナーの登録
     *
     * @param  Illuminate\Events\Dispatcher $events
     * @return array
     */
    public function subscribe($events)
    {
        $events->listen('artisan.begin hoge:*', 'HogeArtisanEventHandler@onArtisanBegin');
        $events->listen('artisan.finish hoge:*', 'HogeArtisanEventHandler@onArtisanFinish');
    }
}
```

イベント購読クラスはArtisanの名前空間単位で作成してみた。
このクラスでは`hoge:*`コマンドで発行された開始イベントと終了イベントのみリッスンしている。

開始ログや終了ログを出力したりするのに便利。

## Laravel 5.3

フレームワークの機能として`CommandStarting`イベントが追加された。
しかし終了イベントがないので、終了イベントを発行したい場合には同じように自分で書く必要がある。

Laravel 5.3では`app/Console/Kernel.php`の`getArtisan`メソッドで同じように書くことで実現できた。
Laravel 5.0〜5.4は同じ方法でできる思う（未検証）。

```app/Console/Kernel.php
<?php

namespace App\Console;

use Event;
use HogeArtisanEventHandler;
use Illuminate\Console\Application as Artisan;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\EventDispatcher\EventDispatcher as SymfonyEventDispatcher;

class Kernel extends ConsoleKernel
{
    ...

    /**
     * {@inheritdoc}
     */
    protected function getArtisan()
    {
        if (is_null($this->artisan)) {
            $dispatcher = new SymfonyEventDispatcher();
            $dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) {
                Event::fire('artisan.begin ' . $event->getCommand()->getName(), $event);
            });
            $dispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event) {
                Event::fire('artisan.finish ' . $event->getCommand()->getName(), $event);
            });
            $this->artisan = (new Artisan($this->app, $this->events, $this->app->version()))
                ->resolveCommands($this->commands);
            $this->artisan->setDispatcher($dispatcher);
            Event::subscribe(new HogeArtisanEventHandler());
            return $this->artisan;
        }

        return $this->artisan;
    }
}
```

## Laravel 5.5以上

フレームワークの機能として`CommandFinished`イベントが追加されたので、自分で書く必要がなくなった。