PHP的ob_flush()與flush()區別

笨小孩丶發表於2017-03-24

說明:純屬個人實踐中的理解以及一些總結,理解有誤或有疑問之處,請告知,共同進步。

一、首先說一下buffer

buffer是指記憶體地址空間。
1、Linux系統預設大小一般為4096(4kb)。
其主要用是儲存速度不同步或者優先順序不同的裝置之間傳處理資料的區域,可以使程式之間的相互等待變少。例如,當你開啟一個編輯器,輸入字元,作業系統並不會立即把這個字元直接寫入到磁碟,而是先寫入到buffer,當寫滿了一個buffer的時候,才會把buffer中的資料寫入磁碟,當呼叫核心函式flush()(這裡指的是linux核心函式)的時候,強制要求把buffer中的資料寫回磁碟。
2、PHP也有自己的buffer機制。
預設是開啟的,大小預設4096(4kb),在php.ini配置檔案中由output_buffering配置。當執行php執行echo,print的時候,是先將資料寫入php的buffer,當一個php buffer寫滿的時候,指令碼程式會將php 的buffer資料傳送給系統核心交由tcp傳給瀏覽器顯示。
3、瀏覽器buffer。
目前瀏覽器普遍為8000Bytes(可能使用者可以設定,未親測),測試 Chrome與360極速模式為8000Bytes,只有輸出資料達到了這個長度或者指令碼結束瀏覽器才會將資料輸出在頁面上。
4、資料流程。
echo/pring -> php buffer -> tcp buffer (伺服器系統buffer)-> 瀏覽器 buffer ->瀏覽器展示

二、ob_flush()與flush()及周邊函式

ob_flush():把資料從PHP的緩衝(buffer)中釋放出來。
flush():把不在緩衝(buffer)中的或者說是被釋放出來的資料傳送到瀏覽器。

程式碼示例1:程式碼效果,每隔一秒輸出一個數字。

for($j = 1; $j <= 5; $j++) { 
	echo str_repeat($i,1).'<br>'; //str_repeat()是將一個字串重複n次
	ob_flush(); //將資料從php的buffer中釋放出來
	flush(); //將釋放出來的資料傳送給瀏覽器
	sleep(1); //一秒鐘後繼續執行 
}

遇到問題:按理解,在最新的谷歌瀏覽器以及360瀏覽器效果都是每隔一秒輸出數字是正常的,但是在一個非常老的谷歌版本中(版本 28.0.1500.72)測試是四秒之後直接全部輸出,當str_repeat()函式的第二個引數設定成1020及以上數字的時候,又是一秒展示一個數字。在360相容模式下無法輸出。

程式碼示例2:關閉php.ini檔案的output_buffering配置,重啟apache。

for($i=1; $i<5; $i++){  
    echo str_repeat($i,1).'<br>';
	//ob_flush();//若開啟這行程式碼,會報failed to flush buffer。因為buffer已關閉
    flush();  
    sleep(1);  
}

遇到問題:按理解,flush()函式是將echo出來的資料傳送給瀏覽器,如果瀏覽器的buffer沒有滿,則會等指令碼執行完成再展示,但是在最新版本的谷歌以及360瀏覽器中都是隔一秒再輸出的。按這個結果理解,似乎flush函式直接讓瀏覽器的buffer釋放出來,然後展示。但是在谷歌老版本(版本 28.0.1500.72)中,卻是在4秒之後一次性展示的,當str_repeat()函式的第二個引數設定成1020及以上數字的時候,又是一秒展示一個數字。實在無法理解。在360相容模式下無法輸出。

程式碼示例3:不關閉output_buffering配置,並設定成4096,重啟apache。

for($i=1; $i<5; $i++){  
    echo str_repeat($i,4096).'<br>';
	//ob_flush();
    //flush();  
    sleep(1);  
}  

說明:當str_repeat()的第二個引數為3997到7996時,會一秒輸出兩個數字,說明設定有效,並且瀏覽器的buffer是8000(個人理解<br>佔了四個位元組)位元組。但是當小於3997時,$i<10時,是四個數字輸出一次,這個位置較難理解,實際資料傳輸是這樣的:
第一次echo的資料在php的buffer ->
第二次echo的資料與第一次資料相加,大小大於4096,輸出給瀏覽器,但相加小於8000,瀏覽器不輸出 -> 第三次echo資料在php的buffer ->
第四次echo資料傳送給瀏覽器,瀏覽器的buffer大於8000,展示出來。

程式碼示例4:關閉output_buffering配置,重啟apache。

for($i=1; $i<5; $i++){  
    echo str_repeat($i,7996).'<br>';
	//ob_flush();
    //flush();  
    sleep(1);  
}  

說明:該程式碼只是為了測試瀏覽器的buffer大小,最新谷歌瀏覽器以及360瀏覽器設定成7996及以上數字時,會一秒輸出一個數字。

程式碼總結:在最新的谷歌瀏覽器以及360瀏覽器中,對flush函式的處理似乎是將瀏覽器的buffer也丟擲直接展示。

周邊函式

ob_start() : 啟用php output_buffering機制,在不寫引數情況下,ob_start()將php buffer空間設定
到了足夠大 ,只有指令碼執行結束,或者呼叫ob_end_flush函式,才會把資料傳送給客戶端瀏覽器。ob_start()函式內的引數可以設定output_buffering大小及輸出機制。具體可以檢視手冊。
ob_end_flush與ob_end_clean:都會關閉ouput_buffering機制。不同的是,ob_end_flush只是把php buffer中的資料傳送到瀏覽器,而ob_clean_clean將php bufeer中的資料清空,但不傳送給客戶端瀏覽器。
還有更多周邊函式,不在這裡一一贅述了。

相關文章