PHP Output complete notification - php

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.

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

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.

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.

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.

Related

Firefox says the connection was reset but the script is running

I get this message(Screenshot below. The browser shows loading for almost 5-6 minutes and then it shows this.) while I run a PHP script which makes multiple API calls and DB updates but I can see that the script is still running by checking its effects(like it processed only 40 records when I see the message and when I check again I can see that the script processed 45 records and it keeps increasing.)
What can cause this problem?
Is it possible to make the browser stay loading until the script finish executing?
Try using this method: this is the solution for me
<?php
ob_end_clean();
header("Connection: close");
$size = ob_get_length();
header("Content-Length: $size");
?>

Would using header("Location: target"); avoid the execution of the rest of the code in the document

By using this:
$size = ob_get_length();
header("Content-Length: $size");
header('Connection: close');
ob_end_flush();
ob_flush();
flush();
Along ignore_user_abort(true); I get to receive with an ajax call a complete status and let the file handle server side without the user having to wait for a response for the contents of the file to be parsed.
I'd like to achieve the same but with a header("Location: target"); instead of header('Connection: close'); - so it looks as everything is finished but the file continues to parse after triggering header("Location: target");
What would be the right approach into letting the file to continue working but redirect the user with PHP.
By the way before five down votes in a row, the question is not duplicate of PHP Redirect User and Continue Process Script although the title seems to resemble this question.
what would be the right approach into letting the file to continue
working but redirect the user with PHP?
You're describing parallel processing with an immediate exit, which you'll have to fudge in PHP:
open the parent PHP page
spawn a new thread to another php file's functionality (in PHP, threading doesn't exist, so you'd have to use curl, or some other means of executing it)
redirect
exit()
think of step two in the same way as you would if you were firing off an ajax request about which you didn't care about the response. it would be done in parallel to your parent page.
no, it does not stop the execution but you can manually stop the execution with exit call

close ajax request from php before script ends

How do you close an ajax request from php before the script ends? Example: user requests php.php, which has the line: echo "phpphp", and after this line, the ajax request finishes and has the data "phpphp", but the PHP script keeps on running, without using processes or forking?
How do i implement this scenario using PHP? has the answer. Set Connection close and Content length headers, with the flushing.
ob_start();
echo "111";
header("Content-Length: ".ob_get_length());
header("Connection: close");
flush();
somescript();
Try streaming the PHP file, and once it hits a certain point, send a return to JS and that will close the JS connection and (perhaps?) allow this php file to continue?
Haven't tried it but it's worth a shot.
// set as plain text
header('content-type:text/plain');
// let it stream
ob_implicit_flush(true);
ob_end_flush();
exit or any alias of exit will stop script execution.
Take a look at output buffering - using ob_start() with ob_flush() or simply flush() might do the trick.

Closing a connection early with PHP

I've been trying to complete a script that sends the proper notification to a users browser to close the connection, but allows the server to keep processing a request. My code is based on what I've seen on:
http://www.php.net/manual/en/features.connection-handling.php#71172
and
close a connection early
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
ignore_user_abort(); // optional
header('Content-Encoding: none');
header("Content-Length: $size");
header("Connection: close");
// flush all output
ob_end_flush();
ob_flush();
flush();
sleep(5);
//just a test to see if the script continues to run
file_put_contents("trash/".date('dmY-H_i_s_1').".txt", "Some text.");
file_put_contents("trash/".date('dmY-H_i_s_2').".txt", "Some text.");
file_put_contents("trash/".date('dmY-H_i_s_3').".txt", "Some text.");
When I go to run the script, sometimes it will create the first file but not write the text to it. Sometimes it doesn't create any files. If I run the script with the return early code commented out, all three files are created just fine. Zlib compression is turned off. Any ideas?
The right way to do this is to use php in fastcgi mode and then you can use the function
fastcgi_finish_request();
it will do exactly what you need - it will close the connection to the browser but the rest of the script will continue to run to the end.
http://php-fpm.org/wiki/Features
It's much better than relying on output buffering.
You can set your extra processing to take place with register_shutdown_function.
Basically, you register something to take place after you script finishes and sends all output to the user. That way the script can keep running for a while after that doing whatever cleanup or what not is needed.
Update: My bad, that no long applies after PHP 4.1
The links you gave and the other stuff I found seems to state you are doing it right. Are you sure there isn't an error or script timeout going on? What does the PHP error log state?
ignore_user_abort() must have a non-null parameter for it to actually ignore user abort. Otherwise it just returns the current ignore setting without changing anything. so you'd need
ignore_user_abort(true);

