我有一个只向客户端生成日志的 php 脚本。
当我回显某些内容时,我希望将其即时传输到客户端。
(因为在处理脚本时,页面是空白的)
我已经玩过ob_start()
and ob_flush()
,但它们没有用。
最好的解决方案是什么?
PS:在通话结束时放一个flush有点脏echo
......
编辑:答案都不起作用,PHP 还是 Apache 故障?
我遇到了同样的问题,并且手册中发布的示例之一有效。必须将字符集指定为此处已经提到的海报之一。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 />';
编辑:
我正在阅读手册页上的评论,遇到了一个错误,指出它ob_implicit_flush
不起作用,以下是它的解决方法:
ob_end_flush();
# CODE THAT NEEDS IMMEDIATE FLUSHING
ob_start();
如果这不起作用,那么甚至可能发生的是客户端没有从服务器接收数据包,直到服务器建立足够的字符来发送它认为值得发送的数据包。
老答案:
您可以使用ob_implicit_flush
which 将告诉输出缓冲关闭缓冲一段时间:
ob_implicit_flush(true);
# CODE THAT NEEDS IMMEDIATE FLUSHING
ob_implicit_flush(false);
所以这就是我发现的。
Flush 在 Apache 的 mod_gzip 或 Nginx 的 gzip 下不起作用,因为从逻辑上讲,它正在对内容进行 gzip 压缩,为此它必须缓冲内容以对其进行 gzip。任何类型的 Web 服务器 gzipping 都会影响这一点。简而言之,在服务器端,我们需要禁用 gzip 并减小 fastcgi 缓冲区大小。所以:
在 php.ini 中:
output_buffering = Off
zlib.output_compression = Off
在 nginx.conf 中:
gzip off;
proxy_buffering off;
手头也有这些行,特别是如果您无权访问 php.ini:
@ini_set('zlib.output_compression',0);
@ini_set('implicit_flush',1);
@ob_end_clean();
set_time_limit(0);
最后,如果你有它,请评论下面的代码:
ob_start('ob_gzhandler');
ob_flush();
PHP测试代码:
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);
}
对于那些在 2018 年到来的人:
唯一对我有用的解决方案:
<?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();
?>
并且保留de“4096”部分非常重要,因为它似乎“填充”了缓冲区......
刷新似乎无法正常工作是自动字符集检测的副作用。
浏览器在知道要显示它的字符集之前不会显示任何内容,如果您不指定字符集,它需要尝试猜测它。问题在于,如果没有足够的数据,它就无法做出很好的猜测,这就是为什么浏览器似乎在显示任何内容之前需要填充这个 1024 字节(或类似)的缓冲区。
因此,解决方案是确保浏览器不必猜测字符集。
如果您要发送文本,请添加一个 '; charset=utf-8' 为其内容类型,如果是 HTML,则将字符集添加到相应的元标记中。
你想要的是flush方法。例子:
echo "log to client";
flush();
为什么不创建一个回显函数,如下所示:
function fecho($string) {
echo $string;
ob_flush();
}
我也有类似的事情要做。使用
// ini_set("output_buffering", 0); // off
ini_set("zlib.output_compression", 0); // off
ini_set("implicit_flush", 1); // on
在我的情况下确实使输出刷新频繁。
但是我必须在特定点(在我运行的循环中)刷新输出,所以同时使用
ob_flush();
flush();
一起为我工作。
我无法用ini_set(...)关闭“output_buffering”,必须直接在php.ini中打开它,phpinfo()在关闭时显示其设置为“无值”,这正常吗?.
不经常提到的一件事是 gzip 压缩由于各种托管环境中的细节而保持打开状态。
这是一种现代方法,使用 PHP-FPM 作为快速 CGI,不需要 .htaccess 重写规则或环境变量:
在 php.ini 或 .user.ini 中:
output_buffering = 0
zlib.output_compression = 0
implicit_flush = true
output_handler =
在 PHP 脚本中:
header('Content-Encoding: none'); // Disable gzip compression
ob_end_flush(); // Stop buffer
ob_implicit_flush(1); // Implicit flush at each output command
有关ob_end_flush() 的需要,请参阅官方 PHP 文档的此评论。
正确使用的功能是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>
请注意,IE 存在一个“问题”,它仅在至少 256 字节时才输出刷新的内容,因此您的页面的第一部分至少需要 256 字节。
这对我来说很好(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";
防病毒软件也可能会干扰输出刷新。就我而言,卡巴斯基反病毒软件 2013 在将数据块发送到浏览器之前会保存数据块,即使我使用的是公认的解决方案。
header( 'X-Accel-Buffering: no' );
header( 'Content-Type: text/html; charset=utf-8' );
echo 'text to display';
echo '<span style="display: none;">' . str_repeat ( ' ', 4096 ) . '</span>';
flush();
usleep( 10 );
有时,问题来自 Apache 设置。Apache 可以设置为 gzip 输出。在文件 .htaccess 中,您可以添加例如:
SetEnv no-gzip 1
试试这个:
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";
请注意,这种方式实际上是在禁用当前脚本的输出缓冲区。我想你可以用 ob_start() 重新启用它(我不确定)。
重要的是,通过像上面那样禁用输出缓冲区,您将无法再使用该header()
函数重定向您的 php 脚本,因为 php 每次脚本执行时只能发送一次 http 标头。但是,您可以使用 javascript 进行重定向。只需让您的 php 脚本回显以下几行:
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;
请注意,如果您在某些共享托管站点(例如 Dreamhost)上,则根本无法禁用 PHP 输出缓冲,而无需经过不同的路线:
更改输出缓冲区缓存 如果您使用 PHP FastCGI,PHP 函数 flush()、ob_flush() 和 ob_implicit_flush() 将无法按预期运行。默认情况下,输出在比 PHP 更高的级别进行缓冲(具体来说,通过Apache 模块 mod_deflate,它在形式/功能上与 mod_gzip 相似)。
如果您需要无缓冲的输出,您必须使用 CGI(而不是 FastCGI)或联系支持以请求 为您的站点禁用 mod_deflate 。
https://help.dreamhost.com/hc/en-us/articles/214202188-PHP-overview
我迟到了讨论,但我读到很多人都在说附加 flush(); 在每个代码的末尾看起来很脏,而且它们是正确的。
最好的解决方案是禁用来自 Apache、中间处理程序和 PHP 的 deflate、gzip 和所有缓冲。然后在你的 php.ini 中你应该有:
output_buffering = Off
zlib.output_compression = Off
implicit_flush = Off
临时解决方案是在你的 php.ini 中有这个,如果你可以用 flush(); 解决你的问题;但是你觉得到处都放着又脏又丑。
implicit_flush = On
如果你只把它放在你的php.ini中,你不需要把flush(); 在你的代码中。
这是我的代码:(适用于 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();
}