I was using a function that made use of proc_open()
to invoke shell commands. It seems the way I was doing STDIO was wrong and sometimes cause
while($r = stream_select($read, $write, $except, null, $timeout)){
As far as I know this will set $r to the number of changed streams, which may be 0 and the loop would no longer continue. I would personally recode this as described in the PHP manual:
while(false !== ($r = stream_select($read, $write, $except, null, $timeout))){
As far as your STDIN is concerned if your process is not interactive then the STDIN may not be necessary. What is the process you are executing?
The whole problem with hanging in stream_get_contents is in the way how process is created. The correct way is to open STDOUT with read/write mode of pipe, eg:
$descriptor = array (0 => array ("pipe", "r"), 1 => array ("pipe", "rw"), 2 => array ("pipe", "rw"));
//Open the resource to execute $command
$t->pref = proc_open($command,$descriptor,$t->pipes);
//Set STDOUT and STDERR to non-blocking
stream_set_blocking ($t->pipes[0], 0);
stream_set_blocking ($t->pipes[1], 0);
This is obvious that when stream_get_contents wants to read the STDOUT pipe it needs read mode. The same bug with hang/freeze/block is in this nice class https://gist.github.com/Arbow/982320
Then blocking disappears. But read does not read nothing.
You've missed this note in the PHP manual for stream_select():
When stream_select() returns, the arrays read, write and except are modified to indicate which stream resource(s) actually changed status.
You need to re-create the arrays before calling stream_select() each time.
Depending on the process you're opening, this may be why your example still blocks.
Well, seems a year passed and forgot this thing is still pending!
However, I wrapped up this mess in a nice PHP class which you can find on Github.
The main remaining problem is that reading STDERR causes the PHP script to block, so it has been disabled.
On the bright side, thanks to events and some nice coding (I hope!), one can actually interact with the process being executed (hence the class name, InterExec
). So you can have bot-style behavior in PHP.