<?php
for($i=0;$i<20;$i++)
{
echo 'printing...<br />';
ob_flush();
flush();
usleep(300000);
}
?>
开发者_如何转开发
Url that contains the code: http://domainsoutlook.com/sandbox/delayed.php
I have a dedicated server so I can make the changes. I am running apache and nginx as the proxy server.
So that'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 this lines at hand, specially if you don't have acces 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, coment 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 for the buffer achieve the minimum size in order to flush data
echo str_repeat(' ',1024*64);
sleep(1);
}
You're using ob_flush
without ob_start
, so there is nothing to flush for it.
It also depends on the webserver and proxy and its settings.
You should disable buffering for Nginx (add proxy_buffering off;
to the config file and restart Nginx)
Also, check if your php.ini contains output_buffering = Off
and zlib.output_compression = Off
.
Main php file;
<?php
header('Content-Type: text/HTML; charset=utf-8');
header( 'Content-Encoding: none; ' );//disable apache compressed
session_start();
ob_end_flush();
ob_start();
set_time_limit(0);
error_reporting(0);
..... bla bla
for(each)........
{
bla bla..
echo "<br>>>>".$i."<<<br>";
ob_flush();
flush(); //ie working must
}
?>
it's working..
You have to fill the buffer, so that it can be flushed to browser. Use this after echo
echo str_pad('',4096)."\n";
Complete code:
<?php
if (ob_get_level() == 0) ob_start();
for( $i=0 ; $i<20 ; $i++) {
echo 'printing...<br />';
echo str_pad('',4096)."\n";
ob_flush();
flush();
usleep(300000);
}
ob_end_flush();
?>
In php.ini:
output_buffering = Off
zlib.output_compression = Off
In nginx.conf:
fastcgi_keep_conn on; # < solution
proxy_buffering off;
gzip off;
As I read, it seems a really hard problem to solve, and the only (dirty) way I found is writing something useless to output to fill the ≠ buffers.
- Without SSL
- Without output_buffering,
flush
is needed.
nginx buffers can be lowered until the PHP header size - With output_buffering,
ob_flush
need to be added to have the same behavior as above
- Without output_buffering,
- With SSL
- There is another buffer for SSL and
NGX_SSL_BUFSIZE
is fixed in nginx compilation
- There is another buffer for SSL and
Here is my test.php file (call it with ?size=...
to change space writing in the loop)
<!DOCTYPE html>
<html>
<head></head>
<body>
<?php
$vars = array('output_buffering', 'zlib.output_compression');
print('<p>');
foreach ($vars as $var) {
print("$var : ");
var_dump(ini_get($var));
print('<br />');
}
print("ob_get_level() : " .ob_get_level());
print('</p>');
if (ob_get_level()) {
$bytes = ob_get_length();
ob_flush();
}
$nb_iterations = !empty($_GET['nb']) ? max(2, (int) $_GET['nb']) : 5;
$size = !empty($_GET['size']) ? $_GET['size'] : 0;
for ($i = 1; $i < $nb_iterations; $i++) {
sleep(1);
print(str_repeat(' ', 1024 * $size ));
print("<p>wait $i s</p>");
if (ob_get_level()) {
$bytes += ob_get_length();
print($bytes + strlen($bytes));
ob_flush(); // this is working, results aren't depending on output_buffering value
}
flush(); // this is needed
}
?>
</body>
</html>
And the lower conf I can set is
location ~ ^/test.php$ {
gzip off;
fastcgi_pass unix:/var/run/php5-fpm/ssl.socket;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $request_filename;
# if too low => upstream sent too big header while reading response header from upstream
fastcgi_buffer_size 128;
fastcgi_buffers 2 128;
fastcgi_busy_buffers_size 128;
}
Another possible cause is mod_security. It looks like it has it's own buffers. So if you are using it you will have to set :
SecResponseBodyAccess Off
Kind of a dirty workaround but so far that it the only way I got it to work.
Just wanted to add to the Roger's answer.
If you are using FastCGI php5-fpm module within Apache2 you must also make sure you are adding
-flush
argument in your Apache2 configuration, i.e.
<IfModule mod_fastcgi.c>
...
FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -flush -socket /tmp/php5-fpm.sock -idle-timeout 480 -pass-header Authorization
</IfModule>
Check your server api with
echo phpinfo();
If you found your server api
Server API : CGI/FastCGI
in CentOS then add this line in "/etc/httpd/conf.d/fcgid.conf"
OutputBufferSize 0
To test, restart your Apache server and try below code
ob_start();
for($i = 0; $i < 10; $i ++) {
echo $i;
echo '<br />';
flush();
ob_flush();
sleep(1);
}
I have noticed that browsers react differently. Chrome, for example, holds on to the input forever, and doesn't seem to care about displaying it any earlier. Unsurprisingly, Firefox will display the input earlier, if the above tips (contributed by other stackoverflowers) are applied, so try with Firefox.
I was able to flush only this way - adding session_write_close();
if (ob_get_level() == 0)
{
if(!ob_start("ob_gzhandler"))ob_start();
}
echo ('bla bla bla');
$ans=ob_get_contents();
ob_end_clean();
header('Connection: close');
header('Content-Length: '.strlen($ans));
header('Status: 200');
echo $ans;
session_write_close();
ob_flush();
flush();
if(!ob_get_level()) ob_start();
echo json_encode(array('valid'=>true,'msg'=>'Flush occured.'));
$size = ob_get_length();
header("Content-Type: application/json");
// Set the content length of the response.
header("Content-Length: {$size}");
//Close the connection if you want to.
header("Connection: close");
// Flush all output.
ob_end_flush();
ob_flush();
flush();
// Close current session (if it exists).
if(session_id()) session_write_close();
I have tried many times when using php-fpm with nginx. Many answers just instruct:
In php.ini:
output_buffering = Off
zlib.output_compression = Off
In nginx.conf:
gzip off;
proxy_buffering off;
BUT they forgot the very important setting in nginx.conf:
fastcgi_keep_conn on;
精彩评论