流的概念

流是一系列概念的集合,包括流包裝器、流資源、流操作、上下文等內容。流是對不同資源進行操作的抽象,允許線性地從指定位置讀取或寫入數據,通過一套統一的API簡化對資源操作的實現。

流由scheme://target指代,scheme是包裝器(Wrapper)的名字,target是流的目標地址。

PHP的流的實現較Java簡單,後者可以通過嵌套實現更靈活的應用。

流的實現

存儲結構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
struct _php_stream {
php_stream_ops *ops;
void *abstract; /* convenience pointer for abstraction */
php_stream_filter_chain readfilters, writefilters;
php_stream_wrapper *wrapper; /* which wrapper was used to open the stream */
void *wrapperthis; /* convenience pointer for a instance of a wrapper */
zval *wrapperdata; /* fgetwrapperdata retrieves this */
int fgetss_state; /* for fgetss to handle multiline tags */
int is_persistent;
char mode[16]; /* "rwb" etc. ala stdio */
int rsrc_id; /* used for auto-cleanup */
int in_free; /* to prevent recursion during free */
/* so we know how to clean it up correctly. This should be set to
* PHP_STREAM_FCLOSE_XXX as appropriate */
int fclose_stdiocast;
FILE *stdiocast; /* cache this, otherwise we might leak! */
#if ZEND_DEBUG
int __exposed; /* non-zero if exposed as a zval somewhere */
#endif
char *orig_path;
php_stream_context *context;
int flags; /* PHP_STREAM_FLAG_XXX */
/* buffer */
off_t position; /* of underlying stream */
unsigned char *readbuf;
size_t readbuflen;
off_t readpos;
off_t writepos;
/* how much data to read when filling buffer */
size_t chunk_size;
int eof;
#if ZEND_DEBUG
const char *open_filename;
uint open_lineno;
#endif
struct _php_stream *enclosing_stream; /* this is a private stream owned by enclosing_stream */
}; /* php_stream */
typedef struct _php_stream_ops {
/* stdio like functions - these are mandatory! */
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; /* label for this ops structure */
/* these are optional */
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結構體最重要的成員是ops和abstract。ops包含了流實例的所有操作邏輯,特別地,php_stream_ops->close在php_stream結構被回收前提供了回收與該流實例相關的資源的機會。abstract用來存儲一個自定義結構的數據,在流的操作邏輯裡可以方便的訪問。

實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#define PHP_DONIESTREAM_STREAMTYPE "doniestream"
static size_t php_doniestream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
{
donie_stream_data *data = stream->abstract;
php_printf("Write to stream: %s\n", buf);
return count;
}
static size_t php_doniestream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
donie_stream_data *data = stream->abstract;
zval **val;
size_t read_size = count;
php_printf("Read from stream: %s\n", data->key);
return read_size;
}
static int php_doniestream_close(php_stream *stream, int close_handle TSRMLS_DC)
{
donie_stream_data *data = stream->abstract;
efree(data->key);
efree(data);
return 0;
}
static php_stream_ops php_doniestream_ops = {
php_doniestream_write,
php_doniestream_read,
php_doniestream_close,
NULL, /* flush */
PHP_DONIESTREAM_STREAMTYPE,
NULL, /* seek */
NULL, /* cast */
NULL, /* stat */
NULL, /* set_option */
};

主要是流的操作邏輯的實現,最後構建的php_stream_ops結構用於後面流包裝器中初始化流實例時賦給php_stream->ops。

包裝器的實現

Wrapper是對某一協議的封裝,主要包含對該類型的流的一系列操作邏輯的實現。

