PHP流的實現

| Comments

流的概念

流是一系列概念的集合,包括流包裝器、流資源、流操作、上下文等內容。流是對不同資源進行操作的抽象,允許線性地從指定位置讀取或寫入數據,通過一套統一的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()註銷。

Comments