ob_flush/flush在手冊中的描述, 都是刷新輸出緩沖區, 并且還需要配套使用, 所以會導致很多人迷惑…
其實, 他們倆的操作對象不同, 有些情況下, flush根本不做什么事情..
ob_*系列函數, 是操作PHP本身的輸出緩沖區.
所以, ob_flush是刷新PHP自身的緩沖區.
而flush, 嚴格來講, 這個只有在PHP做為apache的Module(handler或者filter)安裝的時候, 才有實際作用. 它是刷新WebServer(可以認為特指apache)的緩沖區.
在apache module的sapi下, flush會通過調用sapi_module的flush成員函數指針, 間接的調用apache的api: ap_rflush刷新apache的輸出緩沖區, 當然手冊中也說了, 有一些apache的其他模塊, 可能會改變這個動作的結果..
有些Apache的模塊,比如mod_gzip,可能自己進行輸出緩存, 這將導致flush()函數產生的結果不會立即被發送到客戶端瀏覽器。 甚至瀏覽器也會在顯示之前,緩存接收到的內容。例如 Netscape 瀏覽器會在接受到換行或 html 標記的開頭之前緩存內容,并且在 接受到 </table> 標記之前,不會顯示出整個表格。
一些版本的 Microsoft Internet Explorer 只有當接受到的256個 字節以后才開始顯示該頁面,所以必須發送一些額外的空格來讓這 些瀏覽器顯示頁面內容。 所以, 正確使用倆者的順序是. 先ob_flush, 然后flush, 當然, 在其他sapi下, 不調用flush也可以, 只不過為了保證你代碼的可移植性, 建議配套使用.
flush和ob_flush的使用上有一些特別注意的地方,造成無法刷新輸出緩沖。
一. flush和ob_flush的正確順序,先ob_flush再flush,如下:
ob_flush();
flush();
如果Web服務器的操作系統是windows系統,那順序顛倒或者不使用ob_flush()也不會出現問題。但是在Linux系統上就無法刷新輸出緩沖。
二. 使用ob_flush()前,確保前面的內容大小足夠4069字符。
一些Web服務器的output_buffering默認是4069字符或者更大,即輸出內容必須達到4069字符服務器才會flush刷新輸出緩沖,為了確保flush有效,最好在ob_flush()函數前有以下語句:
print str_repeat(" ", 4096);
以確保到達output_buffering值。
下面這段代碼應該很常見
<?phpfor ($i=1; $i<20; $i++){echo "<font size='10' color='red'>".$i."</font>";echo '<br>';ob_flush();flush();sleep(1);}ob_end_flush();?>
?
<?php for($i=0;$i<10;$i++){echo $i.'<br />';flush();sleep(1); }
有了解過PHP緩存輸出控制函數的朋友肯定對上面這段代碼很熟悉,它想實現的效果是每個1秒輸出1個數字,完成全部輸出需要10秒,不過實際執行中你會發現奇怪的現象,有些人或者有些時候它的表現如你所愿,而有些人或者有些時候卻是10秒后才會一次性輸出10個數字。我曾經為此抓狂不已,有朋友留言說這個情況往往是因為IE的緩存必須達到256個字符才會輸出,可實際上我之前也考慮到IE的情況,可依舊會有時靈時不靈的情況。今天仔細讀過手冊才明白,這些不可預料的現象是有它的理由的。
原來php.ini中有兩個關鍵參數會影響到php的緩存輸出控制:
參數1:output_buffering?:on/off?或者整數?。設置為on時,將在所有腳本中使用輸出緩存控制,不限制緩存的大小。而設置為整數時,如output_buffering=4096,當緩存數據達到4096字節時會自動輸出刷新緩存。而這個參數的不同正是導致以上代碼在不同時候執行結果不同的原因。當output_buffering關閉時,腳本所有的輸出(echo)都會即時發送到客戶端,執行上面代碼時就是每秒輸出一個數字。而開啟output_buffering后,輸出內容就會先緩存在服務端,直到腳本結束時才一起發送給客戶端。
參數2:implicit_flush:on/off。設定ON意味著,當腳本有輸出時,自動立即發送到客戶端。相當于在echo后自動加flush()。
php緩存輸出控制的相關函數:
ob_start()
第一個參數:回調函數,可選。在緩存輸出前可以對其進行過濾或其他處理。最常見的用法是ob_start(‘ob_gzhandler’),即對緩存的數據進行gzip壓縮后再發送給客戶端。 第二個參數:緩存塊的大小,可選。如果被緩存的內容達到或操作緩存塊的大小,緩存會自動輸出。默認值是0,指不限定大小,緩存到結束為止。還有個特殊值1,代表chunk_size=4096。 第三個參數:是否擦除緩存,可選,默認是true,如果設置為false,則在腳本執行結束前,緩存都不會被清除。
可以使用ob_get_contents()以字符串形式獲取服務端緩存的數據,使用ob_end_flush()則會輸出被緩存起來的數據,并關閉緩存。 而使用ob_end_clean()則會靜默的清除服務端緩存的數據,而不會有任何數據或其他行為。 服務端的緩存是堆疊起來的,也就是說你在開啟了ob_start()后,關閉之前,在其內部還可以開啟另外一個緩存ob_start()。不過你也要務必保證關閉緩存的操作和開啟緩存的操作數量一樣多。 ob_start()可以指定一個回調函數來處理緩存數據,如果一個ob_start()內部嵌套了另一個ob_start(),我們假定,外層的ob_start(),編號是A,內層的ob_start()編號是B,它們各自制定了一個回調函數分別是functionA和functionB,那么在緩存B中的數據輸出時,它會先輩funcitonB回調函數處理,再交給外層的functionA回調函數處理,之后才能輸出到客戶端。
另外,手冊說,對于某些web服務器,比如apache,在使用回調函數有可能會改變程序當前的工作目錄,解決方法是在回調函數中自行手動把工作目錄修改回來,用chdir函數,這點似乎不常遇到,遇到的時候記得去查手冊吧。
flush()和ob_flush()
這兩個函數的使用怕是很多人最迷惑的一個問題,手冊上對兩個函數的解釋也語焉不詳,沒有明確的指出它們的區別,似乎二者的功能都是刷新輸出緩存。但在我們文章一開始的代碼中如果講fush()替換成ob_flush(),程序就再不能正確執行了。顯然,它們是有區別的,否則也手冊中直接說明其中一個是另外一個函數的別名即可了,沒必要分別說明。那么它們的區別到底是什么呢?
反復研究了手冊的說明,參考了手冊中一些人的留言,自己琢磨應該是這樣的: 在沒有開啟緩存時,腳本輸出的內容都在服務器端處于等待輸出的狀態,flush()可以將等待輸出的內容立即發送到客戶端。 開啟緩存后,腳本輸出的內容存入了輸出緩存中,這時沒有處于等待輸出狀態的內容,你直接使用flush()不會向客戶端發出任何內容。而ob_flush()的作用就是將本來存在輸出緩存中的內容取出來,設置為等待輸出狀態,但不會直接發送到客戶端,這時你就需要先使用ob_flush()再使用flush(),客戶端才能立即獲得腳本的輸出。
也就是說本文開頭的腳本,可以根據緩存開啟與否,有如下幾種不同的寫法:
注:以下代碼都未考慮IE緩存必須大于256字節才輸出的問題,如在IE下測試,請在代碼開始加一句:“echo str_repeat('',256);”
寫法1:
output_buffering = off
implicit_flush=off
<?php for($i=0;$i<10;$i++){echo $i.'<br />';flush();sleep(1); }
寫法2:
output_buffering = on
implicit_flush=off
<?php for($i=0;$i<10;$i++){echo $i.'<br />';ob_flush();flush();sleep(1); }
寫法3:
output_buffering = off
implicit_flush=off
<?php ob_start();for($i=0;$i<10;$i++){echo $i.'<br />';ob_flush();flush();sleep(1); }
寫法4:
output_buffering = onimplicit_flush=off
<?php ob_end_flush();for($i=0;$i<10;$i++){echo $i.'<br />';flush();sleep(1); }
寫法5:
output_buffering = onimplicit_flush=off
<?php ob_end_clean();for($i=0;$i<10;$i++){echo $i.'<br />';flush();sleep(1); }
寫法6:
output_buffering = on;implicit_flush=on
<?php ob_end_clean();//或者ob_end_flush();for($i=0;$i<10;$i++){echo $i.'<br />';sleep(1);}
寫法7:
output_buffering = on;implicit_flush=on
<?php ob_end_clean(); //或者ob_end_flush(); for($i=0;$i<10;$i++){echo $i.'<br />';flush();sleep(1); }
寫法8:
output_buffering = offimplicit_flush=on
<?php for($i=0;$i<10;$i++){echo $i.'<br />';sleep(1);}
ob_end_flush():輸出當前服務器端緩存的輸出數據,并關閉緩存。
ob_end_clean():清空緩存內容,并關閉緩存。
ob_get_flush():將當前服務器端緩存的輸出數據以字符串形式返回,并關閉緩存
ob_get_contents():將緩存中保存的內容以字符串形式返回,并保留緩存。
ob_get_length():返回緩存中數據的長度。
ob_get_clean():獲取緩存中的數據,請清空緩存,相當于依次執行ob_get_contents()和ob_end_clean()。
ob_implicit_flush():相當于開啟php.ini中的implicit_flush參數,立即發送腳本的輸出。
ob_gzhandler():使用gzip壓縮緩存數據。用于將文本數據壓縮后再發送到客戶端,可以極大減少數據傳輸所用的時間,對于提高網站瀏覽速度幫助很大。通常作為ob_start()的回調函數來使用。
ob_list_handlers():列出所有輸出使用的操作方法。
例1:
<?php ob_start();echo '周伯通'.'<br>';var_dump(ob_list_handlers()).'<br>';ob_end_flush();
輸出:
周伯通 array(2) { [0]=> string(22) "default output handler" [1]=> string(22) "default output handler" }
例2:
<?php ob_start("ob_gzhandler"); echo '周伯通'.'<br>';var_dump(ob_list_handlers()).'<br>';ob_end_flush();
輸出:
周伯通
array(2) { [0]=> string(22) "default output handler" [1]=> string(12) "ob_gzhandler" }
本次輸出,使用了ob_gzhandler的緩存方法。
例3: ? ?
<?php ob_start(create_function('$string','return $string;'));print_r(ob_list_handlers());ob_end_flush();
將所有的數據輸出時,使用的操作操作方法返以數組形式回。如果緩存沒有打開,或者已經關閉,此函數只會返回空數組。