存儲結構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct _php_stream_wrapper {
php_stream_wrapper_ops *wops; /* operations the wrapper can perform */
void *abstract; /* context for the wrapper */
int is_url; /* so that PG(allow_url_fopen) can be respected */
};
typedef struct _php_stream_wrapper_ops {
/* open/create a wrapped stream */
php_stream *(*stream_opener)(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
/* close/destroy a wrapped stream */
int (*stream_closer)(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC);
/* stat a wrapped stream */
int (*stream_stat)(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
/* stat a URL */
int (*url_stat)(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC);
/* open a "directory" stream */
php_stream *(*dir_opener)(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
const char *label;
/* delete a file */
int (*unlink)(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context TSRMLS_DC);
/* rename a file */
int (*rename)(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context TSRMLS_DC);
/* Create/Remove directory */
int (*stream_mkdir)(php_stream_wrapper *wrapper, const char *url, int mode, int options, php_stream_context *context TSRMLS_DC);
int (*stream_rmdir)(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context TSRMLS_DC);
/* Metadata handling */
int (*stream_metadata)(php_stream_wrapper *wrapper, const char *url, int options, void *value, php_stream_context *context TSRMLS_DC);
} php_stream_wrapper_ops;

php_stream_wrapper中最重要的是ops成員,它包含了所有該類型的流的操作邏輯的實現。其中最重要的是stream_opener和stream_closer,前者是流的實例化邏輯,後者是流的析構邏輯。特別的,stream_closer主要是用來銷毀php_stream結構,而php_stream_ops->close是用來回收所有和該流實例相關的資源。

實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#define PHP_DONIESTREAM_WRAPPER "donie"
typedef struct _donie_stream_data {
off_t position;
char *key;
int key_len;
} donie_stream_data;
static php_stream *php_doniestream_wrapper_open(
php_stream_wrapper *wrapper,
const char *filename, const char *mode, int options,
char **opened_path, php_stream_context *context
STREAMS_DC TSRMLS_DC)
{
donie_stream_data *data;
php_url *url;
if (options & STREAM_OPEN_PERSISTENT)
{
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to open %s persistently.", filename);
return NULL;
}
url = php_url_parse(filename);
if (!url)
{
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to parse url %s.", filename);
return NULL;
}
data = emalloc(sizeof(donie_stream_data));
data->position = 0;
data->key_len = strlen(url->host);
data->key = estrndup(url->host, data->key_len+1);
php_url_free(url);
return php_stream_alloc(&php_doniestream_ops, data, 0, mode);
}
static php_stream_wrapper_ops php_doniestream_wrapper_ops = {
php_doniestream_wrapper_open,
NULL, /* stream_closer */
NULL, /* stream_stat */
NULL, /* url_stat */
NULL, /* dir_opener */
PHP_DONIESTREAM_WRAPPER,
NULL, /* unlink */
NULL, /* rename */
NULL, /* mkdir */
NULL, /* rmdir */
NULL /* stream_metadata */
};
static php_stream_wrapper php_doniestream_wrapper = {
&php_doniestream_wrapper_ops,
NULL, /* abstract */
0, /* is_url */
};
PHP_MINIT_FUNCTION(donie)
{
/* register stream wrapper */
if (php_register_url_stream_wrapper(PHP_DONIESTREAM_WRAPPER, &php_doniestream_wrapper TSRMLS_CC) == FAILURE)
{
return FAILURE;
}
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(donie)
{
/* unregister stream wrapper */
if (php_unregister_url_stream_wrapper(PHP_DONIESTREAM_WRAPPER TSRMLS_CC) == FAILURE)
{
return FAILURE;
}
return SUCCESS;
}

PHP_DONIESTREAM_WRAPPER定義了協議名“donie”,所有對格式為“donie://XXX”地址的操作將由這個流實現。

donie_stream_data是一個自定義的結構體,在創建流實例的時候初始化並賦給php_stream->abstract,為以後對流的操作提供方便。

這裡只實現了最關鍵的stream_opener函數,其中,用php_stream_alloc()創建新的流實例。

最後在模塊的MINIT中用php_register_url_stream_wrapper()註冊包裝器,並在MSHUTDOWN中用php_unregister_url_stream_wrapper()註銷。