【php】proc_open()からのstream_get_contents()で処理が固まる場合
問題
あるコマンドを、phpで、proc_open()して、stream_get_contents()しようとしてるのですが、
コマンドの処理は終わった様子なのに、phpがいつまでも終わりません。
何が起きてるんでしょう。
答え
phpで標準出力も標準エラーも全部受け取るようにしていると、
標準出力や標準エラー出力のバッファがいっぱいになって固まることがあるらしいです。
対策1 エラー出力をそもそも出さない
実行しようとしているコマンドがエラー出力をやたらとおこなう場合、コマンドのオプションでエラー出力を抑制できるなら、抑制するとよい。
–verbose とか –quiet など指定できる場合がある(コマンドによる)。
command --quiet
対策2 エラー出力をファイルに逃がす
以下のようにすると固まるので、
$proc = proc_open($cmd, array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w') ), $pipes);
以下のように、ため込まずにファイルに随時出力させると回避できた。
$proc = proc_open($cmd, array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('file', '/tmp/error-xxxx.txt', 'a') ), $pipes);
対策3 エラー出力を/dev/null に捨てる
捨てる。
$proc = proc_open($cmd, array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('file', '/dev/null', 'a') ), $pipes);
対策4 エラー出力をそもそも拾わない
2は捨てて、以下だけにする。
$proc = proc_open($cmd, array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w') ), $pipes);
この場合、エラー出力をphpが受け取らないので、そのままエラー出力がエラー出力として出てくる(端末とかに)。
対策5 エラー出力をちゃんと読みながらちゃんと処理する
エラーも読む必要がある場合ならば、読まねばなるまい。
しかし、バッファがいっぱいになるとまずいようなので、上手に読む。
$proc = proc_open($cmd, array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w') ), $pipes); stream_set_blocking($pipes[1], 0); stream_set_blocking($pipes[2], 0); fwrite($pipes[0], $html); fclose($pipes[0]); $stdout = $stderr = ''; while (feof($pipes[1]) === false || feof($pipes[2]) === false) { $ret = stream_select( $read = array($pipes[1], $pipes[2]), $write = null, $except = null, $timeout = 1 ); if ($ret === false) { // error break; } else if ($ret === 0) { // timeout continue; } else { foreach ($read as $sock) { if ($sock === $pipes[1]) { $stdout .= fread($sock, 4096); } else if ($sock === $pipes[2]) { $stderr .= fread($sock, 4096); } } } } fclose($pipes[1]); fclose($pipes[2]); proc_close($proc);
まとめ
コマンドを静かにする(対策1)、エラー出力を捨てる(対策3)あたりが手軽。
エラー出力も取得する必要があって、出力、エラー出力がそれなりの量になる場合、対策5のような読み取り方をする必要がある。
コメント