注意修改php.ini之後需要重啟apache服務行!
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 = offimplicit_flush=off for($i=0;$i<10;$i++) { echo $i.'<br />'; flush(); sleep(1);}
寫法2:
output_buffering = onimplicit_flush=off for($i=0;$i<10;$i++) { echo $i.'<br />'; ob_flush(); flush(); sleep(1);}
寫法3:
output_buffering = offimplicit_flush=off ob_start();for($i=0;$i<10;$i++) { echo $i.'<br />'; ob_flush(); flush(); sleep(1);}
寫法4:
output_buffering = onimplicit_flush=off ob_end_flush();for($i=0;$i<10;$i++) { echo $i.'<br />'; flush(); sleep(1);}
寫法5:
output_buffering = onimplicit_flush=off ob_end_clean();for($i=0;$i<10;$i++) { echo $i.'<br />'; flush(); sleep(1);}
寫法6:
output_buffering = on;implicit_flush=on ob_end_clean();// 或者ob_end_flush();for($i=0;$i<10;$i++) { echo $i.'<br />'; sleep(1);}
寫法7:
output_buffering = on;implicit_flush=on ob_end_clean();// 或者ob_end_flush();for($i=0;$i<10;$i++) { echo $i.'<br />'; flush(); sleep(1);}
寫法8:
output_buffering = offimplicit_flush=on for($i=0;$i<10;$i++) { echo $i.'<br />'; sleep(1);}