PHP的輸出緩沖區(qū)

PHP的輸出緩沖區(qū)

什么是緩沖區(qū)?
簡(jiǎn)單而言,緩沖區(qū)的作用就是,把輸入或者輸出的內(nèi)容先放進(jìn)內(nèi)存,而不顯示或者讀取.至于為什么要有緩沖區(qū),這是一個(gè)很廣泛的問(wèn)題,如果有興趣,可以在網(wǎng)上找下資料.
其實(shí)緩沖區(qū)最本質(zhì)的作用就是,協(xié)調(diào)高速CPU和相對(duì)緩慢的IO設(shè)備(磁盤(pán)等)的運(yùn)作.

PHP在執(zhí)行的時(shí)候,在什么地方有用到緩沖區(qū)?
想要了解PHP的緩沖區(qū),就要知道執(zhí)行PHP的時(shí)候,緩沖區(qū)被設(shè)置到了什么地方.
當(dāng)執(zhí)行PHP的時(shí)候,如果碰到了echo print_r之類(lèi)的會(huì)輸出數(shù)據(jù)的代碼,PHP就會(huì)將要輸出的數(shù)據(jù)放到PHP自身的緩沖區(qū),等待輸出.
當(dāng)PHP自身的緩沖區(qū)接到指令,指示要輸出緩沖區(qū)的內(nèi)容時(shí),將會(huì)把緩沖區(qū)內(nèi)的數(shù)據(jù)輸出到apache上, apache接受到PHP輸出的數(shù)據(jù),然后再把該數(shù)據(jù)存在到apache自身的緩沖區(qū)內(nèi),等到輸出
當(dāng)apache接受到指令,只是要輸出緩沖區(qū)的內(nèi)容時(shí), 將會(huì)把緩沖區(qū)的內(nèi)容輸出,返回到瀏覽器.

由此可見(jiàn),PHP要輸出數(shù)據(jù)的時(shí)候,將會(huì)經(jīng)過(guò)兩個(gè)緩沖區(qū)(先是自身的,然后是apache的),再返回到瀏覽器.

緩沖區(qū)在PHP中起到什么作用?
1.最常見(jiàn)的就是在使用header函數(shù)之前,就已經(jīng)輸出了某些數(shù)據(jù),這樣會(huì)導(dǎo)致某些錯(cuò)誤,例如 Cannot modify header information – headers already sent by;
echo "this is test";
header("LOCATION http://www.baidu.com");

出現(xiàn)這個(gè)錯(cuò)誤的原因是, 在header之前已經(jīng)輸出了某些數(shù)據(jù),而輸出這些數(shù)據(jù)的同時(shí), apache將會(huì)同時(shí)發(fā)送一個(gè)響應(yīng)狀態(tài)到瀏覽器上(既然有輸出,即這個(gè)請(qǐng)求是有效的),而其后你又再次使用header函數(shù)
發(fā)送http頭,則會(huì)返回這個(gè)錯(cuò)誤,錯(cuò)誤的意思是:HTTP頭已經(jīng)發(fā)送出去了,你不能對(duì)他再做修改.
為什么使用緩沖區(qū)可以避免這個(gè)錯(cuò)誤呢?
因?yàn)閔eader函數(shù)是不受緩沖區(qū)影響的,當(dāng)一碰到header函數(shù)的時(shí)候,PHP馬上執(zhí)行apache發(fā)送這一個(gè)http頭都瀏覽器.
而輸出的數(shù)據(jù)PHP打開(kāi)輸出緩沖區(qū)后, 這些數(shù)據(jù)將會(huì)存放在緩沖區(qū),等待輸出.這樣就可以避免了之前所發(fā)生的錯(cuò)誤.
2.通過(guò)PHP寫(xiě)文件下載程序的時(shí)候.
為了讓文件下載更安全,同時(shí)提高更多的可控性,很多朋友都喜歡用PHP寫(xiě)文件下載頁(yè)面.其原理很簡(jiǎn)單,就是通過(guò)fwrite把文件內(nèi)容讀出并顯示,然后通過(guò)header來(lái)發(fā)送HTTP頭,讓瀏覽器知道這是一個(gè)附件,這樣
就可以達(dá)到提供下載的效果.
如果用上面的辦法提供下載頁(yè)面,會(huì)碰到一個(gè)效率問(wèn)題,如果一個(gè)文件很大,假設(shè)為100M,那么在不開(kāi)啟緩沖區(qū)輸出的情況下,必須要把100M數(shù)據(jù)全部讀出,然后一次返回到頁(yè)面上,如果這樣做,用戶將會(huì)在所有數(shù)據(jù)讀完
之后才會(huì)得到響應(yīng),降低了用戶體驗(yàn)感.
如果開(kāi)啟了輸出緩沖區(qū),當(dāng)PHP程序讀完文件的某一段,然后馬上輸出到apache,然后讓apache馬上返回到瀏覽器,這樣就可以減少用戶等待時(shí)間.那后面的數(shù)據(jù)怎么辦呢?我們可以寫(xiě)一個(gè)while循環(huán),一直一段一段地讀取文件
每讀一段,就馬上輸出,直到把文件全部輸出為止,這樣瀏覽器就可以持續(xù)地接受到數(shù)據(jù),而不必等到所有文件讀取完畢.

