php緩存機(jī)制的一點(diǎn)理解

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腳本已經(jīng)輸出內(nèi)容之后修改header部分蔫浆,則可以使用Output Control的相關(guān)方法追葡。

Output Control的方法不會(huì)影響header()和setcookie()方法。

eg1:

echo 123;
sleep(5);
echo 456;

輸出結(jié)果:

先輸出 123捐康,隔5s涮总,輸出456

eg2:

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

輸出結(jié)果

開(kāi)始沒(méi)有任何輸出巴柿,5s之后輸出123456

eg3:

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

輸出結(jié)果

先輸出a,然后過(guò)了5s輸出123 456

eg3:

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

輸出結(jié)果

  • 先輸出字符 a
  • 5s后輸出123
  • 再過(guò)5s后輸出456
  • 再過(guò)5s后輸出789

ob_end_flush()會(huì)將緩沖區(qū)中的內(nèi)容輸出侠坎,然后關(guān)閉緩沖區(qū)咒循。所以輸出123之后隔5s就會(huì)輸出456杀餐。因?yàn)槿绻藭r(shí)緩沖區(qū)未關(guān)閉干发,則456和789都會(huì)被保存到緩沖區(qū),直到程序結(jié)束的時(shí)候一起輸出史翘,而不是輸出456之后隔了5s才輸出789

eg4:

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

輸出結(jié)果

該例子和eg3的不同之處在于此例子使用了ob_flush(),eg3使用了ob_end_flush(),而ob_flush() 只是將緩沖區(qū)中的內(nèi)容輸出但是并不會(huì)關(guān)閉緩沖區(qū)枉长,所以這也導(dǎo)致了eg4的輸出行為和eg3不同。eg4的輸出順序如下:

  • 先輸出字符 a
  • 5秒之后輸出123
  • 10s之后同時(shí)輸出456 789

因?yàn)?code>ob_flush()只是將當(dāng)前緩沖區(qū)中的內(nèi)容輸出琼讽,并沒(méi)有關(guān)閉緩沖區(qū)必峰,所以輸出123之后,緩沖區(qū)仍然存在跨琳,456和789仍然保存在當(dāng)前緩沖區(qū)中自点,直到程序結(jié)束之后才將緩沖區(qū)中的內(nèi)容全部輸出,所以456和789被同時(shí)輸出脉让。

eg5:測(cè)試ob_start()的回調(diào)函數(shù)

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;

輸出結(jié)果如下:

123123
456456
789789

結(jié)果說(shuō)明了當(dāng)調(diào)用ob_flush()之后,會(huì)調(diào)用ob_callback()方法桂敛。其他的幾種情況可以再測(cè)試一下。如果代碼結(jié)束的時(shí)候緩沖區(qū)仍存在溅潜,則會(huì)自動(dòng)刷新緩沖區(qū)中的內(nèi)容术唬,并且執(zhí)行ob_callback()

eg6 測(cè)試ob_start()的chunk_size參數(shù)使用方法

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;

本例的輸出結(jié)果很有意思,輸出結(jié)果如下:

  • 立即輸出 1234512345
  • 5s之后輸出換行符
  • 再過(guò)5s之后輸出 456
  • 再過(guò)5s輸出789

分析原因:

  1. ob_start()的chunk_size=4滚澜,但是第一次 echo 12345粗仓;這個(gè)字符串的長(zhǎng)度已經(jīng)超過(guò)了4,所以直接輸出了设捐,輸出的時(shí)候調(diào)用了 ob_callback()方法
  2. 過(guò)了5s借浊,ob_flush()刷新緩沖區(qū),但是此時(shí)緩沖區(qū)中的內(nèi)容為空,所以執(zhí)行ob_callback()的時(shí)候輸出一個(gè)換行
  3. 接著執(zhí)行 echo 456萝招;長(zhǎng)度小于4蚂斤,所以執(zhí)行sleep(5) 之后執(zhí)行ob_end_flush(),刷新緩沖區(qū)并且執(zhí)行ob_callback(),輸出 456456
  4. ob_end_flush()已經(jīng)關(guān)閉了緩沖區(qū),所以接下來(lái)過(guò)了5s之后輸出 789

eg7 測(cè)試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";

輸出結(jié)果如下:

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

從結(jié)果中可以看出來(lái):里面緩沖區(qū)的內(nèi)容先被inner()處理了一次槐沼,然后被flush到外層的緩沖區(qū)曙蒸,然后又被外層的outer()處理了一次捌治,然后才最終的輸出。

PHP手冊(cè)中的說(shuō)明如下

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 是棧結(jié)構(gòu)嵌套的纽窟。你可以在一個(gè)ob_start() 中嵌套另一個(gè)ob_start(),這樣就形成了一個(gè)棧結(jié)構(gòu)形式的緩沖區(qū)肖油。必須保證調(diào)用相應(yīng)次數(shù)的ob_end_flush()才可以。如果有多個(gè)ob_start()注冊(cè)了回調(diào)函數(shù)臂港,那么里層的緩沖區(qū)內(nèi)容會(huì)依次被所有外層的回調(diào)函數(shù)處理森枪,最終生成輸出結(jié)果。

eg8 測(cè)試ob_get_clean()ob_clean()

ob_start();
echo 'Text that won\'t get displayed.';
ob_clean();
// ob_end_clean();
echo ob_get_level();

觀察輸出結(jié)果就可以知道這兩個(gè)函數(shù)的作用:二者都會(huì)清空緩沖區(qū)的內(nèi)容(所以緩沖區(qū)的內(nèi)容會(huì)變空)趋艘,不同之處在于ob_clean()不刪除緩沖區(qū),而ob_get_clean()則會(huì)刪除緩沖區(qū)

eg9 如何證明調(diào)用ob_get_clean()的時(shí)候會(huì)執(zhí)行在ob_start()中注冊(cè)的方法

$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;

輸出結(jié)果:5

分析說(shuō)明疲恢,因?yàn)檎{(diào)用ob_end_clean()會(huì)把緩沖區(qū)中的內(nèi)容清空并且刪除緩沖區(qū)凶朗,所以我使用了一個(gè)全局的變量來(lái)測(cè)試調(diào)用ob_end_clean()的時(shí)候是否會(huì)執(zhí)行注冊(cè)的回調(diào)函數(shù)瓷胧,結(jié)果證明是可以的。

eg10 證明調(diào)用ob_clean()的時(shí)候會(huì)執(zhí)行ob_start()中注冊(cè)的方法

$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;

輸出結(jié)果:3---2

輸出結(jié)果分析:調(diào)用ob_clean()的時(shí)候會(huì)把緩沖清空棚愤,所以echo 'Text that won\'t get displayed.';這句話不會(huì)被輸出搓萧。并且ob_clean()不會(huì)刪除緩沖區(qū),所以echo $flag;的時(shí)候緩沖區(qū)仍存在宛畦,所以程序最終輸出的時(shí)候會(huì)執(zhí)行ob_start()注冊(cè)的方法瘸洛。最終的結(jié)果輸出3---2,因?yàn)槊看握{(diào)用回調(diào)函數(shù)的時(shí)候都會(huì)將 $falg加1次和,所以我們可以得出這個(gè)結(jié)果反肋,echo $flag;的時(shí)候$flag的值為2,也就是說(shuō)前面已經(jīng)執(zhí)行了一次outer()方法踏施,所以只能是執(zhí)行ob_clean()的時(shí)候執(zhí)行了注冊(cè)的回調(diào)函數(shù)石蔗。

eg11 測(cè)試嵌套的情況下各個(gè)函數(shù)的作用

ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_start();
echo "<level  3> ";
echo ob_get_level();

輸出結(jié)果如下,很簡(jiǎn)單:

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

最后的3表示現(xiàn)在有三層嵌套關(guān)系

eg12 嵌套下的ob_clean()

ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_start();
echo "<level  3> ";
ob_clean();

輸出結(jié)果如下:

<level 1> <level 2 >

說(shuō)出結(jié)果說(shuō)明了ob_get_clean()是將最內(nèi)層(也就是最新)的緩沖區(qū)清空了。

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);

輸出結(jié)果畅形,注意時(shí)間順序:

經(jīng)過(guò)5s之后才輸出<level 1>养距,然后程序結(jié)束

修改一下上面的程序

ob_start();
echo "<level  1> ";
ob_flush();
sleep(5);

輸出結(jié)果:

立即輸出<level 1>,過(guò)了5s程序結(jié)束

分析上面的兩個(gè)程序的執(zhí)行結(jié)果可以得出結(jié)論:ob_flush()會(huì)立即輸出緩沖區(qū)的內(nèi)容

再次修改上面的程序日熬,使用嵌套緩沖

ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
sleep(5);

輸出結(jié)果如下:

過(guò)了5s之后輸出<level 1> <level 2 >

再次修改程序

ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_flush();
sleep(5);

輸出結(jié)果如下:

