PHP 流抽象層核心在於 php_stream_ops (流操作虛表) 與 php_stream_wrapper_ops (包裝器操作虛表)。本文深入剖析兩結構體、標準 I/O 與 zlib 流實作對比、自定義包裝器開發流程。
typedef struct _php_stream {
php_stream_ops *ops; // 虛表:實際操作函式指標
void *abstract; // 便利指標:儲存具體資料 (如 FILE*, gzFile)
// ... 其他欄位 (位置、狀態、快取、鎖等)
} php_stream;
typedef struct _php_stream_ops {
size_t (*write)(php_stream *stream, const char *buf, size_t count TSRMLS_DC);
size_t (*read)(php_stream *stream, char *buf, size_t count TSRMLS_DC);
int (*close)(php_stream *stream, int close_handle TSRMLS_DC);
int (*flush)(php_stream *stream TSRMLS_DC);
const char *label; // 除錯標籤
int (*seek)(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
int (*cast)(php_stream *stream, int castas, void **ret TSRMLS_DC); // 轉換為 stdio/posix
int (*stat)(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
int (*set_option)(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
} php_stream_ops;
// 讀取實作
size_t php_stdio_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) {
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
return read(data->fd, buf, count); // POSIX read()
}
// abstract 結構
typedef struct {
int fd; // 檔案描述符
FILE *file; // stdio FILE*
// ...
} php_stdio_stream_data;
// 讀取實作
size_t php_zlib_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) {
struct php_gz_stream_data_t *data = (struct php_gz_stream_data_t*)stream->abstract;
return gzread(data->gz_file, buf, count); // zlib gzread()
}
// abstract 結構
typedef struct {
gzFile gz_file; // zlib 壓縮檔案句柄
// ...
} php_gz_stream_data_t;
ops 函式為底層 I/O 的**薄封裝** (Thin Wrapper)。stream->abstract 儲存具體實作所需上下文 (fd、gzFile、SSL* 等)。php_stream_read() → 派發至 stream->ops->read()。typedef struct _php_stream_wrapper_ops {
// 核心:開啟流
php_stream *(*stream_opener)(php_stream_wrapper *wrapper,
char *filename, char *mode, int options,
char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
// 關閉流
int (*stream_closer)(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC);
// 流 stat
int (*stream_stat)(php_stream_wrapper *wrapper, php_stream *stream,
php_stream_statbuf *ssb TSRMLS_DC);
// URL stat (不開啟流)
int (*url_stat)(php_stream_wrapper *wrapper, char *url, int flags,
php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC);
// 目錄開啟
php_stream *(*dir_opener)(php_stream_wrapper *wrapper,
char *filename, char *mode, int options,
char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
const char *label;
// 檔案系統操作
int (*unlink)(php_stream_wrapper *wrapper, char *url, int options,
php_stream_context *context TSRMLS_DC);
int (*rename)(php_stream_wrapper *wrapper, char *url_from, char *url_to,
int options, php_stream_context *context TSRMLS_DC);
int (*stream_mkdir)(php_stream_wrapper *wrapper, char *url, int mode,
int options, php_stream_context *context TSRMLS_DC);
int (*stream_rmdir)(php_stream_wrapper *wrapper, char *url, int options,
php_stream_context *context TSRMLS_DC);
} php_stream_wrapper_ops;
php_stream_ops 實作讀/寫/關閉/Seek 等。php_stream_wrapper_ops 實作 stream_opener (建立 stream、設定 ops/abstract)。php_stream_wrapper 結合 wrapper_ops、scheme 名稱、是否為 URL 包裝器。MINIT 中呼叫 php_register_url_stream_wrapper("scheme", &wrapper) 註冊。MSHUTDOWN 中呼叫 php_unregister_url_stream_wrapper("scheme") 註銷。// 1. 定義 abstract 結構
typedef struct {
char *data;
size_t size;
size_t pos;
} php_memory_stream_data;
// 2. 實作 ops
static size_t php_memory_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) {
php_memory_stream_data *data = (php_memory_stream_data*)stream->abstract;
size_t avail = data->size - data->pos;
if (count > avail) count = avail;
if (count > 0) {
memcpy(buf, data->data + data->pos, count);
data->pos += count;
}
return count;
}
static int php_memory_close(php_stream *stream, int close_handle TSRMLS_DC) {
php_memory_stream_data *data = (php_memory_stream_data*)stream->abstract;
if (data->data) efree(data->data);
efree(data);
return 0;
}
static php_stream_ops php_memory_ops = {
.write = NULL, // 唯讀範例
.read = php_memory_read,
.close = php_memory_close,
.flush = NULL,
.label = "memory",
.seek = NULL,
.cast = NULL,
.stat = NULL,
.set_option = NULL
};
// 3. 實作 wrapper_opener
static php_stream *php_memory_stream_opener(php_stream_wrapper *wrapper,
char *filename, char *mode, int options,
char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) {
// filename 格式: "memory://data_id" 或直接內容
// 此處簡化:從 context 或全域取得預載資料
php_stream *stream = php_stream_alloc(&php_memory_ops, NULL, 0, mode);
// 設定 abstract...
return stream;
}
// 4. 註冊
PHP_MINIT_FUNCTION(memory_wrapper) {
php_stream_wrapper wrapper;
wrapper.wops = &php_memory_wrapper_ops;
wrapper.is_url = 0;
wrapper.flags = 0;
return php_register_url_stream_wrapper("memory", &wrapper TSRMLS_CC);
}
流過濾器 (php_stream_filter) 可在讀寫管道中插入轉換邏輯 (Base64、旋轉加密、壓縮、字元編碼轉換),詳見第 16 章。