ob_start(), ob_flush(), flush(), set_time_limit() – Give user feedback during execution

So every once in a while I want/need to write some code that may take some time to execute. The problem is because it takes so long to execute, I need to make sure to give the user some feedback during this process so they know the page is working. If I hit submit in a page and nothing changes in my screen after 20-30 seconds I assume something is wrong. For example, I wrote a php page that would query multiple users in one db, then for each user load their xml data from another db and parse out the information to return a report for all users in the date range. Depending on the date range, this could take a few minutes to complete. This is where the php output buffer comes into play.

The idea is you start the output buffer with the php ob_start() function,  print/echo stuff to the browser (but it won’t be displayed since it’ll be kept in the buffer), finally flush the buffer so it send it’s contents to the browser. You repeat this each time you want something displayed in the browser. Here’s an example, it’s very similar to the examples you can find the the php.net manual covering output buffers.

<?php
 
// start output buffer
if (ob_get_level() == 0) ob_start();

for($i=0;$i<70;$i++)
{
    echo 'printing...<br />';
    print str_pad('',4096)."\n";
    
    ob_flush();
    flush();
    usleep(30000);

    // depending on what you are doing, you may or may not need this
    set_time_limit(30); 

}
 
?>

First you start the buffer, then loop 70 times and echo a str. I had to add the str_pad() part to make this work on Google Chrome and I won’t try to act like I can explain why… I probably read it somewhere :p. Then we do ob_flush() and flush(), both are needed for this to work. For this example a usleep() call was added to make each page take a little bit longer to execute so we can see this example work. Finally, not needed for this example, but was needed for my report since it took so long to execute – set_time_limit(). I believe the php’s default time limit for executing a page is 30 seconds, anything over that and you will get a php error saying your script is taking too long and has been stopped. To solve this, I reset my timer to 30 seconds on every loop since each call shouldn’t take more than 30 seconds. You can set the time limit to never expire but that could be bad and I wouldn’t recommended.

That’s all the code you need to make this example work. Depending on what you are working on: start the buffer, then just print/echo whatever needed and flush to display on the screen then continue to the next loop/section. If you have any questions, comments, better ways of doing this, or improvements please feel free to comment!

16 thoughts on “ob_start(), ob_flush(), flush(), set_time_limit() – Give user feedback during execution”

  1. well I tried too many codes and by far none of them worked. I tried yours as well but no success. Just wondering if there is any way to make it work.
    I’m a little bit suspicious about mod_gzip and mod_deflate.

  2. I think you may be correct. I turned on mod_deflate on my Apache 2.x config and output buffering stopped working the way it was intended when I tried IE. I assume for gzip or deflate to work correctly, it needs to get the entire output, compress it, and send it to the browser. I wonder if you could change the behavior through ini_set (http://us3.php.net/ini_set) assuming that’s what you want. Anyone have any thoughts?

  3. Great example, and it was super helpful for ironing out the Google Chrome issue (I had the same one). I would like to point out though, that it looks like wordpress replaced your quotation marks and elipsis with fancy typographic versions (” and …), as well as the two single quotes in the first str_pad() argument. Try wrapping that block in tags.

  4. That was exactly the solution I was looking for. Thanks. Couldn’t figure out why it would work in firefox and not chrome! 😛

  5. Great post! What I discovered is that Chrome (and likely Safari) will only stream the buffer if you end the printed line with a , as you did in your example. If you replace it with say a \n (newline), it no longer works. That stuck me for awhile before I figured it out.

  6. Ive been trying to figure this S**T out for hours!!!!! I couldnt get the data to flush() properly this whole time! And simply the str_pad fixed it!

    Thank you dude!

    Can someone explain why the str_pad works??

  7. As an addition: i would use ajax or at least a hidden iframe loading this stuff in the background rather than output it direct to the browser. It’s much more userfriendly.
    By the way: This sounds for me like an ugly workaround and it’s surely not best practice doing it this way. I avoid such things like the plague and i try to take this into account when planning a website.

    1. Hey Johannes,

      I have a process I built at work that publishes a course and creates hundreds to thousands of individual xml files from a db. This process can take up to 10 minutes to complete. All of this is happening sever side so ajax is not an option. Instead of showing a message to the user that read “please wait” with no signs of activity for 10 minutes, I display the different processes that are taking place using output buffers to let the user know that the process is working. As each page is published I display that on the screen, as each lesson is published, I display this on the screen with the total number of lessons left to give them an idea of how much progress is left. I personally don’t see this as a hack and instead see this as a way to enhance my users experience.

Leave a Reply

Your email address will not be published. Required fields are marked *