Current time: 09-02-2015, 01:32 AM Hello There, Guest! (LoginRegister)

Post Reply 
PHP Progress Bar
05-10-2010, 08:47 PM
Post: #1
PHP Progress Bar
I've been trying for days to make a progress bar using just PHP. I've learned that having gzip enabled disrupts the functions of ob_flush() and flush(), so I had Dreamhost disable gzip for the domain I needed it on. Still didn't work. I've tried many MANY different scripts, here's the most basic:

Code:
<?php
ob_start();
for($i=0; $i<5; $i++) {
echo $i."
";
ob_flush();
flush();
sleep(1);
}
ob_end_flush();
}
?>
This should print the number of the current iteration of the for loop, once a second, for 5 seconds. What it does instead is load the page for 5 seconds, then print all the numbers at once.

I looked into my php.ini and saw that the output_buffering value was set to 4096, which I thought to mean that I had to have at least 4096 bytes in the buffer before it'd print to the page. I tried constructing a variable that was 4096 bytes long, appending it to the output buffer as a javascript tag so it wouldn't actually show up on the page (but it was in the source), then doing ob_flush() and flush()...STILL didn't work.

I'm totally baffled here. I really need to know what I can do. Printing my $_SERVER array reports:

[HTTP_ACCEPT_ENCODING] => gzip, deflate

And so does my other subdomains and my main domain, so I think I missed something.

I'd like to avoid setting up my own php.ini, but if it's unavoidable I will do it.
Find all posts by this user
Quote this message in a reply
05-18-2010, 02:03 PM
Post: #2
RE: PHP Progress Bar
(05-10-2010 08:47 PM)HaLo2FrEeEk Wrote:  I've been trying for days to make a progress bar using just PHP. I've learned that having gzip enabled disrupts the functions of ob_flush() and flush(), so I had Dreamhost disable gzip for the domain I needed it on. Still didn't work. I've tried many MANY different scripts, here's the most basic:

Well, the first thing is that last close brace should be a "?>", but I assume that's a cut-and-paste error.

Second, you're creating an output buffering context when you should be tearing them down. This works:

Code:
@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);

for($i=0; $i<5; $i++) {
echo $i."
";
flush();
sleep(1);
}

Thanks to comments in http://php.net/manual/en/function.flush.php for that tip.

That said, you're also forgetting about the browser itself. Most browsers won't render anything until they get a significant chunk of content.

The working progress bars that I've seen involve JavaScript polling the server once a second, obtaining the current level of progress, and updating the bar using DOM requests. Asynchronous JavaScript is your friend. For a dirt simple approach, you could also use a meta refresh once per second.
Find all posts by this user
Quote this message in a reply
05-18-2010, 08:42 PM
Post: #3
RE: PHP Progress Bar
(05-18-2010 02:03 PM)dgatwood Wrote:  Well, the first thing is that last close brace should be a "?>", but I assume that's a cut-and-paste error.

I assume you meant the extra '}' that I had in there, I think that was left over since this was part of a function, but I didn't copy the actual function line. My bad.

(05-18-2010 02:03 PM)dgatwood Wrote:  Second, you're creating an output buffering context when you should be tearing them down. This works:

Code:
@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);

for($i=0; $i<5; $i++) {
echo $i."
";
flush();
sleep(1);
}

Thanks to comments in http://php.net/manual/en/function.flush.php for that tip.

That said, you're also forgetting about the browser itself. Most browsers won't render anything until they get a significant chunk of content.

The working progress bars that I've seen involve JavaScript polling the server once a second, obtaining the current level of progress, and updating the bar using DOM requests. Asynchronous JavaScript is your friend. For a dirt simple approach, you could also use a meta refresh once per second.

Thanks! I'll try the code you sent me, hopefully that works.

And I understand that the browser will only display after it's buffer reaches a certain capacity. For internet explorer I thnk it's 256 bytes. 256 bytes is much better than the 64KB that I had to output before to get this to work, I can definitely print 256 bytes to the page per loop.

And finally, I am using Javascript to update the progress bar, simply by sending this javascript code:

document.getElementById('progress_bar').style.width = ##%

And I increase the number each loop using (100 / the number of items) + current width, that allows me to make it grow perfectly. I'm lucky in that the task that takes a long time is actually up to 30 individual tasks running in their own loop, not one long task, so I won't have to poll the server for completion status, I'll just output the updates for the progress bar and flush the buffer at the end of the loop in my existing code.

Thanks again!
Find all posts by this user
Quote this message in a reply
05-21-2010, 08:42 PM (This post was last modified: 05-21-2010 10:13 PM by HaLo2FrEeEk.)
Post: #4
RE: PHP Progress Bar
Sorry for the double post, but I feel I should tell you. I tried the code you posted, dgatwood, and it didn't work. Copying it in and not making any changes at all resulted in nothing being printed to the page at all, removing the @ symbol from before the apache_setenv function resulted in an error:

Fatal error: Call to undefined function apache_setenv() in [filename removed] on line 1

Commenting out that line caused the page to "load" for 5 seconds then print all 5 numbers at once. Not what I wanted, but you said it worked, and I assume you have Dreamhost. What do you think I'm doing wrong?

Edit: I read that apache functions, like apache_setenv, will not work when you're running PHP as CGI, which I am. I'd rather not change it because I might install Gallery on this domain in the future (to save me having to write my own, which is what I was planning on doing.) Is there an alternative? I emailed tech support and they said that Apache gzip was turned off for this particular domain, so it should be working.
Find all posts by this user
Quote this message in a reply
05-23-2010, 08:28 PM
Post: #5
RE: PHP Progress Bar
There's no available alternative to running PHP as CGI. apache_setenv only exists when you're using mod_php (the PHP Apache module), which we don't have available on shared hosting for security reasons.
Find all posts by this user
Quote this message in a reply
05-27-2010, 01:07 PM
Post: #6
RE: PHP Progress Bar
So the only way I've gotten this to work is by outputting 64kb or more per iteration. With 30 iterations that's almost 3MB that I'm printing. It's all hidden Javascript that I'm printing, but it makes the pages pretty heavy. At least it works though.
Find all posts by this user
Quote this message in a reply
Post Reply 


Forum Jump: