用户名:
密 码: 记住
您当前的位置:首页 > 网络编程 > php教程

php.ini中output_buffering详解

时间:2015-01-23  来源:西部数据  作者:西部数据

php output_buffering

默认情况下,php buffer是开启的,而且该buffer默认值是4096,即4kb,你可以通过在php.ini配置文件中找到output_buffering配置,当echo,print等输出用户数据的时候,输出数据都会写入到php output_buffering中,直到output_buffering写满,会将这些数据通过tcp传送给浏览器显示,你也可以通过ob_start()手动激活php output_buffering机制,使得即便输出超过了4kb数据,也不真的把数据交给tcp传给浏览器,因为ob_start()将php buffer空间设置到了足够大,只有直到脚本结束,或者调用ob_end_flush函数,才会把数据发送给客户端浏览器.

1.当output_buffering=4096,并且输出较少数据(少于一个buffer),代码如下:

  1. <?php 
  2. for ($i = 0; $i < 10; $i++) { 
  3.     echo $i . '<br/>'
  4.     sleep($i + 1); // 
  5. ?> 

现象:不是每隔几秒就会有间断性输出,而是直到响应结束,才能看一次性看到输出,在等待服务器脚本处理结束之前,浏览器界面一直保持空白,这是因为,数据量太小,php output_buffering没有写满,写数据的顺序,依次是echo->php buffer->tcp buffer->browser

2.当output_buffering=0,并且输出较少数据(少于一个buffer),代码如下:

  1. <?php 
  2. //通过ini_set('output_buffering', 0)并不生效 
  3. //应该编辑/etc/php.ini,设置output_buffering=0禁用output buffering机制 
  4. //ini_set('output_buffering', 0); //彻底禁用output buffering功能 
  5. for ($i = 0; $i < 10; $i++) { 
  6.     echo $i . '<br/>'
  7.     flush();  //通知操作系统底层,尽快把数据给客户端浏览器 
  8.     sleep($i + 1); // 
  9. ?> 

现象:与刚才显示并不一致,禁用了php buffering机制之后,在浏览器可以断断续续看到间断性输出,不必等到脚本执行完毕才看到输出,这是因为,数据没有在php output_buffering中停留,写数据的顺序依次是echo->tcp buffer->browser

3.当output_buffering=4096.,输出数据大于一个buffer,不调用ob_start(),代码如下:

  1. #//创建一个4kb大小的文件 
  2. $dd if=/dev/zero of=f4096 bs=4096 count=1 
  3. <?php 
  4. for ($i = 0; $i < 10; $i++) { 
  5.     echo file_get_contents('./f4096') . $i . '<br/>'
  6.     sleep($i +1); 
  7. ?>  

现象:响应还没结束(http连接没有关闭),断断续续可以看到间断性输出,浏览器界面不会一直保持空白,尽管启用了php output_buffering机制,但依然会间断性输出,而不是一次性输出,是因为output_buffering空间不够用,每写满一个php buffering,数据就会发送到客户端浏览器.

4.当output_buffering=4096,输出数据大于一个tcp buffer,调用ob_start(),代码如下 :

  1. <?php 
  2. ob_start(); //开启php buffer 
  3. for ($i = 0; $i < 10; $i++) { 
  4.     echo file_get_contents('./f4096') . $i . '<br/>'
  5.     sleep($i + 1); 
  6. ob_end_flush(); 
  7. ?> 

现象:直到服务端脚本处理完成,响应结束,才看到完整输,输出间隔时间很短,以至你感受不到停顿,在输出之前,浏览器一直保持着空白界面,等待服务端数据,这是因为,php一旦调用了ob_start()函数,它会将php buffer扩展到足够大,直到ob_end_flush函数调用或者脚本运行结速才发送php buffer中的数据到客户端浏览器.

tcpdump观察

在这里,我们通过tcpdump监控一下tcp报文,来观察一下使用ob_start()和没有使用它的一个区别.

1.没有使用ob_start()

  1. 12:30:21.499528 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . ack 485 win 6432 
  2. 12:30:21.500127 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 1:2921(2920) ack 485 win 6432 
  3. 12:30:21.501000 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 2921:7301(4380) ack 485 win 6432 
  4. 12:30:21.501868 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 7301:8412(1111) ack 485 win 643 
  5. 12:30:24.502340 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 8412:14252(5840) ack 485 win 6432 
  6. 12:30:24.503214 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 14252:15712(1460) ack 485 win 6432 
  7. 12:30:24.503217 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 15712:16624(912) ack 485 win 6432 
  8.  
  9. 12:30:31.505934 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 16624:23924(7300) ack 485 win 6432 
  10. 12:30:31.506839 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 23924:24836(912) ack 485 win 6432 
  11. 12:30:42.508871 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 24836:32136(7300) ack 485 win 6432 
  12. 12:30:42.509744 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 32136:33048(912) ack 485 win 6432 
  13. 12:30:57.512137 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . 33048:40348(7300) ack 485 win 6432 
  14. 12:30:57.513016 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 40348:41260(912) ack 485 win 6432 
  15. 12:31:06.513912 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: P 41260:41265(5) ack 485 win 6432 
  16. 12:31:06.514012 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: F 41265:41265(0) ack 485 win 6432 
  17. 12:31:06.514361 IP 192.168.0.8.webcache > 192.168.0.28.cymtec-port: . ack 486 win 6432 

 

2.使用了ob_start()
  1. 12:36:06.542244 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . ack 485 win 6432 
  2. 12:36:51.559128 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 1:2921(2920) ack 485 win 6432 
  3. 12:36:51.559996 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 2921:7301(4380) ack 485 win 6432 
  4. 12:36:51.560866 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 7301:11681(4380) ack 485 win 6432 
  5. 12:36:51.561612 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 11681:16061(4380) ack 485 win 6432 
  6. 12:36:51.561852 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 16061:20441(4380) ack 485 win 6432 
  7. 12:36:51.562479 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 20441:24821(4380) ack 485 win 6432 
  8. 12:36:51.562743 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 24821:29201(4380) ack 485 win 6432 
  9. 12:36:51.562996 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 29201:33581(4380) ack 485 win 6432 
  10. 12:36:51.563344 IP 192.168.0.8.webcache > 192.168.0.28.noagent: P 33581:35041(1460) ack 485 win 6432 
  11. 12:36:51.563514 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 35041:36501(1460) ack 485 win 6432 
  12. 12:36:51.563518 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 36501:37961(1460) ack 485 win 6432 
  13. 12:36:51.563523 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 37961:39421(1460) ack 485 win 6432 
  14. 12:36:51.563526 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . 39421:40881(1460) ack 485 win 6432 
  15. 12:36:51.563529 IP 192.168.0.8.webcache > 192.168.0.28.noagent: FP 40881:41233(352) ack 485 win 6432 
  16. 12:36:51.570364 IP 192.168.0.8.webcache > 192.168.0.28.noagent: . ack 486 win 6432 

通过上面的对比,我们可以看到,数据报文的时间间隔明显不一样,没有使用ob_start(),时间间隔比较大,等待4秒左右就把tcp buffer中的数据发送出去了。数据没有在php buffer中逗留过长时间,就将输出数据发送给了客户端浏览器。这是因为,很快php buffer就被写满了,不得不把数据发送出去。而启用了ob_start(),则不同,发送数据包给客户端,几乎是同一时间发出去的。这就可以推断,数据一直在php buffer中逗留,直到调用了ob_end_flush()才把php buffer中的数据发送给客户端浏览器。

output buffering函数

1.ob_start

激活output_buffering机制。一旦激活,脚本输出不再直接出给浏览器,而是先暂时写入php buffer内存区域。

php默认开启output_buffering机制,只不过,通过调用ob_start()函数据output_buffering值扩展到足够大。也可以指定$chunk_size来指定output_buffering的值。$chunk_size默认值是0,表示直到脚本运行结束,php buffer中的数据才会发送到浏览器。如果你设置了$chunk_size的大小,则表示只要buffer中数据长度达到了该值,就会将buffer中的数据发送给浏览器。

当然,你可以通过指定$ouput_callback,来处理buffer中的数据。比如函数ob_gzhandler,将buffer中的数据压缩后再传送给浏览器。

2.ob_get_contents

获取一份php buffer中的数据拷贝。值得注意的是,你应该在ob_end_clean()函数调用这调用该函数,否则ob_get_contents()返回一个空字符中。

3.ob_end_flush与ob_end_clean

这二个函数有点相似,都会关闭ouptu_buffering机制。但不同的是,ob_end_flush只是把php buffer中的数据冲(flush/send)到客户端浏览器,而ob_clean_clean将php bufeer中的数据清空(erase),但不发送给客户端浏览器。ob_end_flush调用之后,php buffer中的数据依然存在,ob_get_contents()依然可以获取php buffer中的数据拷贝。而ob_end_clean()调用之后ob_get_contents()取到的是空字符串,同时浏览器也接收不到输出,即没有任何输出。

惯用案例:常常在一些模板引擎和页面文件缓存中看到ob_start()使用。在知名开源项目wordpress,drupal,smarty等地方,都能够发现他们的踪影子。这里抽出drupal的应用。

#模板文件代码如下:

  1. //@file:user-profile.tpl.php 
  2. <div> 
  3.      <ul> 
  4.           <li>username: <?php echo $user->name; ?></li> 
  5.           <li>picture:<?php echo $user->picture; ?></li> 
  6.      </ul> 
  7. </div> 
  8.  
  9. //@file:template-render.php 
  10. <?php 
  11. function theme_render_template($template_file$variables) { 
  12.   if (!is_file($template_file) { return ""; } 
  13.   extract($variables, EXTR_SKIP); 
  14.   ob_start(); 
  15.   $contents = ob_get_contents(); 
  16.   ob_end_clean(); 
  17.   return $contents
  18. ?> 
  19.  
  20. //@file:profile.php 
  21. <?php 
  22. $variables = array('user' => $user); 
  23. print theme_render_template('user-profile.tpl.php'$variables); 
  24. ?> 

最后总结:

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

ob_*系列函数, 是操作PHP本身的输出缓冲区.

所以, ob_flush是刷新PHP自身的缓冲区.

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

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

1.有些Apache的模块,比如mod_gzip,可能自己进行输出缓存,

2.这将导致flush()函数产生的结果不会立即被发送到客户端浏览器。

4.甚至浏览器也会在显示之前,缓存接收到的内容。例如 Netscape

5.浏览器会在接受到换行或 html 标记的开头之前缓存内容,并且在

6.接受到 </table> 标记之前,不会显示出整个表格。

8.一些版本的 Microsoft Internet Explorer 只有当接受到的256个

9.字节以后才开始显示该页面,所以必须发送一些额外的空格来让这

10.些浏览器显示页面内容。

所以, 正确使用俩者的顺序是. 先ob_flush, 然后flush,

当然,在其他sapi下,不调用flush也可以,只不过为了保证你代码的可移植性,建议配套使用.

来顶一下
返回首页
返回首页
推荐资讯
WiFi太不安全:7岁女孩11分钟内入侵公共网络 WiFi太不安全:7岁女孩11分钟内入侵近期刚刚发布研究说WiFi网络能获得人们手机里多少私人信息,
不服跑个分?人工智能也出现“刷分”乱象 不服跑个分?人工智能也出现“刷分2014年,人工智能领域突然爆发,成为了科研和科技创业的热门
相关文章
栏目更新
栏目热门