問題
proc_open()
シェルコマンドを呼び出すために利用した関数を使用していました。STDIO のやり方が間違っていたようで、PHP やターゲット コマンドがロックされることがありました。これは元のコードです:
function execute($cmd, $stdin=null){
$proc=proc_open($cmd,array(0=>array('pipe','r'),1=>array('pipe','w'),2=>array('pipe','w')),$pipes);
fwrite($pipes[0],$stdin); fclose($pipes[0]);
$stdout=stream_get_contents($pipes[1]); fclose($pipes[1]);
$stderr=stream_get_contents($pipes[2]); fclose($pipes[2]);
return array( 'stdout'=>$stdout, 'stderr'=>$stderr, 'return'=>proc_close($proc) );
}
ほとんどの場合は機能しますが、それだけでは十分ではなく、常に機能するようにしたいと考えています。
この問題はstream_get_contents()
、STDIO バッファが 4k のデータを超える場合にロックアップすることにあります。
テストケース
function out($data){
file_put_contents('php://stdout',$data);
}
function err($data){
file_put_contents('php://stderr',$data);
}
if(isset($argc)){
// RUN CLI TESTCASE
out(str_repeat('o',1030);
err(str_repeat('e',1030);
out(str_repeat('O',1030);
err(str_repeat('E',1030);
die(128); // to test return error code
}else{
// RUN EXECUTION TEST CASE
$res=execute('php -f '.escapeshellarg(__FILE__));
}
STDERR と STDOUT に、合わせて 4120 バイト (4k を超える) の文字列を 2 回出力します。これにより、PHP が両側でロックアップします。
解決
どうやら、stream_select()
行く方法です。次のコードがあります。
function execute($cmd,$stdin=null,$timeout=20000){
$proc=proc_open($cmd,array(0=>array('pipe','r'),1=>array('pipe','w'),2=>array('pipe','w')),$pipes);
$write = array($pipes[0]);
$read = array($pipes[1], $pipes[2]);
$except = null;
$stdout = '';
$stderr = '';
while($r = stream_select($read, $write, $except, null, $timeout)){
foreach($read as $stream){
// handle STDOUT
if($stream===$pipes[1])
/*...*/ $stdout.=stream_get_contents($stream);
// handle STDERR
if($stream===$pipes[2])
/*...*/ $stderr.=stream_get_contents($stream);
}
// Handle STDIN (???)
if(isset($write[0])) ;
// the following code is temporary
$n=isset($n) ? $n+1 : 0; if($n>10)break; // break while loop after 10 iterations
}
}
パズルの唯一の残りのピースは、STDIN の処理です ( の行を参照(???)
)。
STDIN は、自分の関数を呼び出しているものによって提供される必要があることがわかりましたexecute()
。しかし、STDIN をまったく使用したくない場合はどうすればよいでしょうか。上記のテストケースでは、入力を求めませんでしたが、STDIN に対して何かを行うことになっています。
とはいえ、上記のアプローチは依然として でフリーズしstream_get_contents()
ます。私は何をすべきか/次に何をしようとしているのかよくわかりません.
クレジット
解決策は、元の問題を発見しただけでなく、Jakob Truelsen によって提案されました。4kのヒントも彼のアイデアでした。これに先立って、関数が正常に動作する理由について困惑していました (すべてがバッファ サイズに依存していることを知りませんでした)。