The problem
===========

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 caused PHP or the target command to lock up. This is the original code:

```
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) );
}

```

It works *most of the time*, but that is not enough, I want to make it work always.

The issue lies in `stream_get_contents()` locking up if the STDIO buffers exceed 4k of data.

Test Case
=========

```
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__));
}

```

We output a string twice to STDERR and STDOUT with the combined length of 4120 bytes (exceeding 4k). This causes PHP to lock up on both sides.

Solution
========

Apparently, `stream_select()` is the way to go. I have the following code:

```
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

    }
}

```

**The only remaining piece of the puzzle is handling STDIN (see the line marked `(???)`).**
I figured out STDIN must be supplied by whatever is calling my function, `execute()`. But what if I don't want to use STDIN at all? In my testcase, above, I didn't ask for input, yet I'm supposed to do something to STDIN.

That said, **the above approach still freezes** at `stream_get_contents()`. I'm quite unsure what to do/try next.

Credits
=======

The solution was suggested by Jakob Truelsen, as well as discovering the original issue. The 4k tip was also his idea. Prior to this I was puzzled as to why the function was working fine (didn't know it all depended on buffer size).