Search code examples
phpresourcesstreampipeproc-open

Connect pipes of processes in php


I would like the output of one process created with proc_open to be piped to another one created with proc_open (in php). For example. In bash I can do:

[herbert@thdev1 ~]$ cat foo
2
3
1
[herbert@thdev1 ~]$ cat foo | sort
1
2
3
[herbert@thdev1 ~]$ 

I would like to simulate this in php using proc_open (instead of shell_exec) in order to have control over return-codes, pipes, etc. So I want something like this:

$catPipes=array();
$sortPipes=array();
$cwd = '/tmp';
$env = array();
$catProcess = proc_open("cat foo", array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w")
    ), $catPipes, $cwd, $env);

$sortProcess = proc_open("sort", array(
    0 => array("pipe", "r", $catPipes[1]),
    1 => array("pipe", "w"),
    ), $sortPipes, $cwd, $env);

echo stream_get_contents($sortPipes[1]);
fclose($sortPipes[1]);
//proc_close(this) ... proc_close(that) ... etc

Would someone know how I can simulate the "|" of bash in php, i.e. connect the second descriptor of the cat-process to the first descriptor of the sort-process? Any help would be appreciated! But please do not redirect me to shell_exec, as I want to be able to check exit-codes and log errors :).

EDIT:

My needs-to-work-business-solution btw is:

while(!feof($searchPipes[1])) fwrite($lookupPipes[0], stream_get_line($searchPipes[1], 40000));

Which is basically what the OS would do, but I do not want to my own pipe-management, as I have a kernel/posix for that, and let's be honest, it's not 1976 :)


Solution

  • Yes, you can -- but i think you have to define this the way round. That you can use the STDIN of "sort" as STDOUT pipe for "cat". Have a look at the following, which works for me:

    <?php
    
    $txt = "a\nc\ne\nb\nd\n";
    $fh  = fopen('data://text/plain;base64,' . base64_encode($txt), 'r');
    
    $sort_pipes = array();
    $sort_proc  = proc_open(
        'sort',
        array(
            array('pipe', 'r'),
            STDOUT
        ),
        $sort_pipes
    );
    
    $cat_pipes = array();
    $cat_proc  = proc_open(
        'cat',
        array(
            $fh,
            $sort_pipes[0]
        ),
        $cat_pipes
    );
    

    In the first two rows i defined a data stream from a text string that i do not have to rely on a file somewhere in the filesystem. Note, that i have a list of unsorted characters stored in the data stream (a, c, e, b, d). Running the script above should return a sorted list to STDOUT.

    Note, that you can specify resources as descriptors, too. In this case you must omit the array notation, so:

    STDOUT
    

    instead of

    array(STDOUT)
    

    etc.

    Btw.: you can even write directly to a file specified by a file name. You can find further information on the descriptor specification in the manual entry for proc_open at http://en.php.net/manual/de/function.proc-open.php

    EDIT

    The other way works too, of course: you can also write "cat" to an STDOUT pipe array('pipe', 'w') and use $cat_pipes[1] as STDIN for "sort". :)