PHP的缓冲区你了解过吗( 二 )


implicit_flush已在前面谈论CLI的时候提到过 。对于其他的SAPI,implicit_flush默认被设置为关闭(off),这是正确的设置,因为只要有新数据写入就刷新SAPI的做法很可能并非你所希望的 。对于FastCGI协议,刷新操作(flushing)是每次写入后都发送一个FastCGI数组包(packet),如果发送数据包之前先把FastCGI的缓冲区写满会更好一些 。如果你想手动刷新SAPI的缓冲区,使用PHP的flush()函数 。如果你想写一次就刷新一次,你可以设置INI配置中的implicit_flush选项,或者调用一次ob_implicit_flush()函数 。
output_handler是一个回调函数,它可以在缓冲区刷新之前修改缓冲区中的内容 。PHP的扩展提供了很多回调函数(用户也可以自己编写回调函数,下面会讲到) 。
ob_gzhandler%20:%20使用ext/zlib压缩输出mb_output_handler%20:%20使用ext/mbstring转换字符编码ob_iconv_handler%20:%20使用ext/iconv转换字符编码ob_tidyhandler%20:%20使用ext/tidy整理输出的html文本ob_[inflate/deflate]_handler%20:%20使用ext/http压缩输出ob_etaghandler%20:%20使用ext/http自动生成HTTP的Etag缓冲区中的内容会传递给你选择的回调函数(只能用一个)来执行内容转换的工作,所以如果你想获取PHP传输给web服务器以及用户的内容,你可以使用输出缓冲区回调 。当前有一点也需要提一下,这里说的“输出”指的是消息头(headers)和消息体(body) 。HTTP的消息头也是OB层的一部分 。
消息头和消息体
当你使用一个输出缓冲区(无论是用户的,还是PHP的)的时候,你可能想以你希望的方式发送HTTP消息头和内容 。你知道任何协议都必须在发送消息体之前发送消息头(这也是为什么叫做“头”),但是如果你使用了输出缓冲区层,那么PHP会接管这些,而不需要你操心 。实际上,任何跟消息头的输出有关的PHP函数(header(),setcookie(),session_start())都使用了内部的sapi_header_op()函数,这个函数只会把内容写入到消息头缓冲区中 。然后当你输出内容是,例如使用printf(),这些内容会写入到输出缓冲区(假设只有一个) 。当这个输出缓冲区中的内容需要被发送时,PHP会先发送消息头,然后发送消息体 。PHP为你搞定了所有的事情 。如果你觉得不爽,想自己动手,那你就只有把输出缓冲区禁用掉,除此之外别无他法 。
用户输出缓冲区(user%20output%20buffers)
对于用户输出缓冲区,我们先通过一个示例来看看它是怎么工作的,以及你可以用它来做什么 。再强调一下,如果你想使用默认PHP输出缓冲区层的话,你不能使用CLI,因为它已禁用了这个层 。下面的这个示例用的就是默认PHP输出缓冲区,使用了PHP的内部web服务器SAPI:
/*%20launched%20via%20php%20-doutput_buffering=32%20-dimplicit_flush=1%20-S127.0.0.1:8080%20-t/var/www%20*/echo%20str_repeat('a',%2031);sleep(3);echo%20'b';sleep(3);echo%20'c';在这个示例中,启动PHP的时候将默认输出缓冲区的大小设置为32字节,程序运行后会先向其中写入31个字节,然后进入睡眠状态 。此时屏幕是空的,什么都不会输出,跟预计一样 。2秒之后睡眠结束,再写入了一个字节,这个字节填满了缓冲区,它会立即刷新自身,把里面的数据传递给SAPI层的缓冲区,因为我们将implicit_flush设置为1,所以SAPI层的缓冲区也会立即刷新到下一层 。字符串’aaaaaaaaaa{31个a}b’会出现在屏幕上,然后脚本再次进入睡眠状态 。2秒之后,再输出一个字节,此时缓冲区中有31个空字节,但是PHP脚本已执行完毕,所以包含这1个字节的缓冲区也会立即刷新,从而会在屏幕上输出字符串’c’ 。
从这个示例我们可以看到默认PHP输出缓冲区是如何工作的 。我们没有调用任何跟缓冲区相关的函数,但这并不意味这它不存在,你要认识到它就存在当前程序的运行环境中(在非CLI模式中才有效) 。
OK,现在开始讨论用户输出缓冲区,它通过调用ob_start()创建,我们可以创建很多这种缓冲区(至到内存耗尽为止),这些缓冲区组成一个堆栈结构,每个新建缓冲区都会堆叠到之前的缓冲区上,每当它被填满或者溢出,都会执行刷新操作,然后把其中的数据传递给下一个缓冲区 。
ob_start(function($ctc)%20{%20static%20$a%20=%200;%20return%20$a++%20.%20'-%20'%20.%20$ctc%20.%20"n";},%2010);ob_start(function($ctc)%20{%20return%20ucfirst($ctc);%20},%203);echo%20"fo";sleep(2);echo%20'o';sleep(2);echo%20"barbazz";sleep(2);echo%20"hello";/*%200-%20FooBarbazzn%201-%20Hellon%20*/在此我代替原作者讲解下这个示例 。我们假设第一个ob_start创建的用户缓冲区为缓冲区1,第二个ob_start创建的为缓冲区2 。按照栈的后进先出原则,任何输出都会先存放到缓冲区2中 。


推荐阅读