I stumbled upon this function which promised to work across IE, FF & Chrome. But it does not work in Chrome. Is there a work around?
function buffer_flush(){
echo str_pad('', 512);
echo '<!-- -->';
if(ob_get_length()){
#ob_flush();
#flush();
#ob_end_flush();
}
#ob_start();
}
Here's how I got flush() working in a while loop in Chrome 12.0.742.122 with PHP 5.3.6:
echo("<html><body>");
while(1) {
echo(str_pad($my_string_var,2048," "));
#ob_flush();
flush();
}
Using a lesser str_pad value worked too, but it would take a bit longer for the first output to appear. If any of the other lines were missing, nothing would appear.
The "#" isn't strictly necessary, but it prevents the log from filling up with "nothing in the buffer" notices.
And of course if you have a pre-existing page, just make sure the <html> and <body> tags are in there; I was writing a page from scratch.
With flush()/ob_flush() you only send the output to the browser, but its still up to the browser, when it displays it. I assume, that chrome just waits, until it has enough data received to display a "useful" page, instead of some fragments.
Some suggestions anyway:
Avoid using # (especially if you don't know exactly, what it does)
If you don't call ob_end_*(), you don't need to call ob_start() again. Its inefficient
function buffer_flush(){
echo '<!-- -->'; // ?
ob_flush();
flush();
}
Some browsers (IE6 at the very least, and possibly chrome) require a certain amount of "useful" characters (i.e. not spaces) before outputting anything. In the case of IE6, it even is the compressed data's size that needs to be pushed.
function force_flush() {
echo "\n\n<!-- Deal with browser-related buffering by sending some incompressible strings -->\n\n";
for ( $i = 0; $i < 5; $i++ )
echo "<!-- abcdefghijklmnopqrstuvwxyz1234567890aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz11223344556677889900abacbcbdcdcededfefegfgfhghgihihjijikjkjlklkmlmlnmnmononpopoqpqprqrqsrsrtstsubcbcdcdedefefgfabcadefbghicjkldmnoepqrfstugvwxhyz1i234j567k890laabmbccnddeoeffpgghqhiirjjksklltmmnunoovppqwqrrxsstytuuzvvw0wxx1yyz2z113223434455666777889890091abc2def3ghi4jkl5mno6pqr7stu8vwx9yz11aab2bcc3dd4ee5ff6gg7hh8ii9j0jk1kl2lmm3nnoo4p5pq6qrr7ss8tt9uuvv0wwx1x2yyzz13aba4cbcb5dcdc6dedfef8egf9gfh0ghg1ihi2hji3jik4jkj5lkl6kml7mln8mnm9ono -->\n\n";
while ( ob_get_level() )
ob_end_flush();
#ob_flush();
#flush();
} # force_flush()
There are several components that can have impact on this issue.
Read carefully through the documentation of this function:
http://www.php.net/manual/en/function.flush.php
One solution I had was using Apache2 with mod-php (not as fcgi but as native apache-module) and Chromium. The result came immediately and the script was still running and sending more results.
After typing the following two code-lines every echo-command will immediately push the text to the whatever-PHP-backend:
ob_implicit_flush(1);
#ob_end_flush(); // set an end to the php-output-buffer!
But this php backend can have its own buffer. For example I run nginx as webserver and php is used by the fast-cgi module. Nginx itself has its own buffer ... and so on.
The Browser also can buffer the request. But as I experience Chromium (or Google Chrome) has a verry small or no buffer.
Please read the documentation of every function I mentioned to understand what they really do - but specially the documentation of flush().
Personal hint: Do not put extra characters into the output-buffer but read and understand the configuration of your server.
EDIT:
If you have gzip enabled the whole response from the server will be buffered.
I found that a content type header really makes it work in chrome after few trial and errors.
But i don't know why chrome does not flush otherwise.
after searching for more answers i read that chrome flushes as you expect only when a valid content type is set. fine.
Here is the code i experimented.
<?php
header('Content-Type: text/html; charset=UTF-8');
echo 'starting...';
flush();
echo 'to sleep...';
flush();
sleep(5);
echo 'awake';
if i do not include the content type header i get like the following in one shot after 5 seconds. so what we expect did not work.
starting...to sleep...awake is displayed and the script terminates.
where as when i gave the content type like the above with the subtype(charset) then
starting...to sleep... is displayed immediately and then after 5 seconds awake is displayed.
i am just blindly assuming that with respect to the content type header chrome shows the output.
Besides when i gave 'Content-Type: text/plain' or 'Content-Type: text/html' it did not work. it worked only with the subtype 'charset=[sometexthere]'.
were as application/json worked. and i did not experiment with more mimes.
The reason i am here is
i wanted to use readystate 3 in ajax response. it works fine except chrome and safari. since chrome is using webkit it is the same in both i think.
in other browsers including IE the flushing is working as expected and also the readystate=3 but in chrome and safari i just used the above workaround.
here is the screenshot of the readystate - responsetext from the above php script
in the image there a two sets of response the first one with readystate 3 and responsetext as empty when content type is not used.
in the second response you can see ready state 3 has responsetext with the expected output. this is when content type is used.
so... Chrome only knows.
when used str_pad
When you use string padding you can get more expected result. i tried with 1024 as the above answers suggested but only with content type set.
if padding is used and no content type is set then it did not work.
and
i had raised a question similar to this and i am going to add my own answer by linking this answer to that and back to back... so that it will be easy for users to get more details. hhmmm.
Related
I am trying to do this:
Display "a" for 1 second, clear screen display only "b" for 1 second, clear screen display only "c".
This is what I have so far, buts it's not working:
header("Content-type: text/html; charset=utf-8");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
set_time_limit(0);
ob_implicit_flush(1);
echo "a";
ob_flush();
ob_clean();
sleep(1);
echo "b";
ob_flush();
ob_clean();
sleep(1);
echo "c";
Output buffer doesn't work that way, it's a one way street. What has been passed to the browser has been sent from the server and you don't have access anymore to that data and you don't have any control over the data that the user has already received.
The only way to do this would be to send control characters to clear the screen, but those don't fall into the characters browsers will accept.
In theory you could send \x08 (backspace), but it will not work on anything else but something that allows using these ASCII control characters. Are you working with a terminal or a graphical browser? The first one might accept, the latter most unlikely ever would.
There is no screen. There is only output that is sent from your server (PHP runs on the server) to the browser.
The ob functions use output buffering. Using this technique, you can buffer the output (result of echoes and such) on the server, and even descard or modify it, before sending it to the client (the browser).
Your understanding of those functions is wrong, as is the way you are using them.
At best, the result could be that 'a' appears first, and 'b' would appear a second later. But there are a couple of issues. First of all, you don't start output buffering at all (using ob_start). Secondly, the server might already send the 'a' to the browser, but the browser will also see that it's only one letter, and that the response is still going on, so it will likely not display it. A half response is usually just an incomplete page, so browsers will also buffer the response they get in order not to display a bunch of garbage on screen. They will in most cases only show the response when it is received completely, or when the connection was broken before that.
So in short, this is not going to work. You will need either JavaScript or a meta redirect to fix this.
In a JavaScript-enabled browser, you might do this (no PHP needed):
<body/>
<script type="text/javascript">
// Get the body
var doc = document.getElementsByTagName('body')[0];
// Set its text.
doc.innerText = 'A';
// Replace it with another text after a 1000 milliseconds.
setTimeout(function(){
doc.innerText = 'B';
}, 1000);
</script>
I wouldn't rely on PHP code to do this and hope that it is timed correctly. Use the php to build your page and retrieve any data you need, and use javascript or jquery to do that you are trying to do. With javascript/jquery, you can dynamically set the html of a page without refreshing. Take a look over here
I' am using XMLHttpRequest 2 to get chunked response through it's onprogress event and displaying it in browser. Everything seems to work fine but I have a little confusion. Consider the following code:
<?php
echo "I' am sending ";
ob_flush();
flush();
echo "content to client.";
ob_flush();
flush();
This is pretty standard PHP way of sending chunked response to client. However I was expecting to get output as two different chunks, one containing first echo's result and other containing second's. But I' am getting a single combined chunk of both echo statements. When I put sleep(1) after first flush() then everything seems to work fine.
My question is that is there some deliberate wait on webserver side to wait for some other content before sending already received chunk or is there some thread scheduling mechanism that is forcing network layer of Apache to wait for it's turn.
Edit
I forgot to mention that I have disabled webserver's output buffering which means if I flush something from PHP it will get sent to browser.
Any help is highly appreciated.
Thanks.
See: http://www.icemelon.com/tutorials/20/Output_While_Script_Still_Running.htm
You may be seeing browser output buffering here. Try adding:
for($k = 0; $k < 40000; $k++)
echo ' ';
Right before you flush in order to fill up the browser's buffer and make it spit out your output.
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.
Final update
Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D
I'm trying to achieve fire-and-forget like functionality in PHP.
From php.net
<?php
ignore_user_abort(true);
header("Content-Length: 4");
header("Connection: Close");
echo "abcd";
flush();
sleep(5);
echo "Text user should not see"; // because it should have terminated
?>
This works if I open the script with a browser. (shows "abcd").
But if I open it with file_get_contents or some stream library it will wait for ~5 seconds and show the second text as well.
I'm using PHP 5.2.11 / Apache 2.0
Update
I seems there is some confusion about what I'm trying to accomplish.
I don't want to hide output using output buffers (that's stupid). I want to have the client terminate before the server starts a possibly lengthy process (sleep(5)) and I don't want the client to wait for it (this is what fire-and-forget means, sort off).
The use of output buffers is merely a side effect. I've amended the sample code without the use of output buffers.
What I don't understand is: why does this script behave differently when accessing it from the browser vs. fetching it in PHP with file_get_contents("http://dev/test.php") or some stream library? What I've seen in testing is that for instance stream_get_contents will actually block for 5 seconds before it returns any output at all, the is quite the opposite of what I want.
Update2
Some more results:
The browser somehow responds to the flush(). I can't figure out how to replicate this behavior with streams in PHP, my streams keep blocking.
I've tried fread and found that it behaves similar to stream_get_contents.
Specifying a maxlength has no effect, it will still block for ~5 seconds.
Changing the blocking mode has no effect (other than generating a bunch more calls to stream_get_contents()). It will wait ~5 seconds before returning anything.
stream_set_read_buffer has no effect (tested on a PHP 5.3.5 sever)
The second portion of text is showing up because you're stopping output buffering with ob_end_flush() and ob_end_clean(). When that happens PHP outputs content as normal. Try something like the following:
<?php
ob_start(); // turn on output buffering
print "Text the user will see.";
ob_flush(); // send above output to the user and keep output buffering on
print "Text the user will never see";
ob_end_clean(); // empty the buffer and turn off output buffering. your script should end here.
?>
It's important for ob_end_clean() to appear at the end of the script. It empties the buffer and does not send its contents to the user, thus keeping everything after ob_flush() hidden.
How do you access the script using file_get_contents? How do you access it with your browser? If you access the script without "http://", of course it will never get executed. Use the same URL as in the browser.
Edit:
The browser will render the page even before the connection is closed. Even if you flush, I don't think the connection is closed. You can fire up Wireshark and check. stream_get_contents and file_get_contents will block until they have all the output. Even if you flushed, they can't be sure that there isn't more content. Since the content-length header didn't seem to make {file,stream}_get_contents return earlier, you probably need to implement your own buffering, ala. fopen, read, fclose.
Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D
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