另外,該做法還解決了另外一個(gè)很?chē)?yán)重的問(wèn)題.例如一個(gè)文件是100M,如果不開(kāi)啟緩沖區(qū)的情況下,則需要把100M文件全部讀入內(nèi)存,然后再輸出.但是,如果PHP程序做了內(nèi)存限制呢?為了保證服務(wù)器的穩(wěn)定,管理員通常會(huì)把PHP的執(zhí)行
內(nèi)存設(shè)一個(gè)限制(通過(guò)php.ini總的memory_limit, 其默認(rèn)值是8M), 也就是每個(gè)PHP程序使用的內(nèi)存不能使用超過(guò)這個(gè)值的內(nèi)存. 假設(shè)該值為8M,而要讀入的文件是100M,根本就沒(méi)有足夠的內(nèi)存來(lái)讀入該文件.這個(gè)時(shí)候,我們就需要用到上面的
辦法來(lái)解決這個(gè)問(wèn)題,每次只讀某一段,這樣就可以避免了內(nèi)存的限制
3.靜態(tài)文件緩存
現(xiàn)在很多公司有這么一個(gè)需求, 就是某一個(gè)頁(yè)面在第一次訪問(wèn)的時(shí)候,會(huì)執(zhí)行PHP,然后把顯示的內(nèi)容返回到瀏覽器,同時(shí)需要把這次顯示的內(nèi)容保存到服務(wù)器上,這樣下次訪問(wèn)的時(shí)候,就直接把保存在服務(wù)器上的文件直接顯示,而不需要通過(guò)PHP來(lái)做操作
這就是所謂的”靜態(tài)頁(yè)面緩存”.那怎么樣才能做到把內(nèi)容返回到瀏覽器的同時(shí)把數(shù)據(jù)保存到服務(wù)器上呢?這就要用到輸出緩沖區(qū)了.

ob_start();
echo 'aaa';
$string = ob_get_contents();
file_put_contents('a.html', $string);
ob_flush();
flush();

與輸出緩沖區(qū)有關(guān)的配置
在PHP.INI中,有兩個(gè)跟緩沖區(qū)緊密相關(guān)的配置項(xiàng)
1.output_buffering
該配置直接影響的是php本身的緩沖區(qū),有3種配置參數(shù).on/off/xK(x為某個(gè)整型數(shù)值);
on - 開(kāi)啟緩沖區(qū)
off - 關(guān)閉緩沖區(qū)
256k - 開(kāi)啟緩沖區(qū),而且當(dāng)緩沖區(qū)的內(nèi)容超過(guò)256k的時(shí)候,自動(dòng)刷新緩沖區(qū)(把數(shù)據(jù)發(fā)送到apache);

2.implicit_flush
該配置直接影響apache的緩沖區(qū),有2種配置參數(shù). on/off
on - 自動(dòng)刷新apache緩沖區(qū),也就是,當(dāng)php發(fā)送數(shù)據(jù)到apache的緩沖區(qū)的時(shí)候,不需要等待其他指令,直接就把輸出返回到瀏覽器
off - 不自動(dòng)刷新apache緩沖區(qū),接受到數(shù)據(jù)后,等待刷新指令

與緩沖區(qū)有關(guān)的函數(shù)
1.ob_implicit_flush
作用和implicit_flush一樣,是否自動(dòng)刷新apache的緩沖區(qū)
2.flush
作用是發(fā)送指令到apache,讓apache刷新自身的輸出緩沖區(qū).
3.ob_start
打開(kāi)輸出緩沖區(qū),無(wú)論php.ini的文件如何配置,如果使用該函數(shù),即使output_buffering設(shè)置成off,也會(huì)打開(kāi)輸出緩沖區(qū)
ob_start函數(shù)還接受一個(gè)參數(shù),該參數(shù)是一個(gè)函數(shù)的回調(diào),意思是,在輸入緩沖區(qū)內(nèi)容之前,需要使用調(diào)用傳遞進(jìn)來(lái)的參數(shù)把緩沖區(qū)的內(nèi)容處理一次,再放入緩沖區(qū)內(nèi)
4.ob_flush
指示php本身刷新自身的緩沖區(qū),把數(shù)據(jù)發(fā)送到apache
5.ob_clean
清除php緩沖區(qū)里面的內(nèi)容
6.ob_end_clean
清除php緩沖區(qū)內(nèi)的內(nèi)容,并且關(guān)閉輸出緩沖區(qū)
7.ob_end_flush
把php自身的緩沖區(qū)里的內(nèi)容發(fā)送到apache,并把清除自身緩沖區(qū)內(nèi)的內(nèi)容
8.ob_get_clean
獲取緩沖區(qū)的內(nèi)容之后,清除緩沖區(qū).
9.ob_get_contents
獲取輸出緩沖區(qū)里的內(nèi)容
10.ob_get_flush
獲取緩沖區(qū)里的內(nèi)容,并且把這些內(nèi)容發(fā)送到apache
11.ob_get_length
獲取緩沖區(qū)里內(nèi)容的長(zhǎng)度
12.ob_list_handlers
獲取運(yùn)行ob_start時(shí),所回調(diào)的函數(shù)名稱, 例如:
ob_start(‘ob_gzhandler’);
print_r(ob_list_handlers);
將打印出ob_gzhandler;
13.ob_gzhandler
該函數(shù)的作用是作為ob_start的回調(diào)參數(shù), 在緩沖區(qū)刷新之前,會(huì)調(diào)用該函數(shù)對(duì)數(shù)據(jù)進(jìn)行到底gzip或者deflate壓縮.這個(gè)函數(shù)需要zlib擴(kuò)展的支持.

