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读取纯真ip数据库使用示例
Jan 26 PHP
php过滤html中的其他网站链接的方法(域名白名单功能)
Apr 24 PHP
PHP生成短网址的3种方法代码实例
Jul 08 PHP
百度实时推送api接口应用示例
Oct 21 PHP
PHP中使用SimpleXML检查XML文件结构实例
Jan 07 PHP
PHP中key和current,next的联合运用实例分析
Mar 29 PHP
详解Yii2 rules 的验证规则
Dec 02 PHP
PHP Laravel 上传图片、文件等类封装
Aug 16 PHP
PHP封装的XML简单操作类完整实例
Nov 13 PHP
PHP-FPM 的管理和配置详解
Feb 17 PHP
laravel-admin 在列表页添加自定义按钮的例子
Sep 30 PHP
PHP连接MySQL数据库的三种方式实例分析【mysql、mysqli、pdo】
Nov 04 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
php 需要掌握的东西 不做浮躁的人
2009/12/28 PHP
paypal即时到账php实现代码
2010/11/28 PHP
PHP的一个基础知识 表单提交
2011/07/04 PHP
php中get_headers函数的作用及用法的详细介绍
2013/04/27 PHP
ThinkPHP关于session的操作方法汇总
2014/07/18 PHP
PHP共享内存用法实例分析
2016/02/12 PHP
Yii核心验证器api详解
2016/11/23 PHP
PHP用户注册邮件激活账户的实现代码
2017/05/31 PHP
PHP实现的解汉诺塔问题算法示例
2018/08/06 PHP
懒就要懒到底——鼠标自动点击(含时间判断)
2007/02/20 Javascript
javascript setTimeout和setInterval 的区别
2009/12/08 Javascript
JavaScript高级程序设计 读书笔记之十一 内置对象Global
2012/03/07 Javascript
别了 JavaScript中的isXX系列
2012/08/01 Javascript
jquery+html5制作超酷的圆盘时钟表
2015/04/14 Javascript
Avalon中文长字符截取、关键字符隐藏、自定义过滤器
2016/05/18 Javascript
浅谈JavaScript函数的四种存在形态
2016/06/08 Javascript
BootStrap智能表单实战系列(六)表单编辑页面的数据绑定
2016/06/13 Javascript
基于bootstrop常用类总结(推荐)
2017/09/11 Javascript
vue项目中的webpack-dev-sever配置方法
2017/12/14 Javascript
jquery实现楼层滚动效果
2018/01/01 jQuery
使用async、enterproxy控制并发数量的方法详解
2018/01/02 Javascript
jQuery实现的回车触发按钮事件功能示例
2018/03/25 jQuery
Vue的自定义组件不能使用click方法的解决
2020/07/28 Javascript
jquery实现抽奖功能
2020/10/22 jQuery
Python网络爬虫项目:内容提取器的定义
2016/10/25 Python
python实现搜索文本文件内容脚本
2018/06/22 Python
解决python 自动安装缺少模块的问题
2018/10/22 Python
pyqt弹出新对话框,以及关闭对话框获取数据的实例
2019/06/18 Python
python使用ctypes调用扩展模块的实例方法
2020/01/28 Python
美国零售商店:Blue&Cream
2017/04/07 全球购物
英国异国风情旅游网站:Travel Talk Tours(团体旅游、探险旅游、帆船假期)
2018/07/26 全球购物
比较基础的php面试题及答案-编程题
2012/10/14 面试题
村党支部书记个人对照材料汇报
2014/10/26 职场文书
2015试用期转正工作总结
2014/12/12 职场文书
2016年会开场白台词
2015/06/01 职场文书
信仰观后感
2015/06/03 职场文书