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
分析原因:
-
ob_start()
的chunk_size=4滚澜,但是第一次 echo 12345粗仓;這個(gè)字符串的長(zhǎng)度已經(jīng)超過(guò)了4,所以直接輸出了设捐,輸出的時(shí)候調(diào)用了ob_callback()
方法 - 過(guò)了5s借浊,
ob_flush()
刷新緩沖區(qū),但是此時(shí)緩沖區(qū)中的內(nèi)容為空,所以執(zhí)行ob_callback()的時(shí)候輸出一個(gè)換行 - 接著執(zhí)行 echo 456萝招;長(zhǎng)度小于4蚂斤,所以執(zhí)行sleep(5) 之后執(zhí)行
ob_end_flush()
,刷新緩沖區(qū)并且執(zhí)行ob_callback()
,輸出 456456 -
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()
處理了一次捌治,然后才最終的輸出。
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 >