Wednesday, February 15, 2012

Progress bar for a lengthy PHP process

If you ask an average web-developer if it's possible to make a running progress bar on the web-page while it's loading(!), most of them will say "no". This is understandable as practically all web-pages do not exhibit such progress bars and are in a sense "static" - the whole web-site is dynamically generated from a database, but each page is static except for AJAX changes due to user actions. Here I will show how a lengthy PHP process can show a running progress bar while the page is loading. Of course, it doesn't make sense for pages loading within a second or three, but those import/export/dump/analyze/recode/generate scripts which takes ages.

The most trivial approach is to echo '.'; flush(); - it will display a dot for every loop iteration. Test this:

for ($i = 0; $i < 100; $i++) {
    echo '.';
    usleep(1000000*0.1); // total time 10 sec.

The output should be an increasing amount of dots progressively appearing on the screen during 10 seconds. We need to use flush(); in order to send the PHP output buffer out to the browser. Alternatively we could set ini_set('output_buffering', 0);
There is another trick we need to use sometimes, on some servers flush(); doesn't work alone - either Apache or browsers (particularly IE) are buffering the incoming HTML data as well. So I add echo str_repeat(' ', ini_get('output_buffering')); before flush(); to make sure the buffer is completely filled and being flushed automatically.
But we can improve the aesthetics of the output using some JavaScript. Remember, in the old days we were using write('Some HTML'); to generate some dynamic HTML by JavaScript while the page is loading(!)? So JavaScript can be executed immediately and not only onDOMReady.
So we build a progress bar using a container <div> and inside <div> of a different background color which will  change it's width on the commands from JavaScript while the page in loading(!). Everything is encapsulated into a single class.
Usage example:

require_once 'nadlib/class.ProgressBar.php';

echo 'Starting&hellip;<br />';

$p = new ProgressBar();
echo '<div style="width: 300px;">';
echo '</div>';
for ($i = 0; $i < ($size = 100); $i++) {

echo 'Done.<br />';

Download class:
I hope using this technique will make your waiting more pleasant. You can even spot potential problems if the progress bar stops for unusually long time or is going slower and slower. I once improved the performance by adding a database index after watching the progress bar behavior.
Disclaimer: this idea is inspired by TYPO3 Extension Manager.


kevstev01 said...

I found I had to add the following 2 lines in the class constructor in order that the progress bar would work

ini_set('output_buffering', '0');

Ilya Rogov said...

Great work!

Could you please post your code on Pastebin or somewhere else?

The link you have does not seem to be working.


Slawa said...

Download link has been fixed.

M. said...

Thank you so very much, just what I needed.

I do have a question/remark, for some reason this progress bar does not work on Opera Mobile, do you know why? It seems the output buffer does not get flushed?

(it does work on Opera Mini, FireFox mobile, Android browser)

Slawa said...

Sorry, not sure about Opera Mobile. Maybe it just defers JS execution to save CPU resources and battery? You can ask Opera developers.

M. said...

Thank you for your reply,

I found out why this does not work in Opera Mobile, the browser has an extension called "Delayed Script Execution" which is standard On in Opera Mobile.

The setting can be found and changed by going to: opera:config#Extensions%7CDelayedScriptExecution

jotorres1 said...

Oh wow.. by far one of the best progress bars class i've seen yet. Thanks for such great post.

MK said...

I completely agree with the above comment (from jotorres1).
Very helpful, nice to tweak - all in all very educational post.
Thank you very much.

Otelo said...

Excellent!! Thank you very much, this progress bar is awesome!!

However i've made some changes, instead of "for looping" i put the $p->setProgressBarProgress($x); inside a "while loop".

Inserting 1000 records.

For each mysqli insert statement, $x receives 0.1 (because 0.1 * 1000 records equals 100%).

So i have something like this:

$x = 0;
$num_records = 1000;
$calc = 100/$num_records;
while( $x < 100 )
// insert statement here
$x += $calc;

chandrakesh pandey said...

Where i have to add this code ....

chandrakesh pandey said...

Where i have to add this code ....

Fayiz MV said...

Great work.. absolutely brilliant.. have been searching for kinda progress bar for long time and found it now.

Thank you so much.

sathish said...

but this progress bar time is not dynamic na. is there any way to put a dynamic progress bar for page loading?

Karel said...

Hey Slawa this class is awesome! I need it to show some user feedback during a lengthy server process, and I put it to work with no pain!

I also needed to add a couple timers to show elapsed and (roughly guessed) remaining time. It's relatively accurate if a high number of iterations are involved.

Also added a switch to leave the progress bar on after 100% completion.

This link contains the patch for those two changes, in case somebody needs something similar and want to test/improve.

Thanks for your great job!

Ranjeet Singh said...
This comment has been removed by the author.
Ranjeet Singh said...

Great work :)
Please can u tell me how to use it in Cake php 1.3

Eduardo Silva said...

Hi first of all thanks a lot it work perfect.

Do you know a way to use it more than one in the same code.

Thanks a lot Eduardo

ayukawaa said...

I've modified it for my needs, use it like the original:

Multiple progress bars during long PHP script execution.

Lori Palmquist said...

This is really awesome! Thank you so much for sharing your wonderful work!

shels said...
This comment has been removed by the author.
php-matter said...

can you help me solve the issue with 'Undefined variable':

Trying to use (understand this Progress bar) and i have got the result:


Notice: Undefined variable: content in C:\xampp\htdocs\mcmtech\welcome-project\progress-bar\use-progress-bar.php on line 83

Geolynx Saemane said...

You can fixed it by removing the concatenate on the $content variable on its initialization.

Find this line

$content .=

replace with

$content =

Should get rid of the error hope this helps.