【Laravel】Artisanでプログレスバーを自在に作る

TR;DR

  • LaravelのArtisanではSymfonyのプログレスバーをそのまま使っている
  • Symfonyのプログレスバーはデザイン、表示情報などを自在に設定できて便利

本文

 CLI上で時間のかかる処理を行う時があります。そういった時、何かしら進捗が見えないと異常事態が起きて完了していないのか、正常動作だが処理内容が重すぎて完了しないのかまるでわかりません。プログレスバーはそういった処理の進捗を分かりやすく表示してくれます。LaravelのArtisan上でプログレスバーを使うコードは次です。
Artisanコンソール 6.x Laravel

$users = App\User::all();

// 引数に処理の総ステップ数を渡す
$bar = $this->output->createProgressBar(count($users));

foreach ($users as $user) {
    $this->performTask($user);

    $bar->advance();// プログレスバーの表示を一歩進める
}

$bar->finish();// プログレスバーの表示を完了状態にする

 これで次の様な画面出力がされます。

 これだけでも処理の進捗がわかり便利なのですが、できることならば処理にかかる時間なども知りたいところです。これは手の込んだことをしなくとも実は組み込みで機能が備わっています。
 元々このArtisanのプログレスバー表示はSymfonyのプログレスバーそのものです。というのも次の様な呼び出し過程になっているためです。

// $this->outputから createProgressBar メソッドを呼ぶ
$bar = $this->output->createProgressBar(count($users));

// ↑の $this->output は \Illuminate\Console\OutputStyle で Symfony の SymfonyStyle クラスの継承
// @see /vendor/laravel/framework/src/Illuminate/Console/OutputStyle.php
// @see /vendor/symfony/console/Style/SymfonyStyle.php
class OutputStyle extends SymfonyStyle
{

 この記事ではプログレスバーを扱いますが、タイトルや以前記事に起こした対話コマンドなど様々なCLI出力用テンプレートが output プロパティから呼び出せます。
The Console Component (Symfony Docs)
 プログレスバーについては次の記事にあり、表示する文字の変更や残り時間、使用資源などが操作できるとあります。
Progress Bar (The Console Component – Symfony Docs)
 改造したプログレスバーのコードが次です。

/**
 * コマンド基底クラス.
 * Commandsディレクトリ以下に入れるとArtisan Commandの一つとしてloadされるのでConsole直下に配置
 * Class BaseCommand
 * @package App\Console
 */
class BaseCommand extends Command
{
    /**
     * 色々情報の詰まったフォーマットに改造したプログレスバーを生成して返す。
     * @param  int         $max
     * @return ProgressBar
     */
    protected function createProgressBar($max = 0): ProgressBar
    {
        // プログレスバーインスタンスを生成
        $progressBar = $this->output->createProgressBar($max);
        // メッセージフォーマットを生成. 日本語的には
        // %現在までに進んだステップ数%/%総ステップ数% [%進捗バー%] %進捗パーセント%% %経過時間%/%推定所要時間% %使用しているメモリ量%
        $progressBar->setFormat('%current%/%max% [%bar%] %percent%% %elapsed:6s%/%estimated:-6s% %memory:6s%');
        // そこまで進んでいないバーの部分の文字
        $progressBar->setEmptyBarCharacter(' ');
        // そこまで進んだバーの部分の文字
        $progressBar->setBarCharacter('=');
        // 現在値を示すバーの部分の文字
        $progressBar->setProgressCharacter('>');

        return $progressBar;
    }
}

 このコードで新たに定義した createProgressBar で生成したプログレスバーを使うと次のようにななります。

 見やすくなり、現状がよりわかる情報が表示されるようになりました。
 余談ですが並列動作もカバーしています。PHPで並列処理を作るのは手間ですし滅多に使わないでしょうに手が込んでます。

    public function handle(): void
    {
        // Symfonyの用意したCLI用出力を直接インスタンス化
        $symOutput = new ConsoleOutput();
        $bars      = []; // プログレスバーが入る予定の箱
        foreach (range(0, 5) as $i) {
            // new ProgressBar($symOutput->section()) である行をこのプログレスバーが使うと予約
            $bars[] = new ProgressBar($symOutput->section());
            $bars[$i]->start(100);
        }

        // アニメーション的に各プログレスバーを順次進める
        $usleep = 1000000 / count($bars);
        while (true) {
            foreach ($bars as $bar) {
                $advance = random_int(0, 30);
                $advance = $advance + $bar->getProgress() >= $bar->getMaxSteps()
                    ? $bar->getMaxSteps() - $bar->getProgress()
                    : $advance;
                $bar->advance($advance);
                usleep($usleep);
                if ($bar->getProgress() >= $bar->getMaxSteps()) {
                    $bar->finish();
                    break 2;// 外ループ(ここではwhile)も抜けるbreak
                }
            }
        }
    }

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG