深入理解PHP ob_flush和flush的区别

buffer是一个内存地址空间,Linux系统默认大小一般为4096(1kb),即一个内存页。主要用于存储速度不同步的设备或者优先级不同的 设备之间传办理数据的区域。通过buffer,可以使进程这间的相互等待变少。

这里说一个通俗一点的例子,你打开文本编辑器编辑一个文件的时候,你每输入 一个字符,操作系统并不会立即把这个字符直接写入到磁盘,而是先写入到buffer,当写满了一个buffer的时候,才会把buffer中的数据写入磁 盘,当然当调用内核函数flush()的时候,强制要求把buffer中的脏数据写回磁盘。

同样的道理,在PHP中,当执行echo,print的时候,输出并没有立即通过tcp传给客户端浏览器显示, 而是将数据写入php buffer。php output_buffering机制,意味在tcp buffer之前,建立了一新的队列,数据必须经过该队列。当一个php buffer写满的时候,脚本进程会将php buffer中的输出数据交给系统内核交由tcp传给浏览器显示。所以,数据会依次写到这几个地方echo/pring -> php buffer -> tcp buffer(apache,nginx,lightHTTP) -> browser buffer

ob_flush/flush在手册中的描述, 都是刷新输出缓冲区, 并且还需要配套使用, 所以会导致很多人迷惑。
其实, 他们俩的操作对象不同, 有些情况下, flush根本不做什么事情。

php buffer

ob_flush
# 该函数对PHP本身的的缓存进行输出。PHP本身的缓存受php.ini中的output_buffering的控制。ob_flush()的作用就是将本来存在输出缓存中的内容取出来,设置为等待输出状态,但不会直接发送到客户端,这时你就需要先使用ob_flush()再使用flush(),客户端才能立即获得脚本的输出。

ob_*系列函数, 是操作PHP本身的输出缓冲区。即, ob_flush是刷新PHP自身的缓冲区.

与PHP本身输出缓冲相关的两个PHP配置是:

配置1:output_buffering :on/off 或 者整数 。
on,将在所有脚本中使用输出缓存控制,不限制缓存的大小。
整数,如output_buffering=4096,当缓存数 据达到4096字节时会自动输出刷新缓存。而这个参数的不同将导致相同代码执行结果不同。
off,表示关闭PHP输出缓存, 脚本所有的输出(echo)都会即时发送到客户端,执行打印代码时就是每秒输出。而开启output_buffering后,输出内容就会先缓存 在服务端,直到脚本结束时才一起发送给客户端。

配置2:implicit_flush:on/off。设定ON意味着,当脚本有输出时,自动立即发送到客户端。相当于在echo后自动加flush()。

代码设置implicit_flush的函数:

ob_implicit_flush
# 这个函数强制每当有输出的时候,即刻把输出发送到浏览器。这样就不需要每次输出(echo)后,都用flush()来发送到浏览器了。



<?php
ob_end_clean();
echo str_pad(" ", 256);
for ($i=100; $i>0; $i--) {
   echo $i, '<br/>';
   flush();
   sleep(1); 
} 
?>

以上代码应该隔一秒钟输出一次$i. 以上echo str_pad(" ", 256)的目的是IE需要接受到256个字节之后才开始显示。 以上代码还有以下两种写法。

<?php
echo str_pad(" ", 256);
for ($i=100; $i>0; $i--) {
   echo $i, '<br />';
   ob_flush();
   flush();
   sleep(1); 
} 
?>

<?php
ob_implicit_flush(true);
echo str_pad(" ", 256);
for ($i=100; $i>0; $i--) {
   echo $i, '<br />';
   ob_flush();
   sleep(1); 
} 
?>

tcp buffer

flush
# 刷新PHP程序的缓冲,而不论PHP执行在何种情况下。该函数将当前为止程序的所有输出发送到用户的浏览器。 但是该函数不会对服务器或客户端浏览器的缓存模式产生任何影响,也不会对PHP本身的缓存产生任何影响。

在apache module的sapi下, flush会通过调用sapi_module的flush成员函数指针, 间接的调用apache的api: ap_rflush刷新apache的输出缓冲区, 当然手册中也说了, 有一些apache的其他模块, 可能会改变这个动作的结果。

而flush, 严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区.

有些Apache的模块,比如mod_gzip,可能自己进行输出缓存,这将导致flush()函数产生的结果不会立即被发送到客户端浏览器。

PHP安装模式的影响。以上方式对于PHP以Apache模块方式安装的情况,可以直接使用。 如果以FastCgi方式还需要注意以下几下配置:
a). Apache+Fcgid+PHP
FcgidOutputBufferSize 0(默认是65536)
在配置Fcgid时, 设置这项值为0, 以上代码做刷新缓冲时,才能达到自己想要的效果。
b).IIS+FastCgi+PHP
ResponseBufferLimit=0
修改WINDOWS\system32\inetsrv\fcgiext.ini下的这一项。
c).nginx+php-fpm
fastcgi_buffer_size 4k;
fastcgi_buffers 8 4k;
fastcgi_busy_buffers_size 4k
gzip off;

browser buffer

甚至浏览器也会在显示之前,缓存接收到的内容。例如 Netscape浏览器会在接受到换行或 html 标记的开头之前缓存内容,并且在接受到 标记之前,不会显示出整个表格。

浏览器的输出缓存:IE为256Bytes, Chrome与FireFox为1000Bytes,只有输出数据达到了这个长度或者脚本结束浏览器才会将数据输出在页面上,但一些版本的 Microsoft Internet Explorer 只有当接受到的256个字节以后才开始显示该页面,所以必须发送一些额外的空格来让这些浏览器显示页面内容。

因此需要正确使用俩者的顺序,先ob_flush, 然后flush。当然, 在其他sapi下, 不调用flush也可以, 只不过为了保证你代码的可移植性, 建议配套使用。

参考:
PHP中刷新输出缓冲详解
深入理解ob_flush和flush的区别

标签: none

添加新评论