tags:

views:

86

answers:

4

I'm trying find a way to have PHP to indicate to the browser that all page output is complete. After the page is done we're running some statistics code that usually doesn't take to long but in case it does I don't want to have the users browser waiting for more data. This can't be done via JavaScript because it needs to work with mobile phones.

I'm already starting output buffering using

mb_http_output("UTF8"); 
ob_start("mb_output_handler");

to insure I don't have issues with my sites MB text (Japanese). I was hoping that ob_end_flush() would do the trick but if I place sleep(10); after the ob_end_flush() the browser waits an additional 10 seconds. Does anyone have any ideas about this?

UPDATE: Using jitters approach below I "ob_gzhandler" to get it working with gzip any one see any possible issues here?

//may be also add headers for cache-control and expires (IE)
header("Connection: close"); //tells browser that connection will be closed
ob_start();
ob_start("ob_gzhandler");
//page content
ob_end_flush();
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();

UPDATE AGAIN:

Please take another look at the code above. You need to do an ob_start(); before the ob_start("ob_gzhandler"); and then call ob_end_flush(); prior to calling ob_get_length() so that you get the correct gzip compressed size.

A: 

Use something along these lines

//may be also add headers for cache-control and expires (IE)
header("Connection: close"); //tells browser that connection will be closed
ob_start();
//generate your output
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
//continue statistic processing
jitter
I tried this and if I add something like sleep(20); after the flush(); the browser (FireFox 3.5) still shows the loading indicator for an additional 20 seconds.
AWinter
I finally got this working but only if I include header("Content-Encoding: none"); right below header("Connection: close"); any thoughts on why this might be?
AWinter
Any ideas on how to get this working with out setting header("Content-Encoding: none"); I'd rather keep gzip running when possible
AWinter
Setting encoding to none might prevent (e.g) apache from using mod_gzip which has its own buffer and thus could prevent the above script from working. For my script to work you might have to disable gzip for this script and/or disable apache output buffering.
jitter
I found a way to get this working with gzip. I updated my original post with the change so the code will be nice and pretty.
AWinter
A: 

I don't think there is a way to notify the browser that the output is complete, at least from the script that sends the output. If you use some other script that will monitor the output of your first script and use an iframe maybe then you might be able to do it.

The browser knows when the output is complete when the page is considered loaded. That is what the browser knows.

andreas
A: 

You could fork a new php process in the background and let that take care of the stats. Something like: shell_exec('php stats.php &');

The & at the end makes sure that it's run in the background, so even if the stats.php takes 20 seconds, the visitor won't notice it. You would probably need to pass data to the stats script, which you can do by passing in parameters, like this:

shell_exec('php stats.php -b '. escapeshellarg($_SERVER['HTTP_USER_AGENT']) .' &');

In stats.php, you'd use the $argv variable to get that data.

But I wouldn't do this if the statistics code doesn't take that long to run, since forking a new process for every page load like this has some overhead. I don't know what it is that makes the stats code take a long time to process, but another solution might be to insert the raw data into a database, and let a background job work on that data to create usable statistics. That could be done either by a cron job, or having a screen run in an infinte loop that processes the queue.

Anders Ekdahl
In PHP, if you want the new process to run in the background, you must also redirect stdout, e.g. `shell_exec('php stats.php > /dev/null `
Ben James
True, forgot about that. It's probably good to direct stderr to stdout as well, and then direct stdout to /dev/null.
Anders Ekdahl
A: 

Try to move your statistics code to a seperate function and call this function with an ajax call in the dom.ready or onload event in javascript code on your rendered page like in this meta code:

<html>
  <script type="text/javascript">
    dom.onready = Ajax.call(location.href + '?do_stats');
  </script>
  <body>...
</html>

The dom.ready event can be provided by jQuery or Prototype libraries. Downside is it will only work with js enabled.

Alternatively you could just record all needed information for the stats to a database and dispatch a script collecting the queued data from there and working on it in the background - eg by using cron.

hurikhan77
Unfortunately most Japanese mobile phones don't support javascript and in Japan especially they can usually account for 50% or more of your total user base.
AWinter
Ok, that important point was missing from your question. You should amend it.
hurikhan77
I did forget to mention that. Sorry.
AWinter