Wonder if there's a way for a PHP script returns and print immediately on the browser (via a web page) the result of various functions without waiting for the completion of the next.
Sample PHP code:
<? php
for ($i = 0; $i < 100; $i++) {
echo $i. ":". my_function(); / / takes a long time to run
}
?>
release:
0: value ... (the function of the cycle $i = 1 is still running)
1: value ... (the function of the cycle $i = 2 is still running)
and so on ...
you may use the flush() function to attempt to flush data back to the browser.
description from php.net:
Flushes the write buffers of PHP and whatever backend PHP is using
(CGI, a web server, etc). This attempts to push current output all the
way to the browser with a few caveats.
flush() may not be able to override the buffering scheme of your web
server and it has no effect on any client-side buffering in the
browser. It also doesn't affect PHP's userspace output buffering
mechanism. This means you will have to call both ob_flush() and
flush() to flush the ob output buffers if you are using those.
sample code below, tested on chrome 31.0.1650.57 (linux), Safari (6.0.4) (osx):
note: whether or not to show the flushed output is up to the browser (usually depending on the amount of data in the response). for example Safari (6.0.4) wants 512 bytes of data before outputting data to the browser. You can get around this by doing something like padding 512 characters to beginning of the output.
<?php
header( 'Content-type: text/html; charset=utf-8' );
echo str_repeat(" ",512); //pad the buffer with data (in case browser needs it)
while (true){
//write go to the browser every 2 seconds.. forever
echo "go...<br/>";
ob_flush();
flush();
sleep(2);
}
?>
more info here http://us1.php.net/manual/en/function.flush.php
Related
I have a script that runs for like 1 hour.
I have 2 webhosts, in one of them, I can see the output in realtime, (it loops a million times and echoes stuff) while on the other webhost, I have to wait until the script is finished running before I see any output at all.
How can I change the settings to show the output in realtime? I guess this is a php.ini thing.
Adding code: This code will output while executing in the first server, but not in the second server. Additional info, if i put the value of 1015 lower, the first server waits with outputting. So it seems that my first server is flushing every 1015 characters, while the second does not.
while ($a++ < 5) {
$b = 0;
while ($b++ < 1015) {
echo "H";
}
echo "<br><br>\n";
sleep(1);
}
On my second server, output buffering is off, and i tried turning on implicit_flush to no avail.
This is related to output buffering. If output buffering is turned on, the output is not sent to the client immediately but will wait until a certain amount of data buffered. This usually increases performance but will cause problem like yours.
The simple solution doesn't require changing any php.ini settings, but simply put this at the beginning of your script:
<?php
while (#ob_end_flush());
?>
This will flush and disable all output buffering at once.
(Source: http://www.php.net/manual/en/function.ob-end-flush.php#example-460)
You may also want to read the PHP manual on Output Control. The related php.ini settings are listed here.
If you want to make sure things are being sent (i.e. before a time-consuming task), call flush() at the appropriate times.
Edit:
In fact browsers may have their own buffers, especially when Content-Type is not explicitly set (the browser need to sniff the MIME-type of the file). If this is the case, you cannot control the browser's behaviour.
One way that might solve it is to explicitly set the Content-Type:
header("Content-Type: text/plain"); // if you don't need HTML
// OR
header("Content-Type: text/html");
But this is not guaranteed to work. It works on my Firefox but not Chrome.
Anyway, your final code should look like:
header("Content-Type: text/html"); // Set Content-Type
while (#ob_end_flush()); // Flush and disable output buffers
while ($a++ < 5) {
$b = 0;
while ($b++ < 1015) {
echo "H";
//flush(); // In fact because there is no significant delay, you should not flush here.
}
echo "<br><br>\n";
flush(); // Flush output
sleep(1);
}
Try calling flush periodically. If you are using output buffering, you may want to turn that off with a sequence of ob_end_flush or use ob_flush as well.
I have a ob_start() and a corresponding ob_flush(). I would like to flush a portion of data and continue executing the rest. Using ob_flush() didn't help. Also if possible rest needs to happen without showing loading in browser.
EDIT:
I don't want to use ajax
I have done this in the past and this is how I solved it:
ob_start();
/*
* Generate your output here
*/
// Ignore connection-closing by the client/user
ignore_user_abort(true);
// Set your timelimit to a length long enough for your script to run,
// but not so long it will bog down your server in case multiple versions run
// or this script get's in an endless loop.
if (
!ini_get('safe_mode')
&& strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE
){
set_time_limit(60);
}
// Get your output and send it to the client
$content = ob_get_contents(); // Get the content of the output buffer
ob_end_clean(); // Close current output buffer
$len = strlen($content); // Get the length
header('Connection: close'); // Tell the client to close connection
header("Content-Length: $len"); // Close connection after $len characters
echo $content; // Output content
flush(); // Force php-output-cache to flush to browser.
// See caveats below.
// Optional: kill all other output buffering
while (ob_get_level() > 0) {
ob_end_clean();
}
As I said in a couple of comments before, you should watch out for gzipping your content, since that will alter the length of your content, but not change the header about it. It also can buffer your output, so it won't get send to the client instantly.
You could try letting apache know to not gzip your content by using apache_setenv('no-gzip', '1');. But this will not work if you use rewrite-rules to go to your page, since then it will also modify those environment variables. At least, it did so for me.
See more caveats about flushing your content to the user in the manual.
ob_flush writes the buffer. In other words, ob_flush tells PHP to give Apache (or nginx/lighttpd/whatever) the output and then for PHP to forget about it. Once Apache has the output, it does whatever it wants with it. (In other words, after ob_flush it's out of your control whether or not it gets immediately written to the browser).
So, short answer: There's no guaranteed way to do that.
Just a guess, you're likely looking for AJAX. Whenever people are trying to manipulate when page content loads as you're doing, AJAX is almost always the correct path.
If you want to continue a task in the background, you can use ignore_user_abort, as detailed here, however, that is often not the optimal approach. You essentially lose control over that thread, and in my opinion, a web server thread is not where heavy processing belongs.
I would try to extract it out of the web facing stuff. This could mean a cron entry or just spawning a background process from inside of PHP (a process that though started from inside of script execution will not die with the script, and the script will not wait for it to finish before dying).
If you do go that route, it will mean that you can even make some kind of status system if necessary. Then you could monitor the execution and give the user periodic updates on the progress. (Technically you could make a status system with a ignore_user_abort-ed script too, but it doesn't seem as clean to me.)
this is my function
function bg_process($fn, $arr) {
$call = function($fn, $arr){
header('Connection: close');
header('Content-length: '.ob_get_length());
ob_flush();
flush();
call_user_func_array($fn, $arr);
};
register_shutdown_function($call, $fn, $arr);
}
wrap the function to be executed in the end, after php close the connection. and of course the browser will stop buffering.
function test() {
while (true) {
echo 'this text will never seen by user';
}
}
this is how to call the function
bg_process('test');
first argument is callable,
second argument is an array to be passed to 'test' function with an indexed array
Note : I don't use ob_start() at the beginning of the script.
I have an article explaining how this can be achieved using apache/mod_php on my blog here: http://codehackit.blogspot.com/2011/07/how-to-kill-http-connection-and.html Hope this helps, cheers
If you are using PHP-FPM:
ignore_user_abort(true);
fastcgi_finish_request();
Above two functions are the key factors which ignore_user_abort prevents error and fastcgi_finish_request closes client connection.
fastcgi_finish_request
This function flushes all response data to the client and finishes the request. This allows for time consuming tasks to be performed without leaving the connection to the client open.
not working on Apache.(PHP 5 >= 5.3.3, PHP 7)
Use:
header("Content-Length: $len");
..where $len is the length of the data to be flushed to the client.
I don't have the background to know when and where this is going to work, but I tried on a few browsers, and all returned instantly with:
<?PHP
header("Content-length:5");
echo "this is more than 5";
sleep(5);
?>
edit: Chrome, IE, and Opera showed this, while FireFox showed this is more than 5. All of them closed the request after that though.
How do i output to the browser before the script finishes executing?
For example, the code below will output all 100 "hi" at once, how do I make it so it will output as soon as that section of code is read/processed? For example: PHPBB3 forum shows installing process step by step.
<?php
for ($i = 0; $i <= 100; $i++) {
echo "hi";
echo "<br>";
}
?>
Call ob_implicit_flush() before your loop.
Note that this is not a guarantee (web server buffers, proxy buffers and web browsers who refuse to rerender are your enemy). It MAY help to echo some linefeeds (\n) as I seem to remember that there are browsers who will not rerender until they received a complete line.
This will not work at all if you use output buffering (e.g. for compression using gzip (if the gzip modul you use uses buffering which is not uncommon) or other reasons).
I have a php script that uses cURL and takes about 10-15 minutes to execute. What it does, it parses about 1000 pages looking for specific matches and throughout the script I have diagnostic messages echo'ed out, like "Going to the next page", "Found a match", "Error loading page" ... The way it works now (and the way that it's normal) is it executes for like 10 minutes and only then spits out all my custom messages.
I would like to be able to display those messages as they happen, not when the script is done executing. I was thinking something like AJAX would do it, but am not sure how it would work. Any tips are greatly appreciated. Thanks.
So, this is a old post but I found a solution for this. As I also have to make the same thing, output when the script is still running. Not any answer from here helped.
First of all, I am using Win32 server(production) and XAMPP as local for tests. This example is just a proof of concept and can be modified as you please.
<?php
ob_implicit_flush(true);
for($i=1; $i<=10; $i++){
echo "$i ...<br>";
for($k = 0; $k < 40000; $k++) echo ' ';
sleep(1);
}
?>
So, we open output buffer as implicit. Then we make a demo loop to count from 1 to 10 and display the values as they are been processed. Second loop will fill in the browsers buffer. And finally to check if everything is working well we make a sleep for 1 second. Otherwise the script will run too fast and we could not know if we achieved the goal.
Hope this helps !
You could create a staging table.
The PHP script could, instead of echo'ing the message, store them into a database table (possibly memory table for performance).
You could then periodically poll a seperate PHP script using ajax, which would query the table, and return any new messages to the client.
Use flush to immediately send output to the browser, by flushing the output buffer.
echo "foo";
flush();
echo "bar";
flush();
Actually you're looking for something like flush and ob_flush, however bear in mind that there are a lot of factors that can prevent your output from being flush'd as it happens.
From the flush documentation you'll get:
Several servers, especially on Win32, will still buffer the output from your script until it terminates before transmitting the results to the browser.
Server modules for Apache like mod_gzip may do buffering of their own that will cause flush() to not result in data being sent immediately to the client.
I'm using the #ob_flush() after every echo. In this example PHP_EOL creates a new line after $string
function output($string){
echo $string.PHP_EOL;
#ob_flush();
}
Basically, have your script write HTML output to a temporary log file. Then use ajax to periodically update the end-user's browser with the temporary log file. jQuery will make quick work of this.
Ajax is the only guaranteed way to get it to work on all browsers. Here is a quote from PHP's flush page.
flush() may not be able to override
the buffering scheme of your web
server and it has no effect on any
client-side buffering in the browser.
It also doesn't affect PHP's userspace
output buffering mechanism. This means
you will have to call both ob_flush()
and flush() to flush the ob output
buffers if you are using those.
Sounds to be like you have output buffering turned on.
Calling ob_end_flush() will print what's currently in the buffer, and turn off the buffer for the rest of the script execution.
You can use the flush() function to send all the content of the buffer to the client. http://php.net/manual/fr/function.flush.php
You could use both flush and ob_flush, reminding to set the content type header:
<?php
header( 'Content-type: text/html; charset=utf-8' );
for( $i = 0 ; $i < 10 ; $i++ ){
echo $i . '<br>';
flush();
ob_flush();
sleep(1);
}
Source: dermeister note in php.net ob_flush page.
Tested on Firefox 42.0 and Chrome 46.0
I have a php script that connects 10 different servers to get data. I want it to print the results of the 1st connection before the second one begins.
Using flush and/or ob_flush, you should get what you want.
Here is a quick demonstration :
for ($i=0 ; $i<10 ; $i++) {
echo "$i<br />";
ob_flush();
flush();
sleep(1);
}
Each second, a number will be sent to the browser, without waiting for the loop/script to end.
(Without both flush and ob_flush, it waits until the end of the script to send the output)
Explanation about why you need both, quoting from the flush page in the manual :
Flushes the write buffers of PHP and
whatever backend PHP is using (CGI, a
web server, etc). This attempts to
push current output all the way to the
browser with a few caveats.
flush() may not be able to override
the buffering scheme of your web
server and it has no effect on any
client-side buffering in the browser.
It also doesn't affect PHP's userspace
output buffering mechanism. This means
you will have to call both ob_flush()
and flush() to flush the ob output
buffers if you are using those.
If this doesn't work for you, taking a look at the comments on the two pages of the manual can give you a couple of pointers on "why it could fail"
ob_end_flush
http://us.php.net/ob_end_flush
This function empties the output buffer and disables output buffering. Everything after this function is send to the browser immediately.
Yeah, ob_flush should do it. I do this all the time with a LOOONG page, when I want to watch the progress of the operation.