前言
本文為《現代 PHP》一書的學習筆記。
環境
- Windows 10
- Wnmp 3.1.0
串流
串流在 PHP 4.3.0 中被採用,串流用於概括檔案、網路、資料壓縮和其他的程序中共通的函式和行為。一個串流可以用串流線性的方式被讀取或寫入。
串流是從來源到目的地的資料,可以是一個檔案、命令列程序、網路連線、壓縮檔、暫存記憶體、標準輸出/輸入或任何其他在串流包裝中的資源。
串流提供許多 PHP 的 I/O 函式底層的實作,例如 file_get_contents()
、fopen()
、fgets()
、fwrite()
函式。
串流包裝
許多種不同的串流資料都需要用特殊的協定來讀寫資料,這些協定就是串流包裝。
可以在檔案系統中讀寫資料、用 HTTP
、HTTPS
或 SSH
跟遠端網頁伺服器溝通,或讀寫 ZIP、RAR 或 PHAR 檔。這些所有的通訊方法都可以使用下列相同的程序:
- 開啟通訊。
- 讀取資料。
- 寫入資料。
- 關閉通訊。
每一個串流都有一個 scheme
和 target
,藉由以下格式使用串流的辨識碼來指定:
1 | <scheme>://<target> |
<scheme>
用來辨識串流包裝,而<target>
用來識別串流資料的來源。
http:// 串流包裝
file_get_contents()
函式的字串參數實際上是個串流辨識碼,URL 其實就是 PHP 串流包裝的一種形式,
範例 5-28:使用 HTTP 串流包裝的 Flickr API
1 | $json = file_get_contents( |
file:// 串流包裝
讀取檔案系統的 PHP 串流包裝就是 file://
,由於 PHP 視其為預設設定,因此經常省略 file://
。
範例 5-29:不明顯的 file:// 串流包裝
1 | $handle = fopen('/etc/hosts', 'rb'); |
範例 5-30:明顯的 file:// 串流包裝
1 | $handle = fopen('file:///etc/hosts', 'rb'); |
php:// 串流包裝
php://
串流包裝可以和 PHP 腳本的標準輸入/輸出、標準錯誤敘述進行溝通,可以使用 PHP 的檔案系統函式開啟和讀寫這四種串流:
php://stdin
,顯示來自標準輸入的資料,例如來自命令列傳入的資訊。php://stdout
,將資料寫入當前的緩衝,這個串流只允許寫入。php://memory
,讀寫資料到系統記憶體。php://temp
,當可用記憶體耗盡時,轉而寫入暫存檔。
PHP 檔案系統函式只是為了檔案系統而存在,這是錯誤的觀念,PHP 檔案系統函式可以跟所有有支援的串流包裝一起使用,例如 Amazon、Dropbox 等服務。
自製串流包裝
PHP 提供 streamWrapper
類別說明如何撰寫自製的串流包裝,並支援部分或全部的檔案系統函式。
串流背景
有些 PHP 串流可以接受一些額外參數,用來客製化串流的行為。使用 stream_context_create()
函式來建立串流背景,它會回傳一個背景物件,可以被大部分的 PHP 檔案系統和串流函式使用。
範例 5-31:串流背景
1 | $requestBody = '{"username":"josh"}'; |
串流過濾器
PHP 串流的厲害之處在於傳送時過濾、轉化、添加和移除串流資料的功能。
以下範例使用 tream_filter_append()
函式將過濾器加裝在串流上。
範例 5-32:串流過濾器 string.toupper
範例
1 | $handle = fopen('data.txt', 'rb'); |
以下範例使用 php://filter
串流包裝加裝過濾器到串流。
範例 5-33:串流過濾器 string.toupper
範例
1 | $handle = fopen('php://filter/read=string.toupper/resource=data.txt', 'rb'); |
- 有些 PHP 檔案系統函式只能使用
php://filter
串流包裝加裝過濾器。
假想每天都要開啟 rsync.net
中的日誌檔,每個日誌檔都用 bzip2
壓縮,檔名以 YYYY-MM-DD
作為格式,需要取得前 30 天的日誌紀錄。
範例 5-34:利用 DateTime
和串流過濾器疊代壓縮的日誌檔。
1 | $dateStart = new \DateTime(); |
自製串流過濾器
自製的串流過濾器繼承了 php_user_filter
內建類別的 PHP 類別。
PHP 串流將資料分成連續的 bucket
,每個 bucket
包含固定長度的串流資料(例如 4096 位元組)。每個串流過濾器可以同時接收並且處理數個 bucket
,一段時間內被過濾器接收的 bucket
稱為 bucket brigade
。
假想需要建立一個可以過濾不雅字眼的過濾器。
範例 5-35:自製的 DirtyWordsFilter 串流過濾器
1 | class DirtyWordsFilter extends php_user_filter |
- 過濾器如果處理成功,回傳
PSFS_PASS_ON
常數。
上述例子 filter()
函式接受四個參數:
$in
,一個brigade
帶有多個從串流起點而來的bucket
。$out
,一個brigade
帶有多個通向串流目的地的bucket
。&$consumed
,被自製的過濾器所篩選掉的位元組總數。$closing
,表示filter()
方法是否接收了最後一個bucket bragade
。
範例 5-36:註冊自製的 DirtyWordsFilter 串流過濾器
1 | stream_filter_register('dirty_words_filter', 'DirtyWordsFilter'); |
範例 5-37:使用自製 DirtyWordsFilter 串流過濾器
1 | $handle = fopen('data.txt', 'rb'); |
參考資料
- Josh Lockhart(2015)。現代 PHP。台北市:碁峯資訊。