使用緩沖區(qū)的相關(guān)內(nèi)容
1.ob_flush和flush的次序關(guān)系.上面的分析可以看出,ob_flush是和php自身相關(guān)的,而flush操作的是apache的緩沖區(qū),所有我們?cè)谑褂眠@兩個(gè)函數(shù)的時(shí)候,需要先執(zhí)行ob_flush,
再執(zhí)行flush,因?yàn)槲覀冃枰劝褦?shù)據(jù)從PHP上發(fā)送到apache,然后再由apache返回到瀏覽器.如果php還沒(méi)有把數(shù)據(jù)刷新到apache,就調(diào)用了flush,則apache無(wú)任何數(shù)據(jù)返回到瀏覽器.

2.有的瀏覽器,如果接受到的字符太少,則不會(huì)把數(shù)據(jù)顯示出來(lái),例如老版的IE(必須要大于256k才顯示).這樣就會(huì)造成一個(gè)疑問(wèn), 明明在php和apache都進(jìn)行了刷新緩沖區(qū)的操作,但是瀏覽器就是沒(méi)有出現(xiàn)自己想要的數(shù)據(jù),也許就是這個(gè)原因造成的.所以才測(cè)試的時(shí)候,可以在輸出數(shù)據(jù)的后面加上多個(gè)空格,以填滿數(shù)據(jù),確定不會(huì)瀏覽器造成這類(lèi)詭異的問(wèn)題.

3.有些webserver,他自身的輸出緩沖區(qū)會(huì)有一些限制,比如nginx,他有一個(gè)配置fastcgi_buffer_size 4k, 就是是表明,當(dāng)自身的輸出緩沖區(qū)的內(nèi)容達(dá)到4K才會(huì)刷新,所以為了保證內(nèi)容的數(shù)據(jù),可以添加以下代碼,保證內(nèi)容長(zhǎng)度

<?php

echo str_repeat(" ",4096);

?>

4.在apache中,如果你開(kāi)啟了mod_gzip的壓縮模塊,這樣可能會(huì)導(dǎo)致你的flush函數(shù)刷新不成功,其原因是,mod_gzip有自己的輸出緩沖區(qū),當(dāng)php執(zhí)行了flush函數(shù),指示apache刷新輸出緩沖區(qū),但是內(nèi)容需要壓縮,apache就把內(nèi)容輸出到自身的mod_gzip模塊,mod_gzip也有自身的輸出 緩沖區(qū),他也不會(huì)馬上輸出,所以造成了內(nèi)容不能馬上輸出.為了改善這個(gè)情況,可以關(guān)閉mod_gzip模塊,或者在httpd.conf增加以下內(nèi)容,以禁止壓縮

SetEnv no-gzip dont-vary

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末琳要,一起剝皮案震驚了整個(gè)濱河市糊治,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吉执,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笤虫,死亡現(xiàn)場(chǎng)離奇詭異韩脑,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)眼坏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)酸些,“玉大人宰译,你說(shuō)我怎么就攤上這事∑嵌” “怎么了沿侈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)市栗。 經(jīng)常有香客問(wèn)我缀拭,道長(zhǎng),這世上最難降的妖魔是什么填帽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任蛛淋,我火速辦了婚禮,結(jié)果婚禮上篡腌,老公的妹妹穿的比我還像新娘铣鹏。我一直安慰自己,他們只是感情好哀蘑,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著葵第,像睡著了一般绘迁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卒密,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天缀台,我揣著相機(jī)與錄音,去河邊找鬼哮奇。 笑死膛腐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鼎俘。 我是一名探鬼主播哲身,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贸伐!你這毒婦竟也來(lái)了勘天?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脯丝,沒(méi)想到半個(gè)月后商膊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宠进,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年晕拆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片材蹬。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡实幕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赚导,到底是詐尸還是另有隱情茬缩,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布吼旧,位于F島的核電站凰锡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏圈暗。R本人自食惡果不足惜掂为,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望员串。 院中可真熱鬧勇哗,春花似錦、人聲如沸寸齐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)渺鹦。三九已至扰法,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間毅厚,已是汗流浹背塞颁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吸耿,地道東北人祠锣。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像咽安,于是被迫代替她去往敵國(guó)和親伴网。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容