php缓冲机制的简单了解

Output Control

The Output Control functions allow you to control when output is sent from the script. This can be useful in several different situations, especially if you need to send headers to the browser after your script has begun outputting data. The Output Control functions do not affect headers sent using header() or setcookie(), only functions such as echo and data between blocks of PHP code.
Output Control的用处:比如你想在php脚本已经输出内容之后修改header部分,则可以使用Output Control的相关方法。
Output Control的方法不会影响header()和setcookie()方法。

eg1:

echo 123;
sleep(5);
echo 456;

输出结果:

先输出 123,隔5s,输出456

eg2:

ob_start();
echo 123;
sleep(5);
echo 456;

输出结果

开始没有任何输出,5s之后输出123456

eg3:

echo "a";
ob_start();
echo 123;
sleep(5);
echo 456;

输出结果

先输出a,然后过了5s输出123 456

eg3:

echo "a";
ob_start();
echo 123;
sleep(5);
ob_end_flush();
sleep(5);
echo 456;
sleep(5);
echo 789;

输出结果

  • 先输出字符 a
  • 5s后输出123
  • 再过5s后输出456
  • 再过5s后输出789

ob_end_flush()会将缓冲区中的内容输出,然后关闭缓冲区。所以输出123之后隔5s就会输出456。因为如果此时缓冲区未关闭,则456和789都会被保存到缓冲区,直到程序结束的时候一起输出,而不是输出456之后隔了5s才输出789

eg4:

echo "a";
ob_start();
echo 123;
sleep(5);
ob_flush();
sleep(5);
echo 456;
sleep(5);
echo 789;

输出结果

该例子和eg3的不同之处在于此例子使用了ob_flush(),eg3使用了ob_end_flush(),而ob_flush() 只是将缓冲区中的内容输出但是并不会关闭缓冲区,所以这也导致了eg4的输出行为和eg3不同。eg4的输出顺序如下:
  • 先输出字符 a
  • 5秒之后输出123
  • 10s之后同时输出456 789
因为ob_flush()只是将当前缓冲区中的内容输出,并没有关闭缓冲区,所以输出123之后,缓冲区仍然存在,456和789仍然保存在当前缓冲区中,直到程序结束之后才将缓冲区中的内容全部输出,所以456和789被同时输出。

eg5:测试ob_start()的回调函数

function ob_callback($string){
	return $string.$string."\n";
}

ob_start("ob_callback");
echo 123;
sleep(5);
ob_flush();
sleep(5);
echo 456;
ob_flush();
sleep(5);
echo 789;
输出结果如下:
123123
456456
789789
结果说明了当调用ob_flush()之后,会调用ob_callback()方法。其他的几种情况可以再测试一下。如果代码结束的时候缓冲区仍存在,则会自动刷新缓冲区中的内容,并且执行ob_callback()

eg6 测试ob_start()的chunk_size参数使用方法

function ob_callback($string){
return $string.$string."\n";
}

ob_start("ob_callback",4);
echo 12345;
sleep(5);
ob_flush();
echo 456;
sleep(5);
ob_end_flush();
sleep(5);
echo 789;
本例的输出结果很有意思,输出结果如下:
  • 立即输出 1234512345
  • 5s之后输出换行符
  • 再过5s之后输出 456
  • 再过5s输出789
分析原因:
  1. ob_start()的chunk_size=4,但是第一次 echo 12345;这个字符串的长度已经超过了4,所以直接输出了,输出的时候调用了 ob_callback()方法
  2. 过了5s,ob_flush()刷新缓冲区,但是此时缓冲区中的内容为空,所以执行ob_callback()的时候输出一个换行
  3. 接着执行 echo 456;长度小于4,所以执行sleep(5) 之后执行ob_end_flush(),刷新缓冲区并且执行ob_callback(),输出 456456
  4. ob_end_flush()已经关闭了缓冲区,所以接下来过了5s之后输出 789

eg7 测试PHP的缓冲嵌套

function outer($string){
	return "  <--outer-->  ".$string.'  <--outer-->  ';
}

function inner($string){
	return "  |--inner--|  ".$string.'  |--inner--|  ';
}

ob_start("outer");
echo "1:blah";
ob_start("inner");
echo "2:blah";
输出结果如下:

<--outer--> 1:blah |--inner--| 2:blah |--inner--| <--outer-->

从结果中可以看出来:里面缓冲区的内容先被inner()处理了一次,然后被flush到外层的缓冲区,然后又被外层的outer()处理了一次,然后才最终的输出。

PHP手册中的说明如下

