NYCPHP Meetup

NYPHP.org

[nycphp-talk] fun with proc_open

Hans Zaunere lists at zaunere.com
Mon Oct 10 20:38:27 EDT 2005



Dan Cech wrote on Wednesday, October 05, 2005 5:10 PM:
> Hi all,
> 
> Firstly, apologies for the long email, I couldn't think of a less
> verbose way to describe the issues I'm experiencing.
> 
> I'm in the midst of writing a little program for executing long-running
> processes in the background from php and monitoring their output as they
> run.
> 
> The problem I'm running into is that when I execute a process using
> proc_open and try to read any output from stdout and stderr I am running
> into some odd behavior.
> 
> This only seems to happen under windows, running the same script on my
> debian server works as expected.
> 
> The first read from stdout is fine, but then it doesn't get any more
> output from stdout until the end of the script.
> 
> Is there some issue with using stdout and stderr together in this way
> that I don't know about?
> 
> My test code looks like this:
> 
> <?php
> 
> // allow script to run for 5 minutes
> set_time_limit(300);
> 
> // maximum length of blocks to read from process
> define('FREAD_LENGTH',1024*10);
> 
> $cmd = 'php '.dirname(__FILE__).DIRECTORY_SEPARATOR.'sleep.php';
> 
> $descriptorspec = array(
>    1 => array('pipe','w'), // stdout
>    2 => array('pipe','w'), // stderr
> );
> 
> $process = proc_open($cmd,$descriptorspec,$pipes);
> 
> if (!is_resource($process)) {
>    echo 'failed'."\n";
>    exit(1);
> }
> 
> // don't block process stdout or stderr
> stream_set_blocking($pipes[1],0);
> stream_set_blocking($pipes[2],0);
> 
> // don't buffer stdout or stderr
> stream_set_write_buffer($pipes[1],0);
> stream_set_write_buffer($pipes[2],0);
> 
> while (true) {
>    // read process stdout
>    $stdout = fread($pipes[1],FREAD_LENGTH);
>    if (strlen($stdout) > 0) {
>      echo 'stdout:'. $stdout;
>      flush();
>    }
> 
>    // read process stderr
>    $stderr = fread($pipes[2],FREAD_LENGTH);
>    if (strlen($stderr) > 0) {
>      echo 'stderr:'. $stderr;
>      flush();
>    }
> 
>    // end when both pipes are closed
>    if (feof($pipes[1]) && feof($pipes[2])) {
>      break;
>    }
> 
>    // wait for more output
>    sleep(1);
> }
> 
> // close process stdout and stderr
> fclose($pipes[1]);
> fclose($pipes[2]);
> 
> // grab exit code
> $exitcode = proc_close($process);
> 
> echo 'exitcode:'. $exitcode ."\n";
> 
> // end of script
> 
> sleep.php is a simple script that outputs some data:
> 
> <?php
> 
> ob_implicit_flush();
> 
> echo('started'."\n");
> 
> for ($i = 1;$i <= 10;$i++) {
> 	sleep(1);
> 	echo($i."\n");
> }
> 
> fwrite(STDERR,'done'."\n");
> 
> // end of script
> 
> Running the first script from command line or web browser should output:
> 
> stdout:started
> stdout:1
> stdout:2
> stdout:3
> stdout:4
> stdout:5
> stdout:6
> stdout:7
> stdout:8
> stdout:9
> stdout:10
> stderr:done
> exitcode:0
> 
> On windows I'm seeing:
> 
> stdout:started
> stderr:done
> stdout:1
> 2
> 3
> 4
> 5
> 6
> 7
> 8
> 9
> 10
> exitcode:0
> 
> I've managed to get the same functionality using popen and redirecting
> stderr to a fifo or tempfile, but proc_open seems like the cleaner
> solution if I can get it to work the way I want.
> 
> Any ideas?

Since it works under Linux, it's probably a subtle difference in the
buffering behavior on Windows.  Changing the fread() calls to fgets() seems
to work, but unfortunately I can't say what's different on Windows that
makes this a factor.  It's likely blocking/buffering behavior.

---
Hans Zaunere / President / New York PHP
   www.nyphp.org  /  www.nyphp.com





More information about the talk mailing list