[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