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到了外层的缓冲区中,所以只能等到程序结束的时候才被真正的输出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • PHP的输出缓冲区 什么是缓冲区?简单而言,缓冲区的作用就是,把输入或者输出的内容先放进内存,而不显示或者读取.至...
    桖辶殇阅读 1,948评论 3 12
  • PHP输出控制函数 ob_start ([ callback $output_callback [, int $c...
    Gundy_阅读 352评论 0 3
  • ob的基本原则:如果ob缓存打开,则echo的数据首先放在ob缓存。如果是header信息,直接放在程序缓存。当页...
    金星show阅读 970评论 0 1
  • 1、判断一个变量是否存在的函数:isset判断一个变量是否为null的函数:is_null判断一个变量是否为空的函...
    jianghu000阅读 1,599评论 1 4
  • 时间是贼偷走一切,很多人还来不及吻过谁 的脸就消散在人海茫茫。七岁那年以为抓住那只蝉就能抓住整个夏天。可是它从不会...
    漓若淮然阅读 269评论 0 4