PHPスクリプトから他のプログラムを起動して、その出力結果を得たいことがあります。PHPではexecやproc_openがあります。しかし、標準出力(stdout)と標準エラー出力(stderr)をうまく制御して出力を取得するのは、実はなかなか面倒だし、マニュアルを読みながら自分で一から実装するのは不毛です。
symfony/process
を利用すれば、簡単で安全に外部プログラムを利用できるようになります。
パッケージ名 | Packagist: symfony/process |
作者 | fabpot (Fabien Potencier) |
ライセンス | MIT License |
バージョン | v2.7.6 (2015-10-27) |
インストール
Composerでインストールできます。
cd /your/project
composer.phar require symfony/process
特徴
つかひかた
動作確認のために、標準出力(stdout)と標準エラー出力(stderr)に書き込むテスト用のスクリプトを/tmp/test_output
に用意します。
#!/usr/bin/env php <?php echo "AAAA\n"; fwrite(STDERR, "1111\n"); echo "BBBB\n"; fwrite(STDERR, "2222\n");
呼び出す側のコードは以下のようになります。
<?php use Symfony\Component\Process\Process; $process = new Process('php /tmp/test_output'); $process->run(); // 標準出力の内容を取り出す $stdout = $process->getOutput(); //=> "AAAA\nBBBB\n" // 標準エラー出力の内容を取り出す $process->getErrorOutput(); //=> "1111\n2222\n"
タイムアウトを指定したい
プロセス起動前に$process->setTimeout()
を読んでタイムアウト時間を指定すると、ProcessTimedOutException
が送出されるようになります。
<?php use Symfony\Component\Process\Process; use Symfony\Component\Process\Exception\ProcessTimedOutException; // 1秒待ったあとで "123" を出力する $process = new Process(' php -r" sleep(1); echo 123; " '); // 処理に0.5秒以上かかったら停止する $process->setTimeout(0.5); try { $process->run(); } catch (ProcessTimedOutException $e) { echo "停止しました。"; } var_dump($process->getOutput()); //=> NULL
プロセスが停止されてなければ123
が出力されてるはずですが、タイムアウト指定がきっちり機能してるようです。
正常終了しなければ例外
外部プログラムが異常終了すると作業を続行できないような処理では$process->mustRun()
で実行すると、終了ステータスが0
以外のときProcessFailedException
を送出するようになります。
<?php use Symfony\Component\Process\Process; use Symfony\Component\Process\Exception\ProcessFailedException; $process = new Process('php -r" exit(1); "'); try { $process->mustRun(); } catch (ProcessFailedException $e) { echo $e->getMessage(); exit(1); }
例外が発生するケースが極めて稀な場合は、try-catch
で括らずプログラム全体を異常停止させた方が良いように感じます。
注意
処理の大小に拘らず、プロセス起動は時間がかかります。特にWebアプリケーションでは何度も起動すると容易にボトルネックとなりえますので、本当に必要なことだけを外部プログラムで処理させるようにするべきです。
データを加工するような処理であれば、PHPで実装されたライブラリが存在しないかPackagistで検索してみると良いかもしれません。