本文对PHP输出缓冲区做了详细的介绍(附代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。
PHP 输出缓冲区:
缓冲区:其实就是一个内存地址空间,用来存储不同速度或优先级的设备之间传输的数据,缓冲可以减少进程间交互的等待时间,使得从慢速设备读取数据时,快速设备的操作过程不会被打断。
PHP 的输出流包含了很多内容,通常是开发人员希望 PHP 输出的文本。这些文本大部分都是使用 echo 或 () 函数输出的。
1>任何输出内容的函数都会使用输出缓冲区
这里指的是普通的 PHP 脚本,如果你正在开发 PHP 扩展,那么用到的函数(C 函数)可能会直接写入 SAPI 缓冲层,而不需要经过输出缓冲层(我们可以在 PHP 源文件 main/.h 中了解这些 C 函数的 API 文档)
2> 输出缓冲区并不是唯一用于缓冲输出的层。它实际上只是众多层之一。输出缓冲区层的行为与使用的 SAPI(Web 或 CLI)相关。不同的 SAPI 可能具有不同的行为。
缓冲区逻辑图
最上面的两个就是我们通常所说的“输出缓冲区”
3>SAPI中的输出缓冲区,这些都是PHP中的层,当输出字节离开PHP,进入计算机体系结构中的较低层时,会不断出现缓冲区(终端缓冲区()、fast-cgi缓冲区、Web服务器缓冲区、操作系统缓冲区、TCP/IP堆栈缓冲区等)。
PHP CLI 的 SAPI 有点特殊,CLI 又叫命令行接口,它会强制将 php.ini 配置中的选项设置为 0,也就是禁用默认的 PHP 输出缓冲区。所以在 CLI 中,默认情况下,你想要输出的内容会直接传递给 SAPI 层,除非你手动调用 ob_() 类函数,而在 CLI 中,该值也会被设置为 1。
注意:这个我们经常混淆的角色,PHP源码已经说明了一切:当设置为打开(值1)时,一旦有任何输出写入SAPI缓冲区,就会被立即刷新(flush表示将数据写入下级,缓冲区会被清空)
也就是说,当任何数据被发送到 CLI SAPI 时,CLI SAPI 会立即把数据扔到它的下一层,通常是标准输出管道。 write() 和 () 函数负责做这件事。
默认 PHP 输出缓冲区:如果您使用与 CLI 不同的 SAPI,例如 PHP-FPM,则将使用以下三个与缓冲相关的 php.ini 配置选项。
首先,你不能使用 () 函数在运行时修改这些选项的值,因为这些值会在 PHP 程序启动时解析,在任何脚本运行之前。所以你可以在运行时使用 () 来改变这些值,但不会生效。我们只能通过编辑 php.ini 文件或在执行 PHP 程序时使用 -d 选项来更改它们的值。
默认情况下,PHP 发行版会在 php.ini 中将其设置为 4096 字节。如果将其值设置为 ON,则默认输出缓冲区大小为 16KB。
在 Web 应用程序环境中使用缓冲区进行输出具有以下性能优势:
默认的 4K 设置是一个合适的值,这意味着你可以先写入 4096 个 ASCII 字符,然后再与下面的 SAPI 层通信。在 Web 应用程序环境中,逐字节传输消息对性能不利。更好的方法是一次性将所有内容传输到服务器,或者至少逐块传输。层与层之间的数据交换越少,性能越好。输出缓冲区应始终保持可用。PHP 将负责在请求结束后将其中的内容传输给最终用户,开发人员无需做任何事情。
默认设置为关闭,这样写入的新数据不会刷新 SAPI。对于协议来说,刷新操作是每次写入后发送一个数组数据包。最好在发送数据包之前填满缓冲区。如果需要手动刷新 SAPI 缓冲区,请使用 flush() 函数。如果希望写入一次就刷新,可以设置或调用 once() 函数。
推荐配置:
=4096
= 关闭/否
要修改输出缓冲区大小,请确保使用 4 或 8 的倍数的值,它们分别适用于 32/64 位操作系统。
是一个回调函数,可以在刷新缓冲区之前修改其内容
缓冲区中的内容将被传递给你选择的回调函数(只能使用一个)来执行内容转换工作。所以如果你想要获取 PHP 给 Web 服务器和用户的内容,可以使用输出缓冲区回调。输出指的是:消息头、消息头。HTTP 消息头也是输出缓冲区层的一部分。
邮件头和邮件正文:
其实,凡是和消息头输出有关的 PHP 函数((), (), ())都使用内部函数,只把内容写入消息头缓冲区。当我们使用 () 函数时,内容会写入输出缓冲区(可能多个),而当这个输出缓冲区的内容需要发送时,PHP 会先发送消息头,然后再发送消息体。PHP 已经帮你搞定了一切,如果你想自己动手,只能禁用输出缓冲区。
用户输出缓冲区:
如果您想使用默认的 PHP 输出缓冲层,则无法使用 CLI,因为它已禁用该层。
/*launched via php -d output_buffering=32 -d implicit_flush=1 * */ echo str_repeat('a',31); sleep(3); echo 'b'; sleep(3); echo 'c'; ?>
默认的输出缓冲区大小设置为32字节,程序运行时会先写入31个字节,然后休眠,再写入1个字节。这个字节填满缓冲区,它会立即刷新自己,并将里面的数据传递给SAPI层缓冲区。因为设置为1,SAPI层缓冲区也会立即刷新到下一层,于是输出aa…b,然后休眠,再输出一个字节。此时缓冲区有31个空字节,但是脚本执行了,因此包含这个字节的缓冲区也会立即刷新,这样c就会输出到屏幕上。
用户输出缓冲区:通过()创建,我们可以创建多个这样的缓冲区(直到内存耗尽为止),这些缓冲区形成一个堆栈结构,每个新的缓冲区都会压栈在前一个缓冲区之上,每次填满或者溢出时都会进行刷新操作,然后将里面的数据传递到下一个缓冲区。
function callback($buffer) { // replace all the apples with oranges return ucfirst($buffer); } function callback1($buffer) { // replace all the apples with oranges static $a=0; return $a++.'-'.$buffer."\n"; } ob_start('callback1',10); ob_start("callback",3); echo "fo"; sleep(2); echo 'o'; sleep(2); echo "barbazz"; sleep(2); echo "hello";
根据堆栈的先进后出原则,任何输出都会先存到缓冲区2中。缓冲区2的大小是3个字节,所以当第一个echo语句输出的字符串'fo'会先存到缓冲区2中时,还缺一个字符。当第二个echo语句输出'o'时,缓冲区2已经满了,所以会刷新(flush)。刷新之前先调用()的回调函数,这个函数会把缓冲区的首字母转为大写,所以输出的是'Foo',然后会保存到缓冲区1中。第三个输出的是'',还是会先放到缓冲区2中。这个字符串有7个字节,缓冲区2已经溢出了,所以会立刻刷新。调用回调函数得到的结构是'',然后传递给缓冲区1,缓冲区1保存了''这十个字符。缓冲区1会被刷新。同样,先调用()的回调函数。 缓冲区 1 的回调函数会将模型添加到字符串签名中,因此第一行是 '0-',
最后一个echo语句输出一个字符串'hello',该字符串大于三个字符,所以触发了缓冲区2。由于此时脚本执行完成,所以缓冲区1也立即被刷新,得到'1-Hello'。
因此,像使用 echo 这样简单的事情,如果涉及到缓冲区和性能,也会变得很复杂。所以要注意 echo 输出内容的大小。如果缓冲区配置和输出内容差不多,性能会更好。如果缓冲区配置小于输出内容,则需要在应用程序中对输出内容进行分段。
输出缓冲机制:5.4之后重写了整个缓冲层,我们在开发自己的PECL扩展的时候可以声明自己的输出缓冲回调方法,这样可以和其他PECL扩展区分开来,避免冲突。
输出缓冲区缺陷:
有些 PHP 内部函数也会使用输出缓冲区,这些缓冲区是叠加在其他缓冲区之上的,这些函数会填充自己的缓冲区然后刷新或返回内容。例如 ()、() 和 ::() 都是这种类型,所以这些函数不要用在输出缓冲区的回调函数中,否则会引发未定义的错误。
同样的,当 PHP 执行 echo 或者 print 的时候,并不会立刻通过 TCP 输出到浏览器,而是会先把数据写入 PHP 默认的缓冲区中。我们可以理解为 PHP 有自己的输出缓冲区机制,在发送到系统缓存之前会新建一个队列,数据会通过这个队列传递,当某个 PHP 缓冲区满了,需要输出脚本执行逻辑的时候,脚本就会把里面的数据传送到 SAPI 浏览器。
echo/print->php输出缓冲区->SAPI缓冲区->TCP缓冲区->浏览器缓冲区->浏览器显示
() 和 flush() 的区别:
():从PHP缓冲区释放数据
flush():将不再位于缓冲区中或已释放的数据发送到浏览器。严格来说,这仅在使用 PHP 作为(或安装)时才有效。它是一个刷新(可以视为特定)缓冲区。
在 nginx 中,flush 和 nginx 均失败:
解决方案:我在nginx配置中找到以下设置:
128千;
8 128千;
Nginx 会缓冲 PHP 输出的信息,直到缓冲的数据达到 128k 时才会将其发送给客户端。所以我们首先需要减小这个缓冲区。
例如:
4千;
8 4K;
此外,必须禁用 gzip
gzip 关闭;
然后,在 PHP 中,在刷新之前,输出一个 4k 大小的内容块,例如:
echo (' ',1024*4);
至此PHP就可以通过把需要的内容逐行输出了。
输出缓冲区实践
1>通过()函数手动处理PHP的缓冲机制,这样即使输出内容超出配置参数size,数据也不会传输到浏览器。()设置PHP的缓冲空间足够大,只有脚本执行结束或者调用()函数后,数据才会发送到浏览器
for($i=0;$i<10;$i++){ echo $i.'
'; sleep($i+1); }
执行完之后,每隔几秒就会有输出,直到脚本循环结束,这是因为数据量太小,输出缓冲区未满。
2>当修改量 = 0时
for($i=0;$i<10;$i++){ echo $i.'
'; flush(); sleep($i+1); }
由于缓冲区容量设置为 0,因此 PHP 缓冲机制被禁用。这就是为什么我们在浏览器中看到间歇性输出,而不必等到脚本执行完毕。这是因为数据没有停留在缓存中。
3>我们把参数改为=4096,输出数据大于一个缓冲区,就不会调用()函数了
首先输出一个4k的内容记下来并添加原来的输出内容:
for($i=0;$i<10;$i++){ echo echo str_repeat(' ', 1024*4*8).$i
; sleep($i); }
发现HTTP连接没有关闭,看到断断续续的输出,虽然开启了PHP输出缓冲机制,但是并不是一次性全部输出,这是因为PHP缓冲区空间不足,每次填满一个缓冲区,就会向浏览器发送数据。
4> 参照前面的例子,这次我们调用()
ob_start(); //开启PHP缓冲区 for($i=0;$i<10;$i++){ echo echo str_repeat(' ', 1024*4*8).$i
; sleep($i); }
直到服务器端脚本完全处理完毕、响应结束,才会看到完整的输出。在输出之前,浏览器会一直保持空白。这是因为一旦 PHP 调用(),它就会将 PHP 缓冲区扩展到足够大的大小,直到函数调用或脚本运行完毕,PHP 缓冲区中的数据才会发送到客户端浏览器。
可以通过命令监控TCP数据包,观察使用()和不使用()的区别
摘要:激活机制,一旦激活,脚本不再直接输出到浏览器,而是暂时写入PHP缓冲区
PHP 默认开启了该机制,通过调用函数将该值扩充到足够大的大小。也可以通过 $ 指定该值。$ 的默认值是 0,表示 PHP 缓冲区中的数据要等到脚本运行完毕才会发送到浏览器。如果设置了 $ 的大小,只要缓冲区达到该值,就会发送到浏览器。
可以通过指定参数来处理PHP缓冲区中的数据,比如()压缩缓冲区中的数据并发送到浏览器,()获取PHP缓冲区中数据的副本
ob_start(); echo date('Y-m-d h:i:s'); $output=ob_get_contents(); ob_end_flush(); echo ''.$output;
后者是从中获取的缓冲区的内容
()和(0函数都会关闭输出缓冲,区别在于前者只把PHP缓冲区中的数据发送到客户端浏览器,而后者会删除PHP缓冲区中的数据但不会发送到客户端。前者调用后数据依然存在,而()仍然可以得到PHP缓冲区中数据的副本
输出缓冲和静态页面:
大家都知道静态页面加载速度很快,不需要请求数据库,下面是生成静态页面的脚本代码。
echo str_pad('',1024);//使缓冲区溢出 ob_start();//打开缓冲区 $content=ob_get_contents();//获取缓冲区内容 $f=fopen('./index.html','w'); fwrite($f,$content);//写入到文件 fclose($f); ob_end_clean()清空并关闭缓冲区
()函数在某些模板引擎和页面文件缓冲区中有使用,比如WP,,例如:
在Wp中,经常会在主题目录.php中看到类似的代码:
flush()只有一个,它的位置告诉浏览器哪一部分缓存需要更新,也就是页头以上的部分需要缓存
以及部分Wp代码,可以看到当缓冲开启的时候,添加自己的回调方法
内容压缩输出:将内容压缩输出到客户端浏览器
好处:减少客户端对服务器出口带宽的占用,提高带宽利用率。减少Web服务器(如Nginx等)在处理文本时引入的开销。用户端可以减少网络传输延迟对用户体验的影响,减少浏览器在加载页面内容时占用的内存,有利于提高浏览器的稳定性。
ob_start('ob_gzhandler');//使用gz格式压缩输出 print'my contents'; ob_end_flush();
它会压缩当前脚本和缓冲区,但对其他脚本没有影响。PHP 还提供了另一种压缩方法,可以在 php.ini 中修改
离子=开
这样,所有页面在输出时都会以 zlib 格式压缩。不过,将两者混用是没有意义的,只会消耗额外的 CPU 性能。让它对已经压缩的内容进行压缩。不过根据实践来看,使用 PHP 的压缩效果并不是很理想,通常的做法是放在 Web 服务器上,比如启用,Nginx 使用 gzip,这比 PHP 的压缩效果好很多。
输出缓冲允许第三方库和应用程序框架(TP 等)开发人员完全控制自己的输出内容。例如,可以将它们放在全局缓冲区中进行处理,并且任何输出流内容(例如数据压缩)和任何 HTTP 消息头和 PHP 都以正确的顺序发送。使用输出缓冲区可以有效节省带宽,例如图像、字体、CSS、JS 等前端内容,尤其是前端框架的限制越来越多,让用户能够更快地做出响应,从而有效提高系统性能。
扫一扫在手机端查看
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。