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