PHP Stream(流)是 PHP 4.3.0 引入的強大抽象層,統一了檔案、網路、壓縮等類檔案操作介面。善用 Stream 能大幅提升開發效率,甚至實現虛擬檔案系統、雲存儲透明訪問等高階功能。本文系統解析 Stream 核心概念、內建包裝器、上下文控制、過濾器鏈,並預告自定義包裝器開發。
Stream 是一個具有流式行為的資源物件:可線性讀寫、可用
fseek()跳轉位置。每個 Stream 都有對應的「包裝器」處理特定協議與編碼。
<scheme>://<target>
# 範例
file:///path/to/file.txt
https://example.com/api
compress.zlib://data.gz
php://input
phar://archive.phar/file.php
<scheme>:包裝器名稱(協議)<target>:由包裝器語法定義的目標資源PHP 預設使用 file://,因此 readfile('/path/file') 等同於 readfile('file:///path/file')。
<?php
print_r(stream_get_transports()); // 傳輸層:tcp, udp, unix, ssl, tls...
print_r(stream_get_wrappers()); // 包裝器:file, http, ftp, compress.zlib, php, data, glob, zip, phar...
print_r(stream_get_filters()); // 過濾器:zlib.*, bzip2.*, convert.iconv.*, string.*, mcrypt.*...
?>
Array
(
[0] => tcp
[1] => udp
[2] => unix
[3] => udg
[4] => ssl
[5] => sslv3
[6] => sslv2
[7] => tls
)
Array
(
[0] => https
[1] => ftps
[2] => compress.zlib
[3] => compress.bzip2
[4] => php
[5] => file
[6] => glob
[7] => data
[8] => http
[9] => ftp
[10] => zip
[11] => phar
)
Array
(
[0] => zlib.*
[1] => bzip2.*
[2] => convert.iconv.*
[3] => string.rot13
[4] => string.toupper
[5] => string.tolower
[6] => string.strip_tags
[7] => convert.*
[8] => consumed
[9] => dechunk
[10] => mcrypt.*
[11] => mdecrypt.*
)
| 包裝器 | 對應資源 | 模式 |
|---|---|---|
php://stdin | 標準輸入 | 唯讀 |
php://stdout | 標準輸出 | 唯寫 |
php://stderr | 標準錯誤輸出 | 唯寫 |
| 包裝器 | 儲存位置 | 適用場景 |
|---|---|---|
php://memory | 記憶體 | 小量暫存資料、高速讀寫 |
php://temp | 記憶體(小量)→ 暫存檔(大量) | 不確定大小的暫存資料 |
<?php
// 寫入暫存流
$stream = fopen('php://temp', 'r+');
fwrite($stream, "臨時資料\n第二行");
rewind($stream);
echo stream_get_contents($stream); // 讀取全部
fclose($stream);
?>
在開啟流時套用過濾器,支援讀寫雙向、多重串聯。
<?php
// 寫入時自動 ROT13 編碼
file_put_contents(
"php://filter/write=string.rot13/resource=file:///path/to/file.txt",
"Hello World"
);
// 讀取時:先大寫再 ROT13(管道串聯)
readfile("php://filter/read=string.toupper|string.rot13/resource=https://www.google.com");
?>
string.rot13 - ROT13 編碼string.toupper / string.tolower - 大小寫轉換string.strip_tags - 移除 HTML 標籤convert.iconv.* - 字元編碼轉換(如 convert.iconv.UTF-8/ISO-8859-1)convert.base64-encode / convert.base64-decode - Base64 編解碼Context 是一組參數選項,用於自定義包裝器的具體行為(如 HTTP Header、SSL 驗證、FTP 模式等)。
<?php
$opts = [
'http' => [
'method' => 'POST',
'header' => "Auth: SecretAuthToken\r\n" .
"Content-type: application/x-www-form-urlencoded\r\n" .
"Content-length: " . strlen("Hello World"),
'content' => 'Hello World',
'timeout' => 10,
'follow_location' => 1,
'max_redirects' => 3,
]
];
// 設定預設 context
$default = stream_context_get_default($opts);
// 後續所有 https:// 流自動套用上述選項
$response = file_get_contents('https://localhost/api/endpoint');
// 或建立獨立 context 交替使用
$altContext = stream_context_create($otherOpts);
$response2 = file_get_contents('https://localhost/api/other', false, $altContext);
?>
| 選項 | 說明 | 預設值 |
|---|---|---|
method | HTTP 方法 | GET |
header | 額外 Header(字串或陣列) | 無 |
content | 請求主體資料 | 無 |
user_agent | User-Agent 字串 | PHP/version |
timeout | 連線/讀取超時(秒) | ini 設定 |
follow_location | 是否跟隨重導向 | 1 |
max_redirects | 最大重導向次數 | 20 |
protocol_version | HTTP 協定版本 | 1.0 |
ignore_errors | 錯誤狀態碼仍返回內容 | false |
<?php
$sslOpts = [
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'cafile' => '/path/to/ca-bundle.crt',
'capath' => '/etc/ssl/certs/',
'local_cert' => '/path/to/client.pem',
'passphrase' => 'cert_password',
'allow_self_signed' => false,
'crypto_method' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
]
];
$context = stream_context_create(['http' => [], 'ssl' => $sslOpts['ssl']]);
$response = file_get_contents('https://secure-api.example.com', false, $context);
?>
過濾器可掛載到讀寫流上,實現資料的即時轉換。
<?php
$stream = fopen('data.txt', 'r');
// 掛載讀取過濾器
stream_filter_append($stream, 'string.toupper'); // 轉大寫
stream_filter_append($stream, 'zlib.inflate'); // 解壓縮
// 或使用 php://filter 包裝器(僅限開啟時)
$filtered = fopen('php://filter/read=string.rot13/resource=data.txt', 'r');
?>
<?php
class CustomFilter extends php_user_filter {
public function filter($in, $out, &$consumed, $closing) {
while ($bucket = stream_bucket_make_writeable($in)) {
// 處理資料
$bucket->data = str_rot13($bucket->data); // 範例:ROT13
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON; // PSFS_FEED_ME / PSFS_ERR_ON_ERROR
}
}
// 註冊
stream_filter_register('custom.rot13', 'CustomFilter');
// 使用
$stream = fopen('php://filter/read=custom.rot13/resource=data.txt', 'r');
?>
為 AWS S3、Google Cloud Storage、阿里云 OSS 開發 s3://、gs://、oss:// 包裝器,應用程式無修改即可遷移至雲端。
<?php
// 逐行處理大型 CSV,不載入記憶體
$stream = fopen('php://filter/read=convert.iconv.BIG5/UTF-8/resource=large.csv', 'r');
while (($row = fgetcsv($stream)) !== false) {
processRow($row);
}
fclose($stream);
?>
<?php
// cat data.json | php process.php | jq .
$input = fopen('php://stdin', 'r');
$output = fopen('php://stdout', 'w');
while (($line = fgets($input)) !== false) {
$data = json_decode($line, true);
$result = process($data);
fwrite($output, json_encode($result) . "\n");
}
?>
<?php
// 讀取 gzip 檔案無需解壓縮到磁碟
$fp = fopen('compress.zlib://large.log.gz', 'r');
while (!feof($fp)) {
echo fread($fp, 8192);
}
fclose($fp);
// 寫入 gzip
$fp = fopen('compress.zlib://output.log.gz', 'w');
fwrite($fp, $largeData);
fclose($fp);
?>
| 函數 | 用途 |
|---|---|
stream_wrapper_register() | 註冊自定義包裝器 |
stream_wrapper_unregister() | 取消註冊包裝器 |
stream_wrapper_restore() | 恢復內建包裝器 |
stream_context_create() | 建立 context |
stream_context_get_default() | 獲取/設定預設 context |
stream_context_set_option() | 設定 context 選項 |
stream_filter_register() | 註冊自定義過濾器 |
stream_filter_append/prepend() | 掛載過濾器到流 |
stream_get_meta_data() | 獲取流中繼資料 |
stream_copy_to_stream() | 高效流複製 |
stream_socket_client/server() | 底層 Socket 操作 |
PHP Stream 提供了統一、可擴展的 I/O 抽象層。掌握內建包裝器(php://、compress.*、https://)、Context 控制、過濾器鏈,能解決 90% 的資料流處理需求。下一步進階方向:開發自定義包裝器(實現 s3://、db:// 等虛擬協議),將在後續文章詳細介紹。