關(guān)于PHP流不得不說的那些事相信不少PHP開發(fā)者或多或少都見過類似于 "php://input" 或者 "php://output" 這樣的內(nèi)容,很多人都知道這兩個的作用一個是接收的 POST 請求中的原始 body 內(nèi)容,另一個其實和 echo 之類的輸出一樣是進(jìn)行輸出的。當(dāng)然,我們的文章內(nèi)容不會如此的簡單,其實類似這樣的 php:// 開頭的協(xié)議還有好幾種,它們共同稱為 PHPIO流協(xié)議(PHP輸入/輸出流協(xié)議) 。 這種協(xié)議有什么用呢?我們知道計算機中正常的協(xié)議有 http:// ,這是我們做web開發(fā)最熟悉的。還有 file:// 表示文件,ftp:// 表示ftp協(xié)議,當(dāng)然,還有一些不太常用的 zlib:// 、 data:// 、 rar:// ,等等,這些協(xié)議PHP都是支持的,而且這些協(xié)議都是約定俗成的并且有相應(yīng)的文件或流類型支持的協(xié)議。通過這些協(xié)議我們的程序可以讀取、解析這些協(xié)議所對應(yīng)的相關(guān)內(nèi)容。比如說http協(xié)議,服務(wù)器、客戶端瀏覽器都是因為支持了相同的http協(xié)議規(guī)范,所以才能夠通過這個協(xié)議來進(jìn)行傳輸,而傳輸?shù)膬?nèi)容是什么呢?正是我們看到的網(wǎng)頁或接口文本。而今天我們的主角 php:// 協(xié)議,其實也有另一個別名是 PHP偽協(xié)議 。偽協(xié)議的原因其實就是這種協(xié)議只是PHP自身所支持的并定義的一種協(xié)議,而且也僅僅只是 IO 相關(guān)操作的一種協(xié)議規(guī)范。 好了,廢話就說到這里,我們來一個一個的看看 php:// 相關(guān)的內(nèi)容都有哪些。 stdin 輸入流while ($line = fopen('php://stdin', 'r')) {上述代碼有什么用呢?相信做過 C 或者 Java 開發(fā)的人會更有感覺,stdin 是獲取PHP進(jìn)程腳本的輸入,也就是我們在使用命令行 php xxx.php 運行PHP腳本文件時,獲取命令行輸入內(nèi)容的。上述代碼就是使用 while 循環(huán)一直監(jiān)聽命令行的輸入,當(dāng)你輸入內(nèi)容后進(jìn)行打印,如果輸入的是 exit 就退出循環(huán)也就是結(jié)束腳本的運行。 這里除了正常的用 fopen() 獲取 php://stdin 句柄外,還使用了另一種方式,也就是第二個循環(huán)所展示的 STDIN 常量來方便快捷地直接獲取輸入內(nèi)容。這也是PHP所推薦的方式。同時,下面講的 php://stdout 和 php://stderr ,也有相應(yīng)的 STDOUT 和 STDERR 常量。 stdout 、 stderr 和 output 輸出流$stdout = fopen('php://stdout', 'w');這三種都是輸出流,其實就和 echo 、 print 一樣,就是將內(nèi)容打印輸出的。不過不同的地方在于,stdin 和 stdout 是針對PHP命令行的輸出。也就是說,如果我們是通過瀏覽器查看這個腳本的話,這兩個輸出的內(nèi)容是不會打印到瀏覽器上的。小伙伴們可以試試用 php -S localhost:8081 <測試文件> 來測試下上述代碼,訪問 http://localhost:8081 的話,瀏覽器上會輸出 output 打印的內(nèi)容,而命令行這邊則會打印 stdin 和 stdout 所輸出的內(nèi)容。 另外需要注意的,這三個輸出流都是只寫的,而 stdin 是只讀的。也就是說 file_get_contents() 對這三個輸出流是沒什么用的,而 file_put_contents() 對 stdin 流也是沒效果的。 input 訪問請求的原始數(shù)據(jù)的只讀流這個相信做過接口開發(fā)的大多數(shù)人都會接觸過。當(dāng)前端或客戶端使用 body raw 方式發(fā)送數(shù)據(jù)時,就使用這個協(xié)議來接收POST中的原始 body 內(nèi)容。 echo file_get_contents("php://input");非常簡單,這里我們直接使用 postman 來模擬這種請求,可以看我們是能夠正常接收到 body raw 里面的內(nèi)容的。見下圖: memory 、 temp 內(nèi)存及臨時文件流$mem = fopen('php://memory', 'r+');這兩個流協(xié)議是輸入、輸出都支持的,它們都是在內(nèi)存中讀寫數(shù)據(jù)。不同的是, php://temp 會在數(shù)據(jù)超過一定容量時將數(shù)據(jù)寫到臨時文件中。這里我們就不演示 temp 的操作了,它和 memory 的操作代碼是非常像的。另外需要注意的,它們兩個操作都是一次性的,也就是說,如果我們在寫入(fwrite)后直接關(guān)閉(fclose)了句柄,那么后面再讀取的話(fgets),是無法獲取到內(nèi)容的。 filter 用于數(shù)據(jù)打開時的篩選過濾readfile("php://filter/read=string.toupper/resource=http://www.baidu.com");這個自己試試就知道它的好處了,第一行我們是獲取百度頁面的內(nèi)容,并把內(nèi)容中所有的字母替換成大寫字母了。第二個過濾器則是直接將百度首頁的內(nèi)容轉(zhuǎn)成base64編碼的內(nèi)容了,是不是非常強大,我覺得這個功能可以是我們好好開發(fā)的一個能力。 總結(jié)其實說實話,筆者本人平常也就是用過 php://input 這一個協(xié)議而已,偶爾或者說基本一年難得用上幾次 stdin 來進(jìn)行腳本調(diào)試,但是,這并不妨礙我們了解學(xué)習(xí)這些流協(xié)議的使用。最主要的是,通過學(xué)習(xí)后我們更進(jìn)一步的了解了它們的作用及適用的場景,這樣就可以在將來需要的時候靈活使用。 測試代碼: https://github.com/zhangyue0503/dev-blog/blob/master/php/202003/source/%E5%85%B3%E4%BA%8EPHP%E6%B5%81%E4%B8%8D%E5%BE%97%E4%B8%8D%E8%AF%B4%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B.php 參考文檔: https://www./manual/zh/wrappers.php.php https://www./manual/zh/filters.php |
|
|