Output buffers are stackable, that is, you may call ob_start() while another ob_start() is active. Just make sure that you call ob_end_flush() the appropriate number of times. If multiple output callback functions are active, output is being filtered sequentially through each of them in nesting order.
Output buffers 是栈结构嵌套的。你可以在一个ob_start() 中嵌套另一个ob_start(),这样就形成了一个栈结构形式的缓冲区。必须保证调用相应次数的ob_end_flush()才可以。如果有多个ob_start()注册了回调函数,那么里层的缓冲区内容会依次被所有外层的回调函数处理,最终生成输出结果。
eg8 测试ob_get_clean()ob_clean()
ob_start();
echo 'Text that won\'t get displayed.';
ob_clean();
// ob_end_clean();
echo ob_get_level();
观察输出结果就可以知道这两个函数的作用:二者都会清空缓冲区的内容(所以缓冲区的内容会变空),不同之处在于ob_clean()不删除缓冲区,而ob_get_clean()则会删除缓冲区
eg9 如何证明调用ob_get_clean()的时候会执行在ob_start()中注册的方法
$flag = 1;

function outer($string){
$GLOBALS['flag'] = 5;
// return "  <--outer-->  ".$string.'  <--outer-->  ';
}

ob_start("outer");
echo 'Text that won\'t get displayed.';
$content = ob_get_contents();
ob_end_clean();
echo $flag;
输出结果:5
分析说明,因为调用ob_end_clean()会把缓冲区中的内容清空并且删除缓冲区,所以我使用了一个全局的变量来测试调用ob_end_clean()的时候是否会执行注册的回调函数,结果证明是可以的。
eg10 证明调用ob_clean()的时候会执行ob_start()中注册的方法
$flag = 1;

function outer($string){
$GLOBALS['flag'] = $GLOBALS['flag'] + 1;
return $GLOBALS['flag'].'---'.$string;
}

ob_start("outer");
echo 'Text that won\'t get displayed.';
ob_clean();

echo $flag;
输出结果:3---2
输出结果分析:调用ob_clean()的时候会把缓冲清空,所以echo 'Text that won\'t get displayed.';这句话不会被输出。并且ob_clean()不会删除缓冲区,所以echo $flag;的时候缓冲区仍存在,所以程序最终输出的时候会执行ob_start()注册的方法。最终的结果输出3---2,因为每次调用回调函数的时候都会将 $falg加1,所以我们可以得出这个结果,echo $flag;的时候$flag的值为2,也就是说前面已经执行了一次outer()方法,所以只能是执行ob_clean()的时候执行了注册的回调函数。
eg11 测试嵌套的情况下各个函数的作用
ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_start();
echo "<level  3> ";
echo ob_get_level();
输出结果如下,很简单:

<level 1> <level 2 ><level 3> 3

最后的3表示现在有三层嵌套关系
eg12 嵌套下的ob_clean()
ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_start();
echo "<level  3> ";
ob_clean();
输出结果如下:

<level 1> <level 2 >

说出结果说明了ob_get_clean()是将最内层(也就是最新)的缓冲区清空了。
eg13 嵌套下的ob_end_clean()
ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_start();
echo "<level  3> ";
ob_end_clean();
ob_end_clean();
ob_get_clean();
echo ob_get_level();
eg14 嵌套缓冲下的ob_flush()和ob_end_flush()的作用
ob_start();
echo "<level  1> ";
sleep(5);
输出结果,注意时间顺序:

经过5s之后才输出<level 1>,然后程序结束

修改一下上面的程序
ob_start();
echo "<level  1> ";
ob_flush();
sleep(5);
输出结果:

立即输出<level 1>,过了5s程序结束

分析上面的两个程序的执行结果可以得出结论:ob_flush()会立即输出缓冲区的内容
再次修改上面的程序,使用嵌套缓冲
ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
sleep(5);
输出结果如下:

过了5s之后输出<level 1> <level 2 >

再次修改程序
ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_flush();
sleep(5);
输出结果如下:

过了5s之后输出 <level 1> <level 2 >

为什么我们使用了 ob_flush()却没有立即输出缓冲区的内容呢?显然我们的 ob_flush()只是将内层的<level 2 >flush到了外层,所以5s之后输出。
修改程序来验证我们上面的结论:
ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_end_flush();//①
ob_flush();//②
sleep(5);

输出结果:

直接输出<level 1> <level 2 >,然后程序过了5s之后才结束

这里我们使用了bo_end_flush()来刷新并删除内层缓冲区,如果使用ob_flush()则不会删除内层缓冲区,这样的话第②个ob_flush()刷新的仍然是内层缓冲区,起不到作用。
我们也可以用下面的程序来验证上述观点:
ob_start();
echo "<level  1> ";
ob_flush();//①
ob_start();
echo "<level  2 >";
ob_flush();//②
sleep(5);
输出结果:

先输出<level 1>,过了5s之后再输出<level 2 >

结果分析:
第一个ob_flush()刷新了最外层缓冲区,所以直接输出了<level 1>,第二个ob_flush()只是将<level 2>flush到了外层的缓冲区中,所以只能等到程序结束的时候才被真正的输出。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值