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 编写大型网站问题集
May 07 PHP
php找出指定范围内回文数且平方根也是回文数的方法
Mar 23 PHP
PHP回溯法解决0-1背包问题实例分析
Mar 23 PHP
php自定义urlencode,urldecode函数实例
Mar 24 PHP
PHP利用hash冲突漏洞进行DDoS攻击的方法分析
Mar 26 PHP
Mac版PhpStorm之XAMPP整合apache服务器配置的图文教程详解
Oct 13 PHP
利用PHP生成CSV文件简单示例
Dec 21 PHP
Linux服务器下PHPMailer发送邮件失败的问题解决
Mar 04 PHP
PHP简单实现模拟登陆功能示例
Sep 15 PHP
Laravel实现短信注册的示例代码
May 29 PHP
PHP实现笛卡尔积算法的实例讲解
Dec 22 PHP
yii2.0框架多模型操作示例【添加/修改/删除】
Apr 13 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
如何将一个表单同时提交到两个地方处理
2006/10/09 PHP
php cout&amp;lt;&amp;lt;的一点看法
2010/01/24 PHP
php中突破基于HTTP_REFERER的防盗链措施(stream_context_create)
2011/03/29 PHP
PHP下判断网址是否有效的代码
2011/10/08 PHP
php在文件指定行中写入代码的方法
2012/05/23 PHP
浅析php过滤html字符串,防止SQL注入的方法
2013/07/02 PHP
php中使用PHPExcel读写excel(xls)文件的方法
2014/09/15 PHP
PHP程序员必须清楚的问题汇总
2014/12/18 PHP
php采用session实现防止页面重复刷新
2015/12/24 PHP
php生成验证码,缩略图及水印图的类分享
2016/04/07 PHP
详解PHP用substr函数截取字符串中的某部分
2016/12/03 PHP
页面装载js及性能分析方法介绍
2014/03/21 Javascript
教你如何在 Javascript 文件里使用 .Net MVC Razor 语法
2014/07/23 Javascript
node.js中的fs.appendFile方法使用说明
2014/12/17 Javascript
JavaScript中5种调用函数的方法
2015/03/12 Javascript
jquery.cookie实现的客户端购物车操作实例
2015/12/24 Javascript
js定时器实例分享
2016/12/20 Javascript
vue配置nprogress实现页面顶部进度条
2019/09/21 Javascript
微信小程序实现音乐播放器
2019/11/20 Javascript
js实现星星海特效的示例
2020/09/28 Javascript
Python 迭代器工具包【推荐】
2016/05/06 Python
python logging模块书写日志以及日志分割详解
2019/07/22 Python
pytorch中的上采样以及各种反操作,求逆操作详解
2020/01/03 Python
python3注册全局热键的实现
2020/03/22 Python
Keras自定义IOU方式
2020/06/10 Python
使用python求斐波那契数列中第n个数的值示例代码
2020/07/26 Python
HTML5对比HTML4的主要改变和改进总结
2016/05/27 HTML / CSS
澳大利亚波西米亚风连衣裙在线商店:Fortunate One
2019/04/01 全球购物
广州喜创信息技术有限公司JAVA软件工程师笔试题
2012/10/17 面试题
中学门卫岗位职责
2013/12/26 职场文书
物理专业大学生职业生涯规划书
2014/02/07 职场文书
综合素质自我评价怎么写
2014/09/14 职场文书
长城导游词
2015/01/30 职场文书
2015年社区卫生工作总结
2015/04/21 职场文书
我的中国梦心得体会范文
2016/01/05 职场文书
深度学习tensorflow基础mnist
2021/04/14 Python