5

I am using PHP 5.3.4 with Apache 2.2.17 on a Windows 7 x64 system. I would like to have my PHP page output the result of a system call in real-time to the user's browser. To that end, I have configured output_buffering=Off in php.ini and created this code:

<?php
ob_implicit_flush(true);
ob_end_flush();
system('ping -n 10 www.google.com');
?>

The result of the ping is printed in real-time, but I also get a PHP diagnostic error and callstack at the top of my page that says:

Notice: ob_end_flush() [ref.outcontrol]: failed to delete and flush buffer. No buffer to delete or flush in index.php on line 3

What do I need to do to either correct or suppress this error?

Update If I change ob_end_flush() to $a = 1/0; I get a similar error and the output is realtime in all browsers. Is it something about the way the exception is printed that causes this to work?

hakre
  • 193,403
  • 52
  • 435
  • 836
PaulH
  • 7,759
  • 8
  • 66
  • 143

5 Answers5

6

some web browsers buffer the first x bytes before they start to render a page, under certain conditions.

try just outputting lots of whitespace first

goat
  • 31,486
  • 7
  • 73
  • 96
3

I have a solution that works, but it is non-performant and icky. I throw an exception, but hide the exception dialog.

<?php
    ob_implicit_flush(true);

    // Something about the way exceptions are thrown causes Firefox and Chrome 
    // to be able to display the results of the system call in real-time rather
    // than having to wait for the call to complete. So, I just hide the 
    // exception message. IE9 works with or without this.
    echo "<div style=\"display:none\">";
    $a = 1/0;
    echo "</div>";

    echo "<pre>";
    system('ping -n 5 www.google.com');
    echo "</pre>";
?>

To auto-scroll to the bottom of the page, I add some javascript:

<html><head>
<script language="javascript">
var int = self.setInterval("window.scrollBy(0,1000);", 200);
</script>
</head>
<body>
<?php
    // insert above php code here
    // stop scrolling when the execution finishes
    echo '<script language="javascript">int = window.clearInterval(int);</script>';
?>
</body>
</html>

EDIT

@Chris's answer shows a much better solution.

echo '<div style="display:none">';
for ($a = 0; $a < 768; $a++)
    echo ' ';
echo '</div>';
PaulH
  • 7,759
  • 8
  • 66
  • 143
1

Just add this to flush the buffer:

    if (eregi("chrome",$_SERVER['HTTP_USER_AGENT'])) {
       echo "<div style=\"display:none\">";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";    
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";    
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
          echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
       echo "</div>";
    }

It has to be inside the loop. or between two output you need to show in real-time.

AND here's the shorthand trick:

add this:

if (eregi("chrome",$_SERVER['HTTP_USER_AGENT'])) {
   echo "<div style=\"display:none\"></div>";                    
}
jftremblay
  • 74
  • 1
  • 9
0

In your code, the error is pretty easy to interpret.

You call ob_end_flush() on line 3, but (like the error says), there is no output to flush. In essence, line 3 is useless because no output has been sent, so deleting the line will fix the error. If this is incorporated into a larger file, you might need to keep ob_end_flush() because some output may already have been captured.

EDIT: Since you need to flush it, either:

a: add ob_start(); to the top of the file.

b: replace ob_end_flush(); with flush();

EDIT2: Since the first didn't seem to work, this is the best I can offer: How to echo output in real time, (before script finishes)?

Community
  • 1
  • 1
Grexis
  • 1,502
  • 12
  • 15
  • If I remove it the output is no longer realtime in Chrome 16 or Firefox 10. (Though, it does work in IE9) – PaulH Jan 06 '12 at 22:37
  • per your edit - With change A, it is not realtime in any browser. With change B it is realtime only in IE9. – PaulH Jan 06 '12 at 22:40
  • @PaulH Take a look at the answer to the question I linked. Similar, and probably the same problem. – Grexis Jan 06 '12 at 22:52
  • I have output buffering disabled. I know the browsers are capable of doing this because they will do it when the error is displayed. I've added linefeeds before the `system` call in various forms to no effect. `echo PHP_EOL; echo "
    "; echo "\r\n";`
    – PaulH Jan 06 '12 at 22:56
  • The only other thing I can think of, is to use `ob_start()` with a custom callback function to automatically flush the output. – Grexis Jan 06 '12 at 22:58
0

ob_end_flush() flushes the php output buffer, and requires an active output buffer created with ob_start().

I think you just want to call flush() to send the data to the client.

<?php
ob_implicit_flush(true);
flush();
system('ping -n 10 www.google.com');
?>
al01
  • 122
  • 1
  • 4
  • With this code, the output is not realtime in Chrome 16 or Firefox 10. (Though this does work in IE9) – PaulH Jan 06 '12 at 22:36
  • I think that the browsers (Chrome 16 and ff10) are caching the text before rendering it. I'd go with javascript+ajax, get one ping output at time and display it to the user. I'd be interested in a reliable way to force the page rendering too... – al01 Jan 06 '12 at 22:57
  • I can make it work perfectly if I'm willing to put up with an exception dialog at the top of the page. I don't think the browsers are incapable of doing this. – PaulH Jan 06 '12 at 22:59
  • It may work but there is no reliable solution as far as I know. I just said that a browser may be buffering the data before rendering it. See http://www.php.net/manual/en/function.flush.php – al01 Jan 06 '12 at 23:05