缓冲(buffer)是为了协调吞吐速度相差较大的设备之间的数据传送而采用的技术,用来存放缓冲数据的区域叫缓冲区。
在计算机科学领域,当数据从一个地方传送到另一个地方时,缓冲区可以用来临时存储数据。与缓冲相似的一个技术是缓存(cache),它们都是为了解决数据存储和传输速度不同带来的问题。简单来说,缓冲更多用于数据传输过程中的临时存储,而缓存则更多用于减少重复的数据读取。

如上图,是一个简易的缓冲区模拟图。左端入口的数据具有单个输入体积小、速度快、数量多的特点,而右端输出的数据具有体积大、速度慢的特点。如果没有缓冲区,很容易造成数据堵塞;有了缓冲区之后,当数据积累到一定程度再统一输出,则可以减少系统负担。
PHP在执行过程中,嵌入的HTML代码以及 echo、print_r 等指令都会产生输出。正是因为有输出缓冲区的存在,系统可以在PHP执行过程中暂时保存这些输出,再统一发送给浏览器。
例如运行下面的代码:
<?php
echo "这里是第一行数据";
echo "这里是第二行数据,下面睡眠5秒";
sleep(5);
echo "这里是第三行数据,下面是HTML代码";
?>
<h1>标题</h1>
在实际测试中,会发现浏览器往往是同时显示所有内容,而不是先显示第一行和第二行数据,等待5秒后再显示后面的内容。
不仅如此,PHP的输出缓冲还提供给我们更加强大的功能,我们可以在数据发送之前对其进行捕获、修改等操作。
PHP提供了 ob_ 系列函数,例如下面的代码可以对输出内容进行替换:
<?php
ob_start();
echo "Hello world, this is http://www.hitoy.org/";
$content = ob_get_contents();
ob_end_clean();
echo str_replace(" http://", " https://", $content);
?>
上面的 ob_start()、ob_get_contents()、ob_end_clean() 分别用来开启用户缓冲区、获取缓冲区内容以及清空并关闭缓冲区。
PHP中的输出控制函数比较多,例如:
flush
ob_clean
ob_end_clean
ob_end_flush
ob_flush
ob_get_clean
ob_get_contents
ob_get_flush
ob_get_length
ob_get_level
ob_get_status
ob_gzhandler
ob_implicit_flush
ob_list_handlers
ob_start
PHP中还可以通过 php.ini 的 output_buffering 来设置输出缓冲。On 表示开启,Off 表示关闭,设置数字则表示缓冲区大小。
那么,如果关闭PHP的输出缓冲,上面的代码是不是就可以分段显示了呢?
答案并不完全是。
第一点,即使关闭PHP本身的输出缓冲,PHP输出到Web服务器之间可能仍然存在其他层级的缓冲,需要通过 flush() 等方式进一步刷新。
第二点,一些浏览器对单次接收到的内容长度也有一定要求,如果输出内容太少,可能不会立即显示。
例如:
<?php
echo "这里是第一行数据";
echo str_repeat(" ", 1024);
echo "这里是第二行数据,下面睡眠5秒";
flush();
sleep(5);
echo "这里是第三行数据,下面是HTML代码";
?>
<h1>标题</h1>
当然,在实际生产环境中,直接关闭 output_buffering 的情况比较少见,我们更多时候会通过 ob_ 系列函数进行控制。
下面的例子就是利用输出缓冲实现服务器推送(Comet)的一个简单示例:
<?php
ob_start();
$i = 0;
while($i < 100){
echo $i . str_repeat(" ", 2024);
$i++;
ob_flush();
flush();
sleep(5);
}
?>
通过缓冲和刷新,可以让服务器不断向客户端发送新的内容。
对于PHP来说,输出缓冲不仅可以减少频繁输出带来的开销,还可以让程序在真正发送数据之前对输出内容进行统一处理。因此,在页面输出、内容修改、压缩以及一些实时数据推送场景中,都可以看到输出缓冲的应用。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。