php缓冲 output_buffering的使用详解


Posted in PHP onJune 13, 2013

buffer
buffer是一个内存地址空间,Linux系统默认大小一般为4096(4kb),即一个内存页。主要用于存储速度不同步的设备或者优先级不同的设备之间传办理数据的区域。通过buffer,可以使进程这间的相互等待变少。这里说一个通俗一点的例子,你打开文本编辑器编辑一个文件的时候,你每输入一个字符,操作系统并不会立即把这个字符直接写入到磁盘,而是先写入到buffer,当写满了一个buffer的时候,才会把buffer中的数据写入磁盘,当然当调用内核函数flush()的时候,强制要求把buffer中的脏数据写回磁盘。

同样的道理,当执行echo,print的时候,输出并没有立即通过tcp传给客户端浏览器显示, 而是将数据写入php buffer。php output_buffering机制,意味在tcp buffer之前,建立了一新的队列,数据必须经过该队列。当一个php buffer写满的时候,脚本进程会将php buffer中的输出数据交给系统内核交由tcp传给浏览器显示。所以,数据会依次写到这几个地方:echo/print -> php buffer -> tcp buffer -> browser

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)

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

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

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

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

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

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

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

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

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

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

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

output buffering函数

1.ob_get_level
返回输出缓冲机制的嵌套级别,可以防止模板重复嵌套自己。

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()使用。下面湿CI中加载模板的程序代码:

<SPAN style="WHITE-SPACE: pre">  </SPAN>/*
   * Buffer the output
   *
   * We buffer the output for two reasons:
   * 1. Speed. You get a significant speed boost.
   * 2. So that the final rendered template can be
   * post-processed by the output class.  Why do we
   * need post processing?  For one thing, in order to
   * show the elapsed page load time.  Unless we
   * can intercept the content right before it's sent to
   * the browser and then stop the timer it won't be accurate.
   */
  ob_start();
  // If the PHP installation does not support short tags we'll
  // do a little string replacement, changing the short tags
  // to standard PHP echo statements.
  if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
  {
                        //替换短标记<?=***>
   echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
  }
  else
  {
   include($_ci_path); // include() vs include_once() allows for multiple views with the same name
  }                //记录调试信息
  log_message('debug', 'File loaded: '.$_ci_path);
  // Return the file data if requested
  if ($_ci_return === TRUE)
  {
   $buffer = ob_get_contents();
   @ob_end_clean();
   return $buffer;
  }
  /*
   * Flush the buffer... or buff the flusher?
   *
   * In order to permit views to be nested within
   * other views, we need to flush the content back out whenever
   * we are beyond the first level of output buffering so that
   * it can be seen and included properly by the first included
   * template and any subsequent ones. Oy!
   *
   */
  if (ob_get_level() > $this->_ci_ob_level + 1)
  {
   ob_end_flush();
  }
  else
  {
                        //将模板内容添加到输出流中
   $_ci_CI->output->append_output(ob_get_contents());
                        //清除buffer
   @ob_end_clean();
  }

PHP 相关文章推荐
提升PHP执行速度全攻略(上)
Oct 09 PHP
web方式ftp
Oct 09 PHP
对javascript和select部件的结合运用
Oct 09 PHP
PHP 开源AJAX框架14种
Aug 24 PHP
一步一步学习PHP(4) php 函数 补充2
Feb 15 PHP
PHP批量采集下载美女图片的实现代码
Jun 03 PHP
php中使用GD库做验证码
Mar 31 PHP
PHP数组生成XML格式数据的封装类实例
Nov 10 PHP
php 实现Hash表功能实例详解
Nov 29 PHP
PHP验证码类ValidateCode解析
Jan 07 PHP
微信公众号OAuth2.0网页授权问题浅析
Jan 21 PHP
laravel框架使用极光推送消息操作示例
Feb 15 PHP
如何在PHP中使用正则表达式进行查找替换
Jun 13 #PHP
php启用zlib压缩文件的配置方法
Jun 12 #PHP
Window下PHP三种运行方式图文详解
Jun 11 #PHP
控制PHP的输出:缓存并压缩动态页面
Jun 11 #PHP
基于PHP导出Excel的小经验 完美解决乱码问题
Jun 10 #PHP
win7+apache+php+mysql环境配置操作详解
Jun 10 #PHP
浅谈php中mysql与mysqli的区别分析
Jun 10 #PHP
You might like
全国FM电台频率大全 - 28 甘肃省
2020/03/11 无线电
PHP Warning: Module 'modulename' already loaded in问题解决办法
2015/03/16 PHP
解析 thinkphp 框架中的部分方法
2017/05/07 PHP
PHP调用API接口实现天气查询功能的示例
2017/09/21 PHP
ExtJS 入门
2010/10/29 Javascript
jQuery中的$.ajax()方法应用
2014/05/06 Javascript
jQuery取得设置清空select选择的文本与值
2014/07/08 Javascript
javascript瀑布流式图片懒加载实例
2020/06/28 Javascript
Bootstarp风格的toggle效果分享
2016/02/23 Javascript
基于JavaScript实现百叶窗动画效果不只单纯flas可以实现
2016/02/29 Javascript
Javascript实现鼠标框选操作  不是点击选取
2016/04/14 Javascript
JS不用正则验证输入的字符串是否为空(包含空格)的实现代码
2016/06/14 Javascript
Bootstrap 3的box-sizing样式导致UEditor控件的图片无法正常缩放的解决方案
2016/09/15 Javascript
javascript实现右下角广告框效果
2017/02/01 Javascript
详解vue mixins和extends的巧妙用法
2017/12/20 Javascript
Three.js 再探 - 写一个微信跳一跳极简版游戏
2018/01/04 Javascript
jQuery选择器之基本过滤选择器用法实例分析
2019/02/19 jQuery
ElementUI Tag组件实现多标签生成的方法示例
2019/07/08 Javascript
[05:49]2014DOTA2TI4正赛第二日综述 昔日冠军纷纷落马 VG LGD占尽先机
2014/07/20 DOTA
[01:36:19]Secret vs NB 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
Python中装饰器的一个妙用
2015/02/08 Python
Python实现基于多线程、多用户的FTP服务器与客户端功能完整实例
2017/08/18 Python
Python使用pyautogui模块实现自动化鼠标和键盘操作示例
2018/09/04 Python
python-opencv颜色提取分割方法
2018/12/08 Python
在python中按照特定顺序访问字典的方法详解
2018/12/14 Python
浅谈python输出列表元素的所有排列形式
2020/02/26 Python
Python使用socketServer包搭建简易服务器过程详解
2020/06/12 Python
Python  word实现读取及导出代码解析
2020/07/09 Python
法国在线宠物店:zooplus.fr
2018/02/23 全球购物
写一个函数返回1+2+3+…+n的值(假定结果不会超过长整型变量的范围)
2014/09/05 面试题
我看到了用指针调用函数的不同语法形式
2014/07/16 面试题
Linux如何为某个操作添加别名
2013/03/01 面试题
final, finally, finalize的区别
2012/03/01 面试题
学术会议邀请函范文
2014/01/22 职场文书
2016年教育局“我们的节日——端午节”主题活动总结
2016/04/01 职场文书
基于CSS3画一个iPhone
2021/04/21 HTML / CSS