I have a php script that only produces logs to the client.
When I echo something, I want it to be transferred to client on-the-fly.
(Because while the script is processing, the page is blank)
I had already played around with ob_start()
and ob_flush()
, but they didn't work.
What's the best solution?
PS: it is a little dirty to put a flush at the end of the echo
call...
EDIT: Neither the Answers worked, PHP or Apache Fault?
For those coming in 2018:
The ONLY Solution worked for me:
<?php
if (ob_get_level() == 0) ob_start();
for ($i = 0; $i<10; $i++){
echo "<br> Line to show.";
echo str_pad('',4096)."\n";
ob_flush();
flush();
sleep(2);
}
echo "Done.";
ob_end_flush();
?>
and its very important to keep de "4096" part because it seems that "fills" the buffer...
I'm late to the discussion but I read that many people are saying appending flush(); at the end of each code looks dirty, and they are right.
Best solution is to disable deflate, gzip and all buffering from Apache, intermediate handlers and PHP. Then in your php.ini you should have:
output_buffering = Off
zlib.output_compression = Off
implicit_flush = Off
Temporary solution is to have this in your php.ini IF you can solve your problem with flush(); but you think it is dirty and ugly to put it everywhere.
implicit_flush = On
If you only put it above in your php.ini, you don't need to put flush(); in your code anymore.
Anti-virus software may also be interfering with output flushing. In my case, Kaspersky Anti-Virus 2013 was holding data chunks before sending it to the browser, even though I was using an accepted solution.
Note if you are on certain shared hosting sites like Dreamhost you can't disable PHP output buffering at all without going through different routes:
Changing the output buffer cache If you are using PHP FastCGI, the PHP functions flush(), ob_flush(), and ob_implicit_flush() will not function as expected. By default, output is buffered at a higher level than PHP (specifically, by the Apache module mod_deflate which is similar in form/function to mod_gzip).
If you need unbuffered output, you must either use CGI (instead of FastCGI) or contact support to request that mod_deflate is disabled for your site.
https://help.dreamhost.com/hc/en-us/articles/214202188-PHP-overview
I had a similar thing to do. Using
// ini_set("output_buffering", 0); // off
ini_set("zlib.output_compression", 0); // off
ini_set("implicit_flush", 1); // on
did make the output flushing frequent in my case.
But I had to flush the output right at a particular point(in a loop that I run), so using both
ob_flush();
flush();
together worked for me.
I wasn't able to turn off "output_buffering" with ini_set(...), had to turn it directly in php.ini, phpinfo() shows its setting as "no value" when turned off, is that normal? .
what you want is the flush method. example:
echo "log to client";
flush();
I've gotten the same issue and one of the posted example in the manual worked. A character set must be specified as one of the posters here already mentioned. http://www.php.net/manual/en/function.ob-flush.php#109314
header( 'Content-type: text/html; charset=utf-8' );
echo 'Begin ...<br />';
for( $i = 0 ; $i < 10 ; $i++ )
{
echo $i . '<br />';
flush();
ob_flush();
sleep(1);
}
echo 'End ...<br />';
So here's what I found out.
Flush would not work under Apache's mod_gzip or Nginx's gzip because, logically, it is gzipping the content, and to do that it must buffer content to gzip it. Any sort of web server gzipping would affect this. In short, at the server side we need to disable gzip and decrease the fastcgi buffer size. So:
In php.ini:
output_buffering = Off
zlib.output_compression = Off
In nginx.conf:
gzip off;
proxy_buffering off;
Also have these lines at hand, especially if you don't have access to php.ini:
@ini_set('zlib.output_compression',0);
@ini_set('implicit_flush',1);
@ob_end_clean();
set_time_limit(0);
Last, if you have it, comment the code bellow:
ob_start('ob_gzhandler');
ob_flush();
PHP test code:
ob_implicit_flush(1);
for ($i=0; $i<10; $i++) {
echo $i;
// this is to make the buffer achieve the minimum size in order to flush data
echo str_repeat(' ',1024*64);
sleep(1);
}
One thing that is not often mentionned is gzip compression that keeps turned ON because of details in various hosting environments.
Here is a modern approach, working with PHP-FPM as Fast CGI, which does not need .htaccess rewrite rule or environment variable :
In php.ini or .user.ini :
output_buffering = 0
zlib.output_compression = 0
implicit_flush = true
output_handler =
In PHP script :
header('Content-Encoding: none'); // Disable gzip compression
ob_end_flush(); // Stop buffer
ob_implicit_flush(1); // Implicit flush at each output command
See this comment on official PHP doc for ob_end_flush() need.
Sometimes, the problem come from Apache settings. Apache can be set to gzip the output. In the file .htaccess you can add for instance :
SetEnv no-gzip 1
Flushing seemingly failing to work is a side effect of automatic character set detection.
The browser will not display anything until it knows the character set to display it in, and if you don't specify the character set, it need tries to guess it. The problem being that it can't make a good guess without enough data, which is why browsers seem to have this 1024 byte (or similar) buffer they need filled before displaying anything.
The solution is therefore to make sure the browser doesn't have to guess the character set.
If you're sending text, add a '; charset=utf-8' to its content type, and if it's HTML, add the character set to the appropriate meta tag.
Why not make a function to echo, like this:
function fecho($string) {
echo $string;
ob_flush();
}
This is my code: (work for PHP7)
private function closeConnection()
{
@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
ignore_user_abort(true);
set_time_limit(0);
ob_start();
// do initial processing here
echo json_encode(['ans' => true]);
header('Connection: close');
header('Content-Length: ' . ob_get_length());
ob_end_flush();
ob_flush();
flush();
}
The correct function to use is flush()
.
<html>
<body>
<p>
Hello! I am waiting for the next message...<br />
<?php flush(); sleep(5); ?>
I am the next message!<br />
<?php flush(); sleep(5); ?>
And I am the last message. Good bye.
</p>
</body>
</html>
Please note that there is a "problem" with IE, which only outputs the flushed content when it is at least 256 byte, so your first part of the page needs to be at least 256 byte.
Try this:
while (@ob_end_flush());
ob_implicit_flush(true);
echo "first line visible to the browser";
echo "<br />";
sleep(5);
echo "second line visible to the browser after 5 secs";
Just notice that this way you're actually disabling the output buffer for your current script. I guess you can reenable it with ob_start() (i'm not sure).
Important thing is that by disabling your output buffer like above, you will not be able to redirect your php script anymore using the header()
function, because php can sent only once per script execution http headers.
You can however redirect using javascript. Just let your php script echo following lines when it comes to that:
echo '<script type="text/javascript">';
echo 'window.location.href="'.$url.'";';
echo '</script>';
echo '<noscript>';
echo '<meta http-equiv="refresh" content="0;url='.$url.'" />';
echo '</noscript>';
exit;
This works fine for me (Apache 2.4/PHP 7.0):
@ob_end_clean();
echo "lorem ipsum...";
flush();
sleep(5);
echo "<br>dolor...";
flush();
sleep(5);
echo "<br>sit amet";
Source: Stackoverflow.com