細說flush、ob_flush的區別

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();

將所有的數據輸出時,使用的操作操作方法返以數組形式回。如果緩存沒有打開,或者已經關閉,此函數只會返回空數組。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280011.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280011.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280011.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

關于jHipster框架在構建中的出現的error修復

jhipster The JDL object and the database type are both mandatory.這個錯誤應該是在構建基于jHipster的spring-cloud項目中經常遇到的&#xff0c;因為這個在這個過程中會讀取.yo-rc文件&#xff0c;之后生成相關的.json文件&#xff0c;再之后生成相關的.java文件&#xff…

protobuf編碼

proto2Protocol Buffers 是一種輕便高效的結構化數據存儲格式&#xff0c;可以用于結構化數據序列化&#xff0c;適合做數據存儲或 RPC 數據交換格式。可用于通訊協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。 字段規則 required: 字段必須存在opti…

定制.NET 6.0的Middleware中間件

大家好&#xff0c;我是張飛洪&#xff0c;感謝您的閱讀&#xff0c;我會不定期和你分享學習心得&#xff0c;希望我的文章能成為你成長路上的墊腳石&#xff0c;讓我們一起精進。在本文中&#xff0c;我們將學習中間件&#xff0c;以及如何使用它進一步定制應用程序。我們將快…

Python-循環控制--個人課堂筆記

Python中的兩種循環方式&#xff08;目前學到&#xff09;&#xff1a;for循環和while循環 for循環和while循環的區別&#xff1a; for循環一般用于控制循環的次數&#xff0c;while循環則是條件循環。 操作實例-猜數字小游戲&#xff08;3次猜錯提示游戲結束&#xff09;&…

刪除microsoft_如何從您的Microsoft帳戶中刪除設備

刪除microsoftWhen you sign into Windows 8 or 10 using your Microsoft account (and other Microsoft devices, like an Xbox), those devices become associated with your account. If you want to remove an old device you’ve gotten rid of, you’ll have to pay a vi…

線程的語法 (event,重要)

Python threading模塊 2種調用方式 直接調用 12345678910111213141516171819import threadingimport timedef sayhi(num): #定義每個線程要運行的函數print("running on number:%s" %num)time.sleep(3)if __name__ __main__:t1 threading.Thread(targetsayhi,args(…

求最大值和下標值

本題要求編寫程序&#xff0c;找出給定的n個數中的最大值及其對應的最小下標&#xff08;下標從0開始&#xff09;。 輸入格式: 輸入在第一行中給出一個正整數n&#xff08;1<n≤10&#xff09;。第二行輸入n個整數&#xff0c;用空格分開。 輸出格式: 在一行中輸出最大值及…

windows應用商店修復_如何修復Windows應用商店中的卡死下載

windows應用商店修復Though it’s had its share of flaky behavior since being introduced in Windows 8, the Windows Store has gotten more reliable over time. It still has the occasional problems, though. One of the more irritating issues is when an app update…

OpenWrt:Linux下生成banner

Linux下有三個小工具可以生成banner&#xff1a;1、banner使用#生成banner&#xff1b;2、figlet使用一些普通字符生成banner&#xff1b;3、toilet使用一些復雜的彩色特殊字符生成banner。使用apt-get安裝的時候需要輸入以下命令&#xff1a; $ sudo apt-get install sysvbann…

新冠病毒中招 | 第二天

今天跟大家分享我個人感染奧密克戎毒株第二天的經歷和感受。早上7點多自然醒來&#xff0c;已經沒有四肢乏力的感覺&#xff0c;但是身體的本能還是告訴我不愿意動彈。由于第一天躺著睡了一天&#xff0c;確實是躺得腰酸背疼的。起床量了一下體溫36.4正常&#xff0c;決定今天不…

輸出到Excel

HSSFWorkbook oBook new HSSFWorkbook(); NPOI.SS.UserModel.ISheet oSheet oBook.CreateSheet(); #region 輸出到Excel MemoryStream ms new MemoryStream(); oBook.Write(ms);string sExportPath ""; using (SaveFileDialog saveFileDialog1 new SaveFileDial…

JavaScript 精粹 基礎 進階(5)數組

轉載請注明出處 原文連接 blog.huanghanlian.com/article/5b6… 數組是值的有序集合。每個值叫做元素&#xff0c;每個元素在數組中都有數字位置編號&#xff0c;也就是索引。JS中的數組是弱類型的&#xff0c;數組中可以含有不同類型的元素。數組元素甚至可以是對象或其它數組…

icloud 購買存儲空間_如何釋放iCloud存儲空間

icloud 購買存儲空間Apple offers 5 GB of free iCloud space to everyone, but you’ll run up against that storage limit sooner than you’d think. Device backups, photos, documents, iCloud email, and other bits of data all share that space. Apple為每個人提供5 …

基于LAMP實現web日志管理查看

前言&#xff1a;日志是一個重要的信息庫&#xff0c;如何高效便捷的查看系統中的日志信息&#xff0c;是系統管理員管理系統的必備的技術。實現方式&#xff1a;1、將日志存儲于數據庫。2、采用LAMP架構&#xff0c;搭建PHP應用&#xff0c;通過web服務訪問數據庫&#xff0c;…

WPF效果第二百零七篇之EditableSlider

前面簡單玩耍一下快速黑白灰效果; 今天又玩了一下ZoomBlurEffect,來看看最終實現的效果:1、ps和cs文件都在Shazzam中,咱們自己隨意玩耍;今天主角是下面這位:2、來看看自定義控件布局(TextBox、Slider、ToggleButton)&#xff1a;3、點擊編輯按鈕,我就直接偷懶了:private void E…

閑話高并發的那些神話,看京東架構師如何把它拉下神壇

轉載:閑話高并發的那些神話&#xff0c;看京東架構師如何把它拉下神壇 高并發也算是這幾年的熱門詞匯了&#xff0c;尤其在互聯網圈&#xff0c;開口不聊個高并發問題&#xff0c;都不好意思出門。高并發有那么邪乎嗎&#xff1f;動不動就千萬并發、億級流量&#xff0c;聽上去…

c# Clone方法

clone是深拷貝&#xff0c;copy是淺拷貝&#xff0c;如果是值類型的話是沒什么區別的&#xff0c;如果是引用類型的話深拷貝拷貝的事整個對象的數據&#xff0c;而淺拷貝僅僅拷貝對象的引用。因為類的實例是引用類型&#xff0c;要想用原有的類中的實例的數據的話&#xff0c;既…

使用MyQ打開車庫門時如何接收警報

Chamberlain’s MyQ technology is great for opening and closing your garage door remotely with your smartphone, but you can also receive alerts whenever your garage door opens and closes (as well as receive alerts when it’s been open for an extended amount…

踏實工作,實現價值

工作&#xff0c;為實現自我價值 若想在漫長的職場生涯中穩步高升&#xff0c;首先要踏踏實實&#xff0c;專心致志、充滿激情的去完成工作中的每一項任務&#xff0c;無論工作是繁重的還是瑣碎的&#xff0c;都要嚴格要求自己全身心的去完成。而不是一味的抱怨&#xff0c;一味…

mac 防火墻禁止程序聯網_如何允許應用程序通過Mac的防火墻進行通信

mac 防火墻禁止程序聯網If you use a Mac, chances are you might not even realize that OS X comes with a firewall. This firewall helps ensure unauthorized app and services can’t contact your computer, and prevents intruders from sniffing out your Mac on a ne…