🎃

Laravel 特定の環境でコマンドの実行を止める

2024/11/14に公開

はじめに

この前このようなポストを見かけた。

https://x.com/MrPunyapal/status/1851638598766149706

特定の環境でコマンド実行を無効化してくれる仕組みが11.9.0で増えていたようです。

記事もありました。
https://laravel-news.com/prevent-destructive-commands-from-running-in-laravel-11

どうやって実行を止めているのか調べてみることした。

環境

  • PHP 8.3.13
  • Laravel 11.31.0

そもそも本番環境なら実行する前に確認が入るはず

特定のコマンドは本番環境(APP_ENV=production)であれば下記のように実行する/しないを選択できます。

選択なので許可するれば本番環境でも実行することは可能です。

では、何が違うのか?

この仕組みを追加したプルリクエストを見るのが一番早いので見てみます。

https://github.com/laravel/framework/pull/51376

db:wipe, migrate:fresh, migrate:refresh, migrate:resetの4つのコマンドが本番環境では実行を選択はさせないで止めることができるってことらしい。
この機能を有効にする方法は、DBファサードのprohibitDestructiveCommandsメソッドを実行する必要がある。

DB::prohibitDestructiveCommands($this->app->isProduction());

引数にtrueを渡すと4つのコマンドを無効化できる。

やってみる

この処理を仕込むならAppServiceProviderbootメソッドあたりに書いておけばいいだろう。

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        DB::prohibitDestructiveCommands($this->app->isProduction());
    }
}

仕込んだ状態でAPP_ENV=productionの場合に実行してみると

 % php artisan migrate:fresh

   WARN  This command is prohibited from running in this environment.  

選択もできないで動かなくなった。

もし、どれか1つだけ無効化する場合は下記のように書くことで対応できるようです。

ResetCommand::preventFromRunning($this->app->isProduction());

ほかのコマンドには仕込めないのか?

仕組みとしてはProhibitabletraitを読み込んでいれば使えるようなので自作コマンドにProhibitabletraitを読み込ませれば使えるようです。

app/Console/Commands/SampleCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Console\Prohibitable;

class SampleCommand extends Command
{
    use Prohibitable;

    protected $signature = 'app:sample-command';

    protected $description = 'Command description';

    public function handle()
    {
        if ($this->isProhibited()) {
            echo '実行できない' . PHP_EOL;
            return Command::FAILURE;
        }

        echo '実行した' . PHP_EOL;

        return Command::SUCCESS;
    }
}

自作コマンドではprohibitメソッドを実行することで無効化するかの判定を行うことができる。
仕込む場所は先ほどと同じでAppServiceProviderbootメソッドで問題ないでしょう。

app/Providers/AppServiceProvider.php
namespace App\Providers;

use App\Console\Commands\SampleCommand;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        SampleCommand::prohibit($this->app->isProduction());
    }
}

では、先ほどと同じように実行してみます。

  • APP_ENV=local
% php artisan app:sample-command
実行した
  • APP_ENV=production
% php artisan app:sample-command

   WARN  This command is prohibited from running in this environment.  

実行できない

できましたね。自作コマンドの場合は判定方法を変えれば本番環境以外でも実行を無効化できそうです。

まとめ

たまたま見かけた機能ですがフレームワークから事故防止の仕組みが用意してくれているのは嬉しいですね。
実際に自作コマンドに仕込む機会があるかは不明ですが、備えあれば憂いなしとも言いますし、何か機会があれば仕込んでおくと安心するかもしれないですね。

GitHubで編集を提案

Discussion