After several hours of researching end experimenting, I think I finally came up with a way for a PHP script to display a page, close the connection to the browser, and then to continue processing. The idea is that I can add some potentially lengthy processing to the script by executing it after the browser has closed the connection, but to a visitor, the page appears to load quickly.
I experimented with PHP’s register_shutdown_function, but that doesn’t really do what I need (unless running < PHP 4.0.3). Evidently PHP doesn’t have any way to close STDOUT, like other languages do.
The trick is in sending a Connection: close and Content-Length header. Once a client has received the specified number of bytes, it will close the connection, even though the script may continue. Unfortunately, that means that you need to know the length of the page before displaying it. That can be handled with output buffering, but does make the solution less than ideal.
Here is an example that works for me using PHP 5.1.6.
<?php $start_time = microtime(true); function bclog($message) { global $start_time; $fh = fopen('/tmp/logfile', 'a'); $elapsed = microtime(true) - $start_time; fwrite($fh, "$elapsed - $message\n"); fclose($fh); } header('Content-type: text/plain'); header('Connection: close'); ob_start(); for ($i = 0; $i < 1024; $i++ ) { echo "#"; } bclog("I'm done outputting my normal content"); // Figure the size of our content $size = ob_get_length(); // And send the content-length header header("Content-Length: $size"); // Now flush all of our output buffers ob_end_flush(); ob_flush(); flush(); sleep(5); bclog("Now I'm done with all of my post-processing - FYI, content length was $size"); ?>
If you hit that page in a browser, you will notice that the browser displays the content and is done right away. However, you can tail that logfile, and see something like this:
0.0002360343933 - I'm done outputting my normal content 5.0019490718842 - Now I'm done with all of my post-processing - FYI, content length was 1024
It is not an ideal solution, but I think that is about as good as it is going to get
You may want to turn off gzip encoding if this is not working for some applications.
With apache 2.0, I did this in my .htaccess file:
BrowserMatch .* no-gzip
See http://httpd.apache.org/docs/2.0/mod/mod_deflate.html for more info
Also, I had reports of this rendering a blank page in at least one older browser (IE6 IIRC). That may be due to the mod_gzip as mentioned by Gabe.
I know why they always said you were a genius there at flying-j. This will help me in many things that I am doing.
Steven,
Thanks. I wish that PHP had a better way of doing this, because I’m not 100% confident that this method works for all browsers. Another way that I’ve done it is to exec() a second PHP script in the background
ie:
This seems to work the first time I call it – or if i leave it for a while, but it takes a long time (the time of the sleep) if i call it a twice in a row.
Have I done something silly, or are you guys getting something similar?
Thanks for your help
All the best,
Ash
PS. I am using the Zend framework