13

Could someone please save these 2 files and run them and tell me why I get the error " ob_flush() [ref.outcontrol]: failed to flush buffer. No buffer to flush". I tried googling around and it says that I have to use ob_start(); but when I do then it doesn't print out line by line, but rather returns the whole object from the FOR loop when it has completed. I'm kinda new to PHP so I'm not sure where else to look..

test_process.php

// This script will write numbers from 1 to 100 into file
// And sends continuously info to user
$fp = fopen( '/tmp/output.txt', 'w') or die('Failed to open');
set_time_limit( 120);
ignore_user_abort(true);

for( $i = 0; $i < 100; $i++){
    echo "<script type=\"text/javascript\">parent.document.getElementById( 'foo').innerHTML += 'Line $i<br />';</script>";
    echo str_repeat( ' ', 2048);
    flush();
    ob_flush();
    sleep(1);
    fwrite( $fp, "$i\n");
}

fclose( $fp);

main.html

<html>
    <head>
        <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript" charset="utf-8"></script>

        <style type="text/css" media="screen">
            .msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
            .new{ background-color:#3B9957;}
            .error{ background-color:#992E36;}
        </style>

    </head>
    <body>

        <iframe id="loadarea" width="1024px" height="768px"></iframe><br />
        <script>
            function helper() {
                document.getElementById('loadarea').src = 'test_process.php';
            }
            function kill() {
                document.getElementById('loadarea').src = '';
            }
        </script>

        <input type="button" onclick="helper()" value="Start">
        <input type="button" onclick="kill()" value="Stop">
        <div id="foo"></div>


</body>
</html>
  • Check your config in php.ini about output buffer, it can be enabled to on_start automaticly. Another way to check if it enabled is using ob_end_flush() at the start and remove all the flushes. Also you can check with a `phpinfo();` – dvicino Feb 07 '12 at 18:59
  • Hey, thanks for your response. I tried to enable output buffering but then it returns the whole for loop object instead of line by line... –  Feb 07 '12 at 19:12

3 Answers3

31

You only need ob_flush() if an output buffer is active (for example by ob_start(), or by configuration settings). If you haven't, just remove the ob_flush(). Or you can make it conditional:

 if (ob_get_level() > 0) {ob_flush();}
John
  • 1
  • 13
  • 98
  • 177
Wrikken
  • 69,272
  • 8
  • 97
  • 136
  • Hi, thanks for your response. I tried to take out ob_flush() but then it still returns the whole for loop object only once it's completed processing, instead of returning each line by line as it goes. –  Feb 07 '12 at 19:01
  • Keep in mind caching before displaying can happen anywhere on the route between you & the UA: in PHP, in the webserver, in any proxy or hop on the network, on your local network, on your local computer, in your local browser. At most, you can hint (and if there's no output buffer active `ob_flush` won't do you any good), if you _need_ it, you shouldn't use HTTP but some other direct socket connection. – Wrikken Feb 07 '12 at 19:06
  • but for some reason it works when ob_flush() is there, but it also throws that error, so it would be perfect if that error just went away lol –  Feb 07 '12 at 19:08
  • Only when the error displays, or also when `display_errors` is off? Because if it's the first, it's most likely just the amount of non-empty content which triggers a flush somewhere else. – Wrikken Feb 07 '12 at 19:15
  • when i turn display_errors off then it returns the whole object again –  Feb 07 '12 at 19:23
  • THen, I'd change the `echo str_repeat( ' ', 2048);` to something like `echo '';`, or any other type of non-whitespace & non-intrusive output. – Wrikken Feb 07 '12 at 19:25
  • I replaced that line with display_errors turned off and then it still returns the whole object instead of line by line –  Feb 07 '12 at 19:39
  • aha! so short and sweet :) end of research for the current piece of code today! – Anupam Rekha Feb 19 '16 at 19:05
14

I think you are confusing ob_flush() with flush(). While ob_start() and ob_flush() handles a PHP internal output buffer that catches all outputs, flush() is the normal function that flushes STDOUT like in other programming languages.

Example:

<?php
ob_start();
echo "Foobar\nFoobar\nFoobar\n";
// Nothing printed yet
ob_flush(); // Now it is printed.

echo "Foobar\n"; // Printed directly, because contains a line ending.

echo "Foobar"; // Not printed, because normally buffers are flushed on line endings
flush();  // Printed.

EDIT:

Your output is not printed, because your webserver may buffer the contents. Try to turn off compression and output buffering:

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

Please also keep in mind, that Safari and Internet Explorer have an internal 1K buffer. So you need to add 1 KB of padding data (like spaces), to make them render.

EDIT 2: Your implementation is broken. You want to poll your data with ajax. Use jQuery on the client side:

<div id="counter">0%</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
<script type="text/javascript">
function doPoll(){
    $.post('script-that-returns-stuff.php', function(data) {
        $("#counter").html(data);
        setTimeout(doPoll,5000);
    });
}
doPoll();
</script>

Then in script-that-returns-stuff.php:

<?php
$file = explode("\n", file_get_contents("/tmp/output.txt"));
$last_line = $file[count($file)-1];
echo $last_line."%";
iblue
  • 29,609
  • 19
  • 89
  • 128
  • Hi - thanks for your response. I tried your suggestion but for some reason it doesn't return line by line but rather it returns the entire for loop result only after it has completed processing. –  Feb 07 '12 at 19:03
  • Then implement it in a clean way. Create a JavaScript that polls the server every second and a PHP script, that returns the number of lines in the file. Then display it on the client side. – iblue Feb 07 '12 at 19:10
  • Ok, see Edit 2 for an AJAX implementation. It polls every 5 seconds. To poll more often, just decrese the number in `setTimeout` – iblue Feb 07 '12 at 19:22
  • hey I tried it but with a while or for loop it doesn't flush, it just returns the processed result of the loop instead of the result each time it's looped as it goes –  Feb 07 '12 at 19:34
  • The 1K buffer was the mysterious problem in my implementation of an EventSource loop in PHP. I was able to overcome it by adding `echo str_repeat(" ", 1024), "\n";` to the beginning of the loop, and suddenly, everything worked! – Jacob Pritchett Mar 04 '13 at 18:38
2

Where is ob_start()?

ob_flush flushes the output buffer to your file handle. Maybe you have it wrong.

An example:

ob_start(); //start output buffering
echo 'hello world'; //not outputed
ob_flush(); //sends the output buffer so displays hello world.

manual

Iznogood
  • 12,447
  • 3
  • 26
  • 44
  • Hi, thanks for your response. I didn't include it in the code above because when I do then it doesn't return line by line but rather it returns the entire for loop result only after it has completed processing. –  Feb 07 '12 at 19:00