過(guò)了5s之后輸出 <level 1> <level 2 >

為什么我們使用了 ob_flush()卻沒(méi)有立即輸出緩沖區(qū)的內(nèi)容呢棍厌?顯然我們的 ob_flush()只是將內(nèi)層的<level 2 >flush到了外層,所以5s之后輸出竖席。

修改程序來(lái)驗(yàn)證我們上面的結(jié)論:

ob_start();
echo "<level  1> ";
ob_start();
echo "<level  2 >";
ob_end_flush();//①
ob_flush();//②
sleep(5);

輸出結(jié)果:

直接輸出<level 1> <level 2 >耘纱,然后程序過(guò)了5s之后才結(jié)束

這里我們使用了bo_end_flush()來(lái)刷新并刪除內(nèi)層緩沖區(qū),如果使用ob_flush()則不會(huì)刪除內(nèi)層緩沖區(qū)毕荐,這樣的話第②個(gè)ob_flush()刷新的仍然是內(nèi)層緩沖區(qū)束析,起不到作用。

我們也可以用下面的程序來(lái)驗(yàn)證上述觀點(diǎn):

ob_start();
echo "<level  1> ";
ob_flush();//①
ob_start();
echo "<level  2 >";
ob_flush();//②
sleep(5);

輸出結(jié)果:

先輸出<level 1>东跪,過(guò)了5s之后再輸出<level 2 >

結(jié)果分析:

第一個(gè)ob_flush()刷新了最外層緩沖區(qū)畸陡,所以直接輸出了<level 1>鹰溜,第二個(gè)ob_flush()只是將<level 2>flush到了外層的緩沖區(qū)中,所以只能等到程序結(jié)束的時(shí)候才被真正的輸出丁恭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末曹动,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子牲览,更是在濱河造成了極大的恐慌墓陈,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件第献,死亡現(xiàn)場(chǎng)離奇詭異贡必,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)庸毫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)仔拟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人飒赃,你說(shuō)我怎么就攤上這事利花。” “怎么了载佳?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵炒事,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蔫慧,道長(zhǎng)挠乳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任姑躲,我火速辦了婚禮睡扬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肋联。我一直安慰自己威蕉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布橄仍。 她就那樣靜靜地躺著韧涨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侮繁。 梳的紋絲不亂的頭發(fā)上虑粥,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音宪哩,去河邊找鬼娩贷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛锁孟,可吹牛的內(nèi)容都是我干的彬祖。 我是一名探鬼主播茁瘦,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼储笑!你這毒婦竟也來(lái)了甜熔?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤突倍,失蹤者是張志新(化名)和其女友劉穎腔稀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體羽历,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焊虏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秕磷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诵闭。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖跳夭,靈堂內(nèi)的尸體忽然破棺而出涂圆,到底是詐尸還是另有隱情们镜,我是刑警寧澤币叹,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站模狭,受9級(jí)特大地震影響颈抚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嚼鹉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一贩汉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锚赤,春花似錦匹舞、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至浑侥,卻和暖如春姊舵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寓落。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工括丁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伶选。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓史飞,卻偏偏與公主長(zhǎng)得像尖昏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子构资,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • PHP的輸出緩沖區(qū) 什么是緩沖區(qū)?簡(jiǎn)單而言,緩沖區(qū)的作用就是,把輸入或者輸出的內(nèi)容先放進(jìn)內(nèi)存,而不顯示或者讀取.至...
    桖辶殤閱讀 2,012評(píng)論 3 12
  • PHP輸出控制函數(shù) ob_start ([ callback $output_callback [, int $c...
    Gundy_閱讀 363評(píng)論 0 3
  • ob的基本原則:如果ob緩存打開(kāi)会宪,則echo的數(shù)據(jù)首先放在ob緩存。如果是header信息蚯窥,直接放在程序緩存掸鹅。當(dāng)頁(yè)...
    金星show閱讀 993評(píng)論 0 1
  • 1、判斷一個(gè)變量是否存在的函數(shù):isset判斷一個(gè)變量是否為null的函數(shù):is_null判斷一個(gè)變量是否為空的函...
    jianghu000閱讀 1,698評(píng)論 1 4
  • 時(shí)間是賊偷走一切拦赠,很多人還來(lái)不及吻過(guò)誰(shuí) 的臉就消散在人海茫茫巍沙。七歲那年以為抓住那只蟬就能抓住整個(gè)夏天『墒螅可是它從不會(huì)...
    漓若淮然閱讀 278評(píng)論 0 4