【Laravel11】php artisanコマンドを本番環境で制限したかった話。

こんにちは。
バージョン違いはもはや別物。
只野です。

最近Laravel11を使って色々開発している私です。

Laravel便利ですね~。フレームワークに色々便利機能が搭載されていて、データベースの初期構築も正しくマイグレーションファイルが作成されていればコマンド一発!

これのおかげで開発時は環境を素早く作ることが可能ですね。Dockerと組み合わせればもはや今風それっぽい開発環境のできあがり…

開発環境まではよい!

だが!

Laravelの便利コマンドは「artisan」というコマンドにほぼ含まれているのですが、この中に“本番環境で誤って実行されると困るコマンド”がいくつか存在する!

本番環境で実行されると困るコマンド!

  • php artisan migrate:reset(すべてのマイグレーションをロールバック!)
  • php artisan migrate:refresh(すべてのマイグレーションをロールバックしてから再びマイグレーション!)
  • php artisan migrate:rollback(直近のマイグレーションをロールバック!)
  • php artisan migrate:fresh(すべてのテーブルをドロップ(削除)してから再びマイグレーション!)
  • php artisan db:seed(シーダーを利用した初期データを作成!)

こんな感じで“開発時は便利でも本番環境で実行された日には号泣する”かもしれないコマンドがLaravel artisanには含まれています。

そこで私は思ったわけです。

このコマンド達を本番環境で封印したい!

こんなん新人がやらかした!!とか余裕であると考えます。

そんで「Laravelは人気だからネット調べればサクッと出てくるでしょう。」そう考えていた時期が私にもありました。

Laravel 11だと情報が全然ねーぞ!

現在最新のバージョンだけあり、まー情報が少ない。

前のバージョンのやり方はLaravel11ではフレームワークから削除されたファイルを利用していたりしてできない。そんなんばっかです。ちくしょう。コピペさせろ!

しょうがないので公式ドキュメント読んだり、前のバージョンで使えそうなとこなり半日程度調べて下記方法が使えそうと結論になりました。

Laravel11はWebアプリでもコンソールアプリでも、処理実行時に必ず呼び出される箇所があります。

今回はその処理内に「コンソールコマンドの前処理」を実装してい、該当のコマンドが来た際にはコマンドを停止する方式を採用していきます。

具体的なソースは下記になります。

{アプリケーションルート}\app\Providers\AppServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
// 1:追記開始
use Illuminate\Support\Facades\Log;
use Illuminate\Console\Events\CommandStarting;
use Illuminate\Support\Facades\Event;
// 1:追記終了

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {

// 2:追記開始
        // ■禁止コマンドイベント
        Event::listen(CommandStarting::class, function (CommandStarting $event) {

            // デバッグモードは対象外
            if (config('app.debug') == true) {
                return;
            }

            // コマンド禁止
            if ($event->command == 'reset'
                || $event->command == 'fresh'
                || $event->command == 'migrate:refresh'
                || $event->command == 'migrate:reset'
                || $event->command == 'migrate:fresh') 
            {
                dd(__('error.command-ban', ['command' => $event->command]));
                exit;
            }

            // オプション指定必須
            if ($event->command == 'migrate:rollback'
                && strpos($event->input, '--step') <= 0) 
            {
                dd(__('error.command-no-option', ['command' => $event->command, 'option' => '--step']));
                exit;
            }

            if ($event->command == 'db:seed'
            && strpos($event->input, '--class') <= 0) 
            {
                dd(__('error.command-no-option', ['command' => $event->command, 'option' => '--class']));
                exit;
            }
        });
// 2:追記終了
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        //
    }
}

{アプリケーションルート}\lang\ja\error.php

※フレームワークファイルじゃないので自分で作成。また「.env」の「APP_LOCALE」を「ja」に変更。Laravel言語設定は各自調べて。

<?php

return [
    'command-ban' => '本番環境では実行できないコマンドです。「:command」',
    'command-no-option' => '本番環境ではオプション「:option」の指定がない場合、実行できないコマンドです。「:command」',
];

といった感じ。これでデバッグモード以外で該当の「php artisan」コマンドを実行するとコンソールにこんな感じでメッセージが出せて、該当コマンドの停止ができる。

laravel@XXXXXXXXXX:/var/www/html/maker$ php artisan migrate:fresh  

Deprecated: PHP Startup: Use of mbstring.internal_encoding is deprecated in Unknown on line 0
"本番環境では実行できないコマンドです。「migrate:fresh」" // app/Providers/AppServiceProvider.php:32

フレームワーク的な仕組みとかは各自調べてください。

仕組みとしてはまず「AppServiceProvider 」というフレームワークの初期化処理でイベント登録をしています。

ソースだと「Event::listen(CommandStarting::class, function (CommandStarting $event)」で該当クラスに対してイベントを紐づけています。

今回は「CommandStarting 」というLaravelだとコンソールコマンドの前処理にあたるクラス。このクラスが利用される箇所があれば、自身が記載した処理を実行してくれます。

あとは前処理で防ぎたいコマンド名の判定をして、該当コマンドであれば処理を停止といった流れです。

ソースの「 if (config(‘app.debug’) == true) {」でデバッグモードの場合は判定処理を行わなようにもしています。

ちなみに「CommandStarting 」で取得できる内容は下記内容となります。

コマンド「php artisan migrate:rollback –step=10」のようなコマンドを発行した場合

$event->command
// 出力内容 migrate:rollback

$event->input
// 出力内容 'migrate:rollback' --step=10

違いは大本のコマンドだけとるか、オプション含めてユーザーが発行したコマンドを取得できるかになります。

これが正解かはわかりませんが、一旦できたので良しとします。

Laravelは公式で日本語ドキュメントとか充実してほしいところです。

もっと簡単な方法があればだれか教えてください~

本日はこの辺で。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です