2009年8月2日 星期日

PHP:flush跟ob_flush

今天看到一個範例 用了flush跟ob_flush 在想到底有什麼不同
後來查了PHP手冊
http://tw.php.net/manual/en/book.outcontrol.php

在順序上,必須先用ob_flush再用flush

ob_flush();
flush();


ob_flush是把在PHP緩衝區(output_buffer)(假設有打開)的東西輸出,但並不是立刻輸出到螢幕上
flush則是把非PHP緩衝區,伺服器上準備輸出的資料輸出到瀏覽器上"顯示出來"

因此順序上必須先用ob_flush()把緩衝區上的資料輸出後,才能用flush()把從緩衝區輸出的資料給列印到螢幕上,不然光用ob_flush(),把緩衝區清空他並不會立刻把資料輸出到螢幕上
而只用flush(),資料全部在緩衝區他也沒東西可列印到螢幕上

網路上最常看到的例子

for($i=0;$i<10;$i++)
{
echo $i;
ob_flush();
flush();
sleep(1);
}

上面這隻程式會一秒輸出一個數字到螢幕上,可以試試把flush()或ob_flush()其中一個拿掉
就會變成十秒後全部輸出,而不是一次輸出一個字
上述狀況是在output_buffer打開的情況下,因為echo的東西會先把資料存到output_buffer
再從output_buffer頃印到螢幕上
上述程式碼結果其實等同於

ob_end_clean();//關閉output_buffer
for($i=0;$i<10;$i++)
{
echo $i;
flush();
sleep(1);
}

在output_buffer關閉的情況下,輸出資料不會搬到緩衝區,可以直接用flush()把他頃印出來

至於output_buffer打開的方式有兩種
第一種是修改php.ini的output_buffer選項

output_buffer = off

改成

output_buffer = N

N是一個數字,文件上建議是改成4096

第二種方法則是用他提供的ob_start()函數

用法可以參照手冊,而要關閉output_buffer則是使用ob_end_clean 或ob_end_flush
  • ob_end_clean — Clean (erase) the output buffer and turn off output buffering
  • ob_end_flush — Flush (send) the output buffer and turn off output buffering


所以前面提到的例子完整一點,不修改php.ini的話應該是要

ob_start();//打開output_buffer
for($i=0;$i<10;$i++)
{
echo $i;
ob_flush();
flush();
sleep(1);
}
ob_end_clean();//關閉output_buffer


那就有個疑問,為何這麼麻煩要把資料先搬到緩衝呢
有個說法是可以提高效率
而官方另一種有說服力的理由,配合header修改
http://tw.php.net/manual/en/outcontrol.examples.basic.php


ob_start();
echo "Hello\n";
setcookie("cookiename", "cookiedata");

ob_end_flush();

上面這段程式,如果在output_buffer關閉的情況下使使用,會出現

Cannot modify header information - headers already sent

這段錯誤訊息
為什麼呢?看一下setcookie的說明

setcookie() defines a cookie to be sent along with the rest of the HTTP headers.
Like other headers,cookies must be sent before any output from your script (this is a protocol restriction).
This requires that you place calls to this function prior to any output,
including <html> and <head> tags as well as any whitespace.

上面的說明很清楚了,會動到header的部分,但是前幾篇文章有提到
header輸出之前不能有任何output到瀏覽器螢幕上,不然就會出現上面的error
http://hatsukiakio.blogspot.com/2009/07/cannot-send-session-cache-limiter.html


所以可以透過output_buffer先把輸出資料鎖在output_buffer裡面
這樣就可以執行header相關函數了

P.S output_buffer必須夠大,不然output_buffer滿了他一樣會輸出到螢幕上
這樣的話header函數依然會有危險

沒有留言: