實現流
php的流最強力的特性之一是它可以訪問眾多數據源: 普通文件, 壓縮文件, 網絡透明通道, 加密網絡, 命名管道以及域套接字, 它們對於用戶空間以及內部都是統一的API.
php流的表象之下
對於給定的流實例, 比如文件流和網絡流, 它們的不同在於上一章你使用的流創建函數返回的php_stream結構體中的ops成員.
typedef struct _php_stream { ... php_stream_ops *ops; ... } php_stream;
php_stream_ops結構體定義的是一個函數指針集合以及一個描述標記.
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); 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;
當流訪問函數比如php_stream_read()被調用時, 流包裝層實際上解析調用了stream->ops中對應的函數, 這樣實際調用的就是當前流類型特有的read實現. 比如, 普通文件的流ops結構體中的read函數實現如下(實際的該實現比下面的示例復雜一點):
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); }
而compress.zlib流使用的ops結構體中則read則指向的是如下的函數:
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); }
這裡第一點需要注意的是ops結構體指向的函數指針常常是對數據源真正的讀取函數的一個瘦代理. 在上面兩個例子中, 標准I/O流使用posix的read()函數, 而zlib流使用的是libz的gzread()函數.
你可能還注意到了, 這裡使用了stream->abstract元素. 這是流實現的一個便利指針, 它可以被用於獲取各種相關的捆綁信息. 在上面的例子中, 指向自定義結構體的指針, 用於存儲底層read函數要使用的文件描述符.
還有一件你可能注意到的事情是php_stream_ops結構體中的每個函數都期望一個已有的流實例, 但是怎樣得到實例呢? abstract成員是怎樣設置的以及什麼時候流指示使用哪個ops結構體? 答案就在你在上一章使用過的第一個打開流的函數(php_stream_open_wrapper())中.
當這個函數被調用時, php的流包裝層嘗試基於傳遞的URL中的scheme://部分確定請求的是什麼協議. 這樣它就可以在已注冊的php包裝器中查找對應的php_stream_wrapper項. 每個php_stream_wrapper結構體都可以取到自己的ops元素, 它指向一個php_stream_wrapper_ops結構體:
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); int (*stream_stat)(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); 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;