Laravel のコマンド実行時に標準入力にデータを渡して実行する
バッチの処理やちょっとした処理を行う時に、よく Laravel のコマンドを作成することがあります。 その際にオプションや引数ではなく、ローカルにあるファイルをまるっと渡したいなーと思ったのですが、その際に対応した内容についてのまとめです。
Heroku を使っていたので Heroku の環境でコマンドを実行する際に、ローカルにあるファイルを使用したいという状況でした。(データ内容的に s3 などには置きたくなかった)
PHPでの標準入力
PHP での標準入力の読み込みに関しては、今回は file_get_contents を使用します。
サンプルコードは Laravel のコマンドを作成する前提です。
<?php declare(strict_types=1); namespace App\Console\Commands; use Illuminate\Console\Command; class Sample extends Command { protected $signature = 'sample:test'; protected $description = 'hogehoge'; public function __construct() { parent::__construct(); } public function handle() { $data = file_get_contents('php://stdin'); var_dump($data); } }
コマンド実行時に標準入力へデータを渡すのは下記のようにします。 もし外部からデータを取得してそのまま使用する場合には、ある程度バリデーションをちゃんとした方が良いかもしれません。
// ローカルで実行する場合 $ php artisan sample:test < test.json // herokuの環境で実行する場合 $ heroku run -a heroku-app php artisan sample:test < test.json // curl等で取得したデータをそのまま渡す $ curl "https://zipcloud.ibsnet.co.jp/api/search?zipcode=7830060" | php artisan sample:test % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 290 100 290 0 0 308 0 --:--:-- --:--:-- --:--:-- 307 string(290) "{ "message": null, "results": [ { "address1": "高知県", "address2": "南国市", "address3": "蛍が丘", "kana1": "コウチケン", "kana2": "ナンコクシ", "kana3": "ホタルガオカ", "prefcode": "39", "zipcode": "7830060" } ], "status": 200 }"
テストをどうするか
file_get_contents を簡単に mock する方法があるのか調べたのですが、あまり良さそうな方法はありませんでした。
渡したいデータの内容にもよるのですが、このように標準入力以外でもデータを渡せるようにしておけば、動作確認やテストなどもやりやすくなるのかなと思います。
<?php $data = config('env.sample') ?? \file_get_contents('php://stdin'); // or $data = $this->option('input') ? \Storage::get($this->option('input')) : file_get_contents('php://stdin');
テストはこんな感じでそれぞれテスト用のデータを渡せるようにして、標準入力にデータを渡さずにテストを行えるようにしました。
<?php Config::set('env.sample', 'ここに渡したいデータ'); $this->artisan('sample:command')->assertExitCode(0); // or \Storage::fake('test'); $contents = []; // ここにテストをしたいデータを作成する \Storage::put('test.json', json_encode($list));
使用頻度が高いコマンドなら色々と考慮することも多いかもしれないですが、今回は一時的に使いたいコ処理だったので、こんな感じでも問題ないかなと思いました。