什麼是緩衝區?
簡單而言,緩衝區的做用就是,把輸入或者輸出的內容先放進內存,而不顯示或者讀取.至於爲何要有緩衝區,這是一個很普遍的問題,若是有興趣,能夠在網山找下資料.
其實緩衝區最本質的做用就是,協調高速CPU和相對緩慢的IO設備(磁盤等)的運做.php
PHP在執行的時候,在什麼地方有用到緩衝區?
想要了解PHP的緩衝區,就要知道執行PHP的時候,緩衝區被設置到了什麼地方.
當執行PHP的時候,若是碰到了echo print_r之類的會輸出數據的代碼,PHP就會將要輸出的數據放到PHP自身的緩衝區,等待輸出.
當PHP自身的緩衝區接到指令,指示要輸出緩衝區的內容時,將會把緩衝區內的數據輸出到apache上, apache接受到PHP輸出的數據,而後再把該數據存在到apache自身的緩衝區內,等到輸出
當apache接受到指令,只是要輸出緩衝區的內容時, 將會把緩衝區的內容輸出,返回到瀏覽器.html
因而可知,PHP要輸出數據的時候,將會通過兩個緩衝區(先是自身的,而後是apache的),再返回到瀏覽器.nginx
緩衝區在PHP中起到什麼做用?
1.最多見的就是在使用header函數以前,就已經輸出了某些數據,這樣會致使某些錯誤,例如 Cannot modify header information – headers already sent by;web
echo "this is test"; header("LOCATION http://www.baidu.com");
出現這個錯誤的緣由是, 在header以前已經輸出了某些數據,而輸出這些數據的同時, apache將會同時發送一個響應狀態到瀏覽器上(既然有輸出,即這個請求是有效的),而其後你又再次使用header函數
發送http頭,則會返回這個錯誤,錯誤的意思是:HTTP頭已經發送出去了,你不能對他再作修改.
爲何使用緩衝區能夠避免這個錯誤呢?
由於header函數是不受緩衝區影響的,當一碰到header函數的時候,PHP立刻執行apache發送這一個http頭都瀏覽器.
而輸出的數據PHP打開輸出緩衝區後, 這些數據將會存放在緩衝區,等待輸出.這樣就能夠避免了以前所發生的錯誤.
2.經過PHP寫文件下載程序的時候.
爲了讓文件下載更安全,同時提升更多的可控性,不少朋友都喜歡用PHP寫文件下載頁面.其原理很簡單,就是經過fwrite把文件內容讀出並顯示,而後經過header來發送HTTP頭,讓瀏覽器知道這是一個附件,這樣
就能夠達到提供下載的效果.
若是用上面的辦法提供下載頁面,會碰到一個效率問題,若是一個文件很大,假設爲100M,那麼在不開啓緩衝區輸出的狀況下,必需要把100M數據所有讀出,而後一次返回到頁面上,若是這樣作,用戶將會在全部數據讀完
以後纔會獲得響應,下降了用戶體驗感.
若是開啓了輸出緩衝區,當PHP程序讀完文件的某一段,而後立刻輸出到apache,而後讓apache立刻返回到瀏覽器,這樣就能夠減小用戶等待時間.那後面的數據怎麼辦呢?咱們能夠寫一個while循環,一直一段一段地讀取文件
每讀一段,就立刻輸出,直到把文件所有輸出爲止,這樣瀏覽器就能夠持續地接受到數據,而沒必要等到全部文件讀取完畢.apache
另外,該作法還解決了另一個很嚴重的問題.例如一個文件是100M,若是不開啓緩衝區的狀況下,則須要把100M文件所有讀入內存,而後再輸出.可是,若是PHP程序作了內存限制呢?爲了保證服務器的穩定,管理員一般會把PHP的執行
內存設一個限制(經過php.ini總的memory_limit, 其默認值是8M), 也就是每一個PHP程序使用的內存不能使用超過這個值的內存. 假設該值爲8M,而要讀入的文件是100M,根本就沒有足夠的內存來讀入該文件.這個時候,咱們就須要用到上面的
辦法來解決這個問題,每次只讀某一段,這樣就能夠避免了內存的限制
3.靜態文件緩存
如今不少公司有這麼一個需求, 就是某一個頁面在第一次訪問的時候,會執行PHP,而後把顯示的內容返回到瀏覽器,同時須要把此次顯示的內容保存到服務器上,這樣下次訪問的時候,就直接把保存在服務器上的文件直接顯示,而不須要經過PHP來作操做
這就是所謂的」靜態頁面緩存」.那怎麼樣才能作到把內容返回到瀏覽器的同時把數據保存到服務器上呢?這就要用到輸出緩衝區了.瀏覽器
ob_start(); echo 'aaa'; $string = ob_get_contents(); file_put_contents('a.html', $string); ob_flush(); flush();
與輸出緩衝區有關的配置
在PHP.INI中,有兩個跟緩衝區緊密相關的配置項
1.output_buffering
該配置直接影響的是php自己的緩衝區,有3種配置參數.on/off/xK(x爲某個整型數值);
on - 開啓緩衝區
off - 關閉緩衝區
256k - 開啓緩衝區,並且當緩衝區的內容超過256k的時候,自動刷新緩衝區(把數據發送到apache);緩存
2.implicit_flush
該配置直接影響apache的緩衝區,有2種配置參數. on/off
on - 自動刷新apache緩衝區,也就是,當php發送數據到apache的緩衝區的時候,不須要等待其餘指令,直接就把輸出返回到瀏覽器
off - 不自動刷新apache緩衝區,接受到數據後,等待刷新指令安全
與緩衝區有關的函數
1.ob_implicit_flush
做用和implicit_flush同樣,是否自動刷新apache的緩衝區
2.flush
做用是發送指令到apache,讓apache刷新自身的輸出緩衝區.
3.ob_start
打開輸出緩衝區,不管php.ini的文件如何配置,若是使用該函數,即便output_buffering設置成off,也會打開輸出緩衝區
ob_start函數還接受一個參數,該參數是一個函數的回調,意思是,在輸入緩衝區內容以前,須要使用調用傳遞進來的參數把緩衝區的內容處理一次,再放入緩衝區內
4.ob_flush
指示php自己刷新自身的緩衝區,把數據發送到apache
5.ob_clean
清除php緩衝區裏面的內容
6.ob_end_clean
清除php緩衝區內的內容,而且關閉輸出緩衝區
7.ob_end_flush
把php自身的緩衝區裏的內容發送到apache,並把清除自身緩衝區內的內容
8.ob_get_clean
獲取緩衝區的內容以後,清除緩衝區.
9.ob_get_contents
獲取輸出緩衝區裏的內容
10.ob_get_flush
獲取緩衝區裏的內容,而且把這些內容發送到apache
11.ob_get_length
獲取緩衝區裏內容的長度
12.ob_list_handlers
獲取運行ob_start時,所回調的函數名稱, 例如:
ob_start(‘ob_gzhandler’);
print_r(ob_list_handlers);
將打印出ob_gzhandler;
13.ob_gzhandler
該函數的做用是做爲ob_start的回調參數, 在緩衝區刷新以前,會調用該函數對數據進行到底gzip或者deflate壓縮.這個函數須要zlib擴展的支持.服務器
使用緩衝區的相關內容
1.ob_flush和flush的次序關係.上面的分析能夠看出,ob_flush是和php自身相關的,而flush操做的是apache的緩衝區,全部咱們在使用這兩個函數的時候,須要先執行ob_flush,
再執行flush,由於咱們須要先把數據從PHP上發送到apache,而後再由apache返回到瀏覽器.若是php尚未把數據刷新到apache,就調用了flush,則apache無任何數據返回到瀏覽器.函數
2.有的瀏覽器,若是接受到的字符太少,則不會把數據顯示出來,例如老版的IE(必需要大於256k才顯示).這樣就會形成一個疑問, 明明在php和apache都進行了刷新緩衝區的操做,可是瀏覽器就是沒有出現本身想要的數據,也許就是這個緣由形成的.因此才測試的時候,能夠在輸出數據的後面加上多個空格,以填滿數據,肯定不會瀏覽器形成這類詭異的問題.
3.有些webserver,他自身的輸出緩衝區會有一些限制,好比nginx,他有一個配置fastcgi_buffer_size 4k, 就是是代表,當自身的輸出緩衝區的內容達到4K纔會刷新,因此爲了保證內容的數據,能夠添加如下代碼,保證內容長度
<?php echo str_repeat(" ",4096); ?>
4.在apache中,若是你開啓了mod_gzip的壓縮模塊,這樣可能會致使你的flush函數刷新不成功,其緣由是,mod_gzip有本身的輸出緩衝區,當php執行了flush函數,指示apache刷新輸出緩衝區,可是內容須要壓縮,apache就把內容輸出到自身的mod_gzip模塊,mod_gzip也有自身的輸出 緩衝區,他也不會立刻輸出,因此形成了內容不能立刻輸出.爲了改善這個狀況,能夠關閉mod_gzip模塊,或者在httpd.conf增長如下內容,以禁止壓縮
SetEnv no-gzip dont-vary