PHP + gzip: close connection and continue executing

I am responsible for the backend portion of an API, written in PHP, which is primarily used by a Flash client. What happens right now is: the Flash client makes a call, the backend loads the necessary data, does any necessary processing and post processing, logging and caching and then returns the result to the client.
What I would like to have happen is return the data to the client as soon as possible, close the connection, and then do all the stuff that the client doesn't have to care about. This could make the API seem much more responsive. Following the suggestions here:
http://php.net/manual/en/features.connection-handling.php
actually works, except that I have to turn off gzip encoding in order to make it work, which isn't very practical. We use mod_deflate in apache, so a solution that works with that would be ideal, but I would also consider a different method to gzip our content if that is necessary.
It seems like there should be a way to let Apache know "I've sent you all the data I'm going to send," but I can't seem to find anything like that.
For those wondering, yes I can flush the results early, but the Flash client will not process them until the connection is closed.
You might try breaking it into two pages.
In the first page, do the necessary processing, then load the second page via curl, and die().
That would cause the first page to complete and close, independent of the second page processing.
ie:
Page 1:
<?php
// Do stuff
// Post or get second page...
// Send Data to client
die();
?>
Page 2:
<?php
// Do other stuff....
?>
See http://www.php.net/curl
There's a kind of hack to do this by placing the code you want to execute after the connection closes within a callback method registered via to register_shutdown_function();
#Theo.T since the comment system mangled the crap out of my code, I'm posting it here:
No luck. The following prints out the extra crap and takes the full execution time to close the connection when using mod_deflate:
function sleepLongTime() {
print "you can't see this";
sleep(30);
}
ob_end_clean();
register_shutdown_function('sleepLongTime');
header("Connection: close\r\n");
ignore_user_abort(true);
ob_start();
echo ('Text user will see');
ob_end_flush();
flush();
ob_end_clean();
die();
set_time_limit(0);
header("Connection: close");
header("Content-Length: " .(strlen($stream)+256));
ignore_user_abort(true);
echo $stream;
echo(str_repeat(' ',256));
#ob_flush();
#flush();
#ob_end_flush();
your_long_long_long_long_function_here();
this will tell the user to close the connection once all of $stream is received . but be careful not to echo anything before the header part u know :p
if you are sending binary data (swf) you might need to remove the '+256' and echo(str_repeat(' ',256)); but in this case the code 'might' fail if the data sent is les than 256 bytes .
Today I also met this case, after some tests around, I found this way works:
Two steps:
Make sure the php script output is not with gzip encoding, the solution can refer to this link:
<IfModule mod_env.c>
SetEnvIfNoCase Request_URI "\.php$" no-gzip dont-vary
</IfModule>
Add the above to .htaccess file under the prj web site, then avoid apache gzip it automatically.
As some people said at features.connection-handling.php,
set_time_limit(0);
ignore_user_abort(true);
// buffer all upcoming output - make sure we care about compression:
if(!ob_start("ob_gzhandler"))
ob_start();
echo $stringToOutput;
// get the size of the output
$size = ob_get_length();
// send headers to tell the browser to close the connection
header("Content-Length: $size");
header('Connection: close');
// flush all output
ob_end_flush();
ob_flush();
flush();
// close current session
if (session_id()) session_write_close(); //close connection
// here, do what you want.

Categories