July 23, 2021 • ☕️☕️ 8 min read
【 環境 】
Laravel のバージョン: 8.16.1
PHP のバージョン: 7.4.7
MySQL のバージョン: 5.7  
以前、こんなの書きました。
Laravel:Command クラスの handle メソッドに記述されている return 0 って何?  
class SampleCommand extends Command
{
// 中略
    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        return 0;  //← これは一体何?
    }
}概要をまとめると、こんな感じ。
コマンドラインから叩く場合、「 ; 」で繋げると良さげ。
php artisan command:name ; echo $?結論としては、出来ませんでした。
(詳細は冒頭で紹介したブログを参照)  
が、Schedule クラスには onSuccess メソッドと onFailure メソッドがあり、これを使えば成功したかどうかを検出できるのでは?
と思ったので実験。
Kernel クラスは、こんな感じ。
    protected function schedule(Schedule $schedule)
    {
        $this->scheduleExecuteCommand01($schedule);
        $this->scheduleExecuteCommand02($schedule);
        $this->scheduleExecuteCommand03($schedule);
    }
    private function scheduleExecuteCommand01(Schedule $schedule)
    {
        $schedule->command(Batch01Command::class)
            ->everyMinute()
            ->runInBackground()
            ->withoutOverlapping()
            ->onSuccess(function () {
                \Log::info('Batch01Command successful.');
            })
            ->onFailure(function () {
                \Log::info('Batch01Command failed.');
            });
    }
    private function scheduleExecuteCommand02(Schedule $schedule)
    {
        $schedule->command(Batch02Command::class)
            ->everyMinute()
            ->runInBackground()
            ->withoutOverlapping()
            ->onSuccess(function () {
                \Log::info('Batch02Command successful.');
            })
            ->onFailure(function () {
                \Log::info('Batch02Command failed.');
            });
    }
    private function scheduleExecuteCommand03(Schedule $schedule)
    {
        $schedule->command(Batch03Command::class)
            ->everyMinute()
            ->runInBackground()
            ->withoutOverlapping()
            ->onSuccess(function () {
                \Log::info('Batch03Command successful.');
            })
            ->onFailure(function () {
                \Log::info('Batch03Command failed.');
            });
    }Batch01Command は 0 を返し、
Batch02Command は 0 以外の値を返すようにする。
Batch03Command は return を記述しない。  
    public function handle()
    {
        return 0;
    }    public function handle()
    {
        return 1;
    }    public function handle()
    {
        // 戻り値を指定しない
    }以下のような結果になりました。
[2021-07-22 13:30:17] Batch01Command successful.  
[2021-07-22 13:30:17] Batch02Command failed.  
[2021-07-22 13:30:21] Batch03Command successful.  Batch01Command、Batch02Command は、バッチリ意図通りです。
そして、return を省略すると 0 が返るので、Batch03Command も予想通り。  
という訳で、コマンドのリターンコードを受け取って判別、という事はできませんが、onSuccess メソッドと onFailure メソッドを使って、コマンドが成功したか失敗したかを判別できそうです。
戻り値を指定しなかった場合は 0 が返るのですが、メソッド内で明らかなエラーが起こった時も 0 を返すの?
と思い、以下のようなコードで実験。
    private function scheduleExecuteCommand04(Schedule $schedule)
    {
        $schedule->command(Batch04Command::class, [0])
            ->everyMinute()
            ->runInBackground()
            ->withoutOverlapping()
            ->onSuccess(function () {
                \Log::info('Batch04Command successful.');
            })
            ->onFailure(function () {
                \Log::info('Batch04Command failed.');
            });
    }    public function handle()
    {
        $param1 = $this->argument('param1');
        $val1 = 10 / $param1;
        \Log::info($param1);
        \Log::info($val1);
    }以下のような結果になりました。
[2021-07-22 13:44:43] local.ERROR: Division by zero {"exception":"[object] (ErrorException(code: 0): Division by zero at /var/www/html/my-laravel-app/app/Console/Commands/Batch01Command.php:42)
[stacktrace]
#0 /var/www/html/my-laravel-app/app/Console/Commands/Batch01Command.php(42): Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError(2, 'Division by zer...', '/var/www/html/m...', 42, Array)
#1 /var/www/html/my-laravel-app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): App\\Console\\Commands\\Batch01Command->handle()
(中略)
[2021-07-22 13:44:49] local.INFO: Batch01Command failed.  ゼロ割によるエラーが出力されています。
そして、onFailure に分岐したようで、「Batch01Command failed」を出力し、しっかりとエラー扱いにしてくれています。
コマンドの場合はどうなるの?
省略した場合は 0 が返るから、正常終了と見なされるの?
と、気になったので調べてみた。
php artisan command:batch04 0 ; echo $?結果、こんな感じ。
   ErrorException 
  Division by zero
(中略)
  15  artisan:37
      Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
1ノイズが混ざって見づらくなってるけど、「1」が返ってきています。
また、ゼロ割が発生しないようにコマンドを叩いたら、「0」が返ってきました。
> php artisan command:batch04 1 ; echo $?
> 0省略時は 0 が返って来るようですが、明らかなエラーが発生した場合は 0以外の値が返って来るようです。
しかし、
「PHPの処理としては正常に終了しているけど、レコードがちゃんと更新されていない(本当はレコードが更新されているけど、更新件数が 0件)」
といった処理は異常終了として検出されないので、常にリターンコードを意識するようにした方がいんじゃないかと思います。  
コマンドを以下のように変えて実行してみた。
    public function handle()
    {
        try {
            $param1 = $this->argument('param1');
            $val1 = 10 / $param1;
            \Log::info($param1);
            \Log::info($val1);
            return 0;
        } catch (Exception $e) {
            \Log::error($e->getMessage());
            return 1;
        }
    }こんなログが出力されました。
[2021-07-22 15:48:31] local.ERROR: Division by zero {"exception":"[object] (ErrorException(code: 0): Division by zero at /var/www/html/my-laravel-app/app/Console/Commands/Batch04Command.php:43)
[stacktrace]
#0 /var/www/html/my-laravel-app/app/Console/Commands/Batch04Command.php(43): Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError(2, 'Division by zer...', '/var/www/html/m...', 43, Array)
#1 /var/www/html/my-laravel-app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): App\\Console\\Commands\\Batch04Command->handle()
#2 /var/www/html/my-laravel-app/vendor/laravel/framework/src/Illuminate/Container/Util.php(40): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
(中略)
[2021-07-22 15:48:36] local.INFO: Batch04Command failed.  どうやら、コマンド内においては、システム的なエラーが発生しても Try catch で補足できず、エラーを返すようです。
スケジューラからの起動でなく、コマンドからの起動でも同じ結果となりました。
> php artisan command:batch04 0 ; echo $?
> 1という事で、システム的なエラーが発生した場合は、こっちが return の値を指定しなかったとしても、気を利かしてエラーコードを返し、onFailure に分岐してくれるようです。
Schedule クラスから command を実行する時、リターンコードは取れないけど、onSuccess メソッドと onFailure メソッドを使えばエラーを検知できるので、活用しよう!