数字营销 · Web开发 · 基础设施

PHP输出缓冲及其应用

介绍PHP输出缓冲的基本原理,以及ob系列函数、flush等方法在内容输出、修改和服务器推送中的应用。

缓冲(buffer)是为了协调吞吐速度相差较大的设备之间的数据传送而采用的技术,用来存放缓冲数据的区域叫缓冲区。

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

PHP缓冲

如上图,是一个简易的缓冲区模拟图。左端入口的数据具有单个输入体积小、速度快、数量多的特点,而右端输出的数据具有体积大、速度慢的特点。如果没有缓冲区,很容易造成数据堵塞;有了缓冲区之后,当数据积累到一定程度再统一输出,则可以减少系统负担。

PHP在执行过程中,嵌入的HTML代码以及 echoprint_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.inioutput_buffering 来设置输出缓冲。On 表示开启,Off 表示关闭,设置数字则表示缓冲区大小。

那么,如果关闭PHP的输出缓冲,上面的代码是不是就可以分段显示了呢?

答案并不完全是。

第一点,即使关闭PHP本身的输出缓冲,PHP输出到Web服务器之间可能仍然存在其他层级的缓冲,需要通过 flush() 等方式进一步刷新。

第二点,一些浏览器对单次接收到的内容长度也有一定要求,如果输出内容太少,可能不会立即显示。

例如:

<?php
echo "这里是第一行数据";
echo str_repeat("&nbsp;", 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("&nbsp;", 2024);

$i++;

ob_flush();
flush();

sleep(5);
}
?>

通过缓冲和刷新,可以让服务器不断向客户端发送新的内容。

对于PHP来说,输出缓冲不仅可以减少频繁输出带来的开销,还可以让程序在真正发送数据之前对输出内容进行统一处理。因此,在页面输出、内容修改、压缩以及一些实时数据推送场景中,都可以看到输出缓冲的应用。

参考资料

  1. Output Control 函数
  2. 关于php_buffering的疑问

评论0

欢迎分享你的看法,也欢迎补充不同的实践经验。