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,不用COM,生成excel文件
Oct 09 PHP
php visitFile()遍历指定文件夹函数
Aug 21 PHP
php入门学习知识点四 PHP正则表达式基本应用
Jul 14 PHP
PHP中获取内网用户MAC地址(WINDOWS/linux)的实现代码
Aug 11 PHP
PHP容易忘记的知识点分享
Apr 30 PHP
php遍历文件夹和文件列表示例分享
Mar 11 PHP
PHP将session信息存储到数据库的类实例
Mar 04 PHP
试用php中oci8扩展
Jun 18 PHP
腾讯CMEM的PHP扩展编译安装方法
Sep 25 PHP
PHP中header函数的用法及其注意事项详解
Jun 13 PHP
在Laravel 中实现是否关注的示例
Oct 22 PHP
YII2框架中添加自定义模块的方法实例分析
Mar 18 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
基于empty函数的输出详解
2013/06/17 PHP
php实现指定字符串中查找子字符串的方法
2015/03/17 PHP
php中preg_match的isU代表什么意思
2015/10/01 PHP
一个可绑定数据源的jQuery数据表格插件
2010/07/17 Javascript
基于JQuery制作的产品广告效果
2010/12/08 Javascript
基于jquery实现状态限定编辑的代码
2012/02/11 Javascript
修改jQuery Validation里默认的验证方法
2012/02/14 Javascript
ajax中get和post的说明及使用与区别
2012/12/23 Javascript
jquery通过select列表选择框对表格数据进行过滤示例
2014/05/07 Javascript
jQuery移除元素自动解绑事件实现思路及代码
2014/05/31 Javascript
jQuery+ajax实现无刷新级联菜单示例
2015/05/21 Javascript
分享纯手写漂亮的表单验证
2015/11/19 Javascript
基于JavaScript实现网页倒计时自动跳转代码
2015/12/28 Javascript
javascript回到顶部特效
2016/07/30 Javascript
Vue.js bootstrap前端实现分页和排序
2017/03/10 Javascript
jQuery源码解读之extend()与工具方法、实例方法详解
2017/03/30 jQuery
jQuery简单实现的HTML页面文本框模糊匹配查询功能完整示例
2018/05/09 jQuery
puppeteer实现html截图的示例代码
2019/01/10 Javascript
微信小程序实现导航栏和内容上下联动功能代码
2020/06/29 Javascript
解决vue刷新页面以后丢失store的数据问题
2020/08/11 Javascript
[06:49]2018DOTA2国际邀请赛寻真——VirtusPro傲视群雄
2018/08/12 DOTA
python字符串,数值计算
2016/10/05 Python
python 给DataFrame增加index行名和columns列名的实现方法
2018/06/08 Python
利用Python正则表达式过滤敏感词的方法
2019/01/21 Python
Python实现的列表排序、反转操作示例
2019/03/13 Python
用python wxpy管理微信公众号并利用微信获取自己的开源数据
2019/07/30 Python
python3 map函数和filter函数详解
2019/08/26 Python
使用Python进行防病毒免杀解析
2019/12/13 Python
基于Django实现日志记录报错信息
2019/12/17 Python
Python数据可视化图实现过程详解
2020/06/12 Python
Python截图并保存的具体实例
2021/01/14 Python
详解CSS3中常用的样式【基本文本和字体样式】
2020/10/20 HTML / CSS
创造美妙香氛体验:Aera扩散器和香水
2018/11/25 全球购物
说一下mysql, oracle等常见数据库的分页实现方案
2012/09/29 面试题
给女朋友道歉的话大全
2015/01/20 职场文书
关于销售人员的年终工作总结要点
2019/08/15 职场文书