Performing post-output script processing in PHP

Posted on January 18th, 2008 in General,Programming by Brandon

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

5 Responses to 'Performing post-output script processing in PHP'

Subscribe to comments with RSS or TrackBack to 'Performing post-output script processing in PHP'.


  1. on March 26th, 2008 at 8:47 pm

    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

  2. Brandon said,

    on March 26th, 2008 at 8:55 pm

    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.

  3. Steven said,

    on May 7th, 2008 at 8:16 am

    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.

  4. Brandon said,

    on May 8th, 2008 at 8:35 am

    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:

    exec("/usr/bin/php /path/to/second/script.php SANITIZED
             ARGUMENTS > /dev/null 2>&1 &");

  5. on February 8th, 2009 at 9:51 am

    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

Post a comment

Please copy the string cgLMJH to the field below: