剖析 PHP 中的输出缓冲


Posted in PHP onDecember 21, 2006

我们先来看一段代码:

    〈?php
    for ($i=10; $i〉0; $i--)
    {
     echo $i;
     flush();
     sleep(1);
    }
    ?〉

    按照php手册里的说法,该函数将当前为止程序的所有输出发送到用户的浏览器。

    上面的这段代码,应该隔一秒钟输出一次$i。但是实际中却不一定是这样。有可能是等了10秒钟后,所有的输出同时呈现出来。

    好,我们来改一下这段代码,改成

    〈?php
    ob_end_clean();//修改部分
    for ($i=10; $i〉0; $i--)
    {
     echo $i;
     flush();
     sleep(1);
    }
    ?〉
    嘿,加了这一句ob_end_clean();,居然就OK了。实际上,我们把ob_end_clean()换成ob_end_flush()也一样OK。

    我再来改一改。

    〈?php
    for ($i=10; $i〉0; $i--)
    {
     echo $i;
     ob_flush();//修改部分
     flush();
     sleep(1);
    }
    ?〉
    运行一下,是不是发现$i也隔一秒输出一次了?这是为什么呢?
    别急,我们来看看php.ini。

    打开php.ini,搜索output_buffering,我们会看到类似这样的设置 output_buffering = 4096。正如它的名字output_buffering一样,这个设置的作用就是把输出缓冲一下,缓冲大小为4096bytes.

    在我们的第一段代码里,之所以没有按预期的输出,正是因为这个output_buffering把那些输出都缓冲了。没达到4096bytes或者脚本结束,输出是不会被发送出去的。

    而第二段代码中的ob_end_clean()和ob_end_flush()的作用,就是终止缓冲。这样就不用等到有4096bytes的缓冲之后才被发送出去了。

    第三段代码中,用了一句ob_flush(),它的作用就是把缓冲的数据发送出去,但是并不会终止缓冲,所以它必须在每次flush()前使用。

    如果不想使用ob_end_clean(),ob_end_flush()和ob_flush(),我们就必须把php.ini里的output_buffering设得足够小,例如设为0。需要注意的是,如果你打算在脚本中使用ini_set(”output_buffering”,”0″)来设置,那么请停下来吧,这种方法是不行的。因为在脚本一开始的时候,缓冲设置就已经被载入,然后缓冲就开始了。

    可能你会问了,既然ob_flush()是把缓冲的数据发送出去,那么为什么还需要用flush()???直接用下面这段代码不行吗??

    〈?php
    for ($i=10; $i〉0; $i--)
    {
     echo $i;
     ob_flush();
     sleep(1);
    }
    ?〉
    请注意ob_flush()和flush()的区别。前者是把数据从PHP的缓冲中释放出来,后者是把不在缓冲中的或者说是被释放出来的数据发送到浏览器。所以当缓冲存在的时候,我们必须ob_flush()和flush()同时使用。

    那是不是flush()在这里就是不可缺少的呢?不是的,我们还有另外一种方法,使得当有数据输出的时候,马上被发送到浏览器。下面这两段代码就是不需要使用flush()了。(当你把output_buffering设为0的时候,连ob_flush()和ob_end_clean()都不需要了)

    〈?php
    ob_implicit_flush(true);
    for ($i=10; $i〉0; $i--)
    {
     echo $i;
     ob_flush();
     sleep(1);
    }
    ?〉
    〈?php
    ob_end_clean();
    ob_implicit_flush(true);
    for ($i=10; $i〉0; $i--)
    {
     echo $i;
     sleep(1);
    }
    ?〉
    请注意看上面的ob_implicit_flush(true),这个函数强制每当有输出的时候,即刻把输出发送到浏览器。这样就不需要每次输出(echo)后,都用flush()来发送到浏览器了。

    以上所诉可能在某些浏览器中不成立。因为浏览器也有自己的规则。我是用Firefox1.5,IE6,opera8.5来测试的。其中opera就不能正常输出,因为它有一个规则,就是不遇到一个HTML标签,就绝对不输出,除非到脚本结束。而FireFox和IE还算比较正常的。

    最后附上一段非常有趣的代码,作者为PuTTYshell。在一个脚本周期里,每次输出,都会把前一次的输出覆盖掉。
    以下代码只在firefox下可用,其他浏览器并不支持multipart/x-mixed-replace的Content-Type.

    〈?php
      header('Content-type: multipart/x-mixed-replace;boundary=endofsection');
      print “\n--endofsection\n“;

      $pmt = array(“-“, “\\“, “|“, “/“ );
      for( $i = 0; $i 〈10; $i ++ ){
         sleep(1);
         print “Content-type: text/plain\n\n“;
         print “Part $i\t“.$pmt[$i % 4];
         print “--endofsection\n“;
         ob_flush();
         flush();
      }
      print “Content-type: text/plain\n\n“;
      print “The end\n“;
      print “--endofsection--\n“;
    ?〉 

