3

I have a "long" script I want to execute in a PHP page, and I want its output to be 'refreshed' as soon as the script outputs something. I've read plenty of solutions like questions 4706525, 9182094, 8882383, PHP Flush Manual but it's not working as expected in my case!

My test script:

#!/bin/bash

echo "This is a test script"
echo "Sleeping"
sleep 30
echo "Done"

Executable permission is set for www-data.

My PHP page:

<?php
@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
@ini_set('output_buffering', 0);
@apache_setenv('output_buffering', 0);

echo "Here<br>";
flush();
$cmd = "../test.sh";
$pipes = array();
$descriptors = array(
             0 => array("pipe", "r"),
             1 => array("pipe", "w"),
             2 => array("pipe", "w"),
             );
echo "Starting process<br>";
flush();
$process = proc_open($cmd, $descriptors, $pipes, realpath('./'), array());
echo "<pre>";
if (is_resource($process)) {
    while ($s = fgets($pipes[1])) {
        print $s;
        flush();
    }
} else {
    print "Cannot create process\n";
}
echo "</pre>";
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
?>

NB. My test script, test.sh, is in a directory above the PHP page, thus ../test.sh. Not that that changes anything. But it's not a typo.

My php.ini has those (although, I wasn't too keen at changing that server wide, but I wanted to test if that was the issue):

zlib.output_compression = Off
output_buffering = Off

I use LAMPP.

If I run the PHP page in a terminal,

$ php test.php

It works fine: I immediately get "This is a test script" and "Sleeping", and after a while, "Done".

If I load the page in my browser, it does not work: it waits until test.sh has completed before outputting anything.

Edited: If I add echo str_pad('',4096)."\n" in the loop, then it works. However, this fix suggests that for a reason I do not understand, output buffering is still set to its default value (4096) and not off as I tried to configure.

while ($s = fgets($pipes[1])) {
  print $s;
  echo str_pad('',4096)."\n";    
  flush();
}

Furthermore, this solution is not perfect because , in reality, it adds spaces to the output.

I am looking for a solution that

  • refreshes output of the PHP page
  • does not modify php.ini
  • does not modify the output

Thanks!

Community
  • 1
  • 1
user1381
  • 506
  • 1
  • 5
  • 19
  • The behaviour you're trying to achieve depends on many factors. I assume you read http://php.net/flush page. What browser are you using? Try adding newlines after `print $s` in the loop so it'd be `print $s . "\n"`, see if that helps. – marekful Aug 28 '15 at 09:59
  • There is one caveat that you should be aware of. PHP’s output_buffering setting has two possible values. One is to indicate whether it is On and the second is it to indicate the maximum size of the buffer. If output_buffering is set to 1, then you might not be able to see your content or browser loading indicator rotating until PHP code execution is finished. This is because having output_buffering on 1 means that we have enabled it but haven’t specified a maximum size, so in this case PHP buffers can hold data up to the number in the memory_limit setting. Source:http://www.sitepoint.com/ – sanderbee Aug 28 '15 at 10:04
  • @marekful no, that does not change anything (and honestly, I don't think why it should, as it's only modifying the display). – user1381 Aug 28 '15 at 11:19
  • @sanderbee well output_buffering is Off in my case. So there's no buffer size. So, that doesn't solve my issue either, unless I misunderstand your point. – user1381 Aug 28 '15 at 11:24
  • 1
    Regardless of server side buffering or compression, the browser/agent may employ some buffering strategy. I proposed the newline based on PHP's flush documentation, which mentions it could help (though somewhat seem outdated as it refers to Netscape). Also, look at the first entry under 'User Contributed Notes'. It claims to have a tested solution which sends content line by line to the browser which displays it line by line as they are being sent. In that solution 4K of newlines are sent to achieve the desired result. Maybe you should give it a try. (`str_pad('',4096)."\n";`) – marekful Aug 28 '15 at 11:26
  • @marekful indeed adding str_pad('',4096)."\n"; works!!! But that's strange, because 4096 is the default value for output_buffering. And I changed my php.ini to output_buffering = 0. I added print "output_buffering = " . ini_get('output_buffering'); and it displays output_buffering = 0. It's just like it is not using it?! Additionally, using str_pad(...) is not the perfect solution, because then if you run the page via php test.php, you'll indeed see plenty of spaces. – user1381 Aug 28 '15 at 12:12
  • There are potentially a lot of things between the output of PHP and the network socket. Telling apache not compress the output does not bypass mod_deflate (and hence any buffering), it merely tells it not to compress. And the above is not going to work on anything other than a client connecting directly to apache running mod_php - you didn't say if that was the architecture here. – symcbean Aug 28 '15 at 12:24
  • @symcbean no, the client is not expected to be on the same host that the one running mod_php – user1381 Aug 28 '15 at 14:14

1 Answers1

0
    ob_flush();
    flush();

Flushes the output to the web server/browser

Edmunds22
  • 715
  • 9
  • 10