PHP 相关文章推荐
PHP MSSQL 存储过程的方法
Dec 24 PHP
frename PHP 灵活文件命名函数 frename
Sep 09 PHP
PHP 处理图片的类实现代码
Oct 23 PHP
PHP 将图片按创建时间进行分类存储的实现代码
Jan 05 PHP
PHP上传文件时文件过大$_FILES为空的解决方法
Nov 26 PHP
php实现按指定大小等比缩放生成上传图片缩略图的方法
Dec 15 PHP
PHP5.4起内置web服务器使用方法
Aug 09 PHP
php代码检查代理ip的有效性
Aug 19 PHP
PHP实现随机生成水印图片功能
Mar 22 PHP
微信公众号实现会员卡领取功能
Jun 08 PHP
ThinkPHP框架实现的邮箱激活功能示例
Jun 15 PHP
Thinkphp5.0框架视图view的模板布局用法分析
Oct 12 PHP
PHP 的几个配置文件函数
Dec 21 #PHP
使用 MySQL 开始 PHP 会话
Dec 21 #PHP
PHP 编程请选择正确的文本编辑软件
Dec 21 #PHP
PHP 模板高级篇总结
Dec 21 #PHP
PHP函数utf8转gb2312编码
Dec 21 #PHP
小偷PHP+Html+缓存
Dec 20 #PHP
PR值查询 | PageRank 查询
Dec 20 #PHP
You might like
解析PHP工厂模式的好处
2013/06/18 PHP
Thinkphp 框架配置操作之动态配置、扩展配置及批量配置实例分析
2020/05/15 PHP
showModelessDialog()使用详解
2006/09/21 Javascript
javascript 面向对象编程  function是方法(函数)
2009/09/17 Javascript
JavaScript 页面编码与浏览器类型判断代码
2010/06/03 Javascript
Textarea与懒惰渲染实现代码
2012/01/04 Javascript
js调用AJAX时Get和post的乱码解决方法
2013/06/04 Javascript
js替代copy(示例代码)
2013/11/27 Javascript
jquery实现点击label的同时触发文本框点击事件的方法
2015/06/05 Javascript
JavaScript对象参数的引用传递
2016/01/14 Javascript
体验jQuery和AngularJS的不同点及AngularJS的迷人之处
2016/02/02 Javascript
解决JS内存泄露之js对象和dom对象互相引用问题
2017/06/25 Javascript
react native 文字轮播的实现示例
2018/07/27 Javascript
微信小程序跨页面传递data数据方法解析
2019/12/13 Javascript
微信小程序分享小程序码的生成(带参数)以及参数的获取
2020/03/25 Javascript
vue实现路由懒加载的3种方法示例
2020/09/01 Javascript
[03:04]DOTA2超级联赛专访ZSMJ “莫名其妙”的逆袭
2013/05/23 DOTA
[01:04]DOTA2上海特锦赛现场采访 FreeAgain遭众解说围攻
2016/03/25 DOTA
[58:09]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第三场 6.2
2018/06/03 DOTA
python基于xmlrpc实现二进制文件传输的方法
2015/06/02 Python
python3实现暴力穷举博客园密码
2016/06/19 Python
Python模拟三级菜单效果
2017/09/11 Python
Python生成数字图片代码分享
2017/10/31 Python
Python 函数返回值的示例代码
2019/03/11 Python
Python虚拟环境库virtualenvwrapper安装及使用
2020/06/17 Python
python 实现端口扫描工具
2020/12/18 Python
HTML5印章绘制电子签章图片(中文英文椭圆章、中文英文椭圆印章)
2019/06/03 HTML / CSS
使用layui实现左侧菜单栏及动态操作tab项的方法
2020/11/10 HTML / CSS
Ariat官网:美国马靴和服装品牌
2019/12/16 全球购物
Servlet的实例是在生命周期什么时候创建的?配置servlet最重要的是什么?
2012/05/30 面试题
服装设计专业毕业生求职信
2014/04/09 职场文书
副护士长竞聘演讲稿
2014/04/30 职场文书
期末评语大全
2014/05/04 职场文书
公路局群众路线教育实践活动第一阶段工作汇报
2014/10/25 职场文书
房产电话营销开场白
2015/05/29 职场文书
2015年秋季运动会前导词
2015/07/20 职场文书