You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
8.8 KiB

  1. An Overview of the PHP Streams abstraction
  2. ==========================================
  3. $Id$
  4. Please send comments to: Wez Furlong <wez@thebrainroom.com>
  5. Note: this doc is preliminary and is intended to give the reader an idea of
  6. how streams work and should be used.
  7. Why Streams?
  8. ============
  9. You may have noticed a shed-load of issock parameters flying around the PHP
  10. code; we don't want them - they are ugly and cumbersome and force you to
  11. special case sockets and files everytime you need to work with a "user-level"
  12. PHP file pointer.
  13. Streams take care of that and present the PHP extension coder with an ANSI
  14. stdio-alike API that looks much nicer and can be extended to support non file
  15. based data sources.
  16. Using Streams
  17. =============
  18. Streams use a php_stream* parameter just as ANSI stdio (fread etc.) use a
  19. FILE* parameter.
  20. The main functions are:
  21. PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
  22. PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t
  23. count);
  24. PHPAPI int php_stream_eof(php_stream * stream);
  25. PHPAPI int php_stream_getc(php_stream * stream);
  26. PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen);
  27. PHPAPI int php_stream_close(php_stream * stream);
  28. PHPAPI int php_stream_flush(php_stream * stream);
  29. PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence);
  30. PHPAPI off_t php_stream_tell(php_stream * stream);
  31. These (should) behave in the same way as the ANSI stdio functions with similar
  32. names: fread, fwrite, feof, fgetc, fgets, fclose, fflush, fseek, ftell.
  33. Opening Streams
  34. ===============
  35. Ultimately, I aim to implement an fopen_wrapper-like call to do this with
  36. minimum fuss.
  37. Currently, mostly for testing purposes, you can use php_stream_fopen to open a
  38. stream on a regular file.
  39. PHPAPI php_stream * php_stream_fopen(const char * filename, const char *
  40. mode);
  41. This call behaves just like fopen(), except it returns a stream instead of a
  42. FILE *
  43. Casting Streams
  44. ===============
  45. What if your extension needs to access the FILE* of a user level file pointer?
  46. You need to "cast" the stream into a FILE*, and this is how you do it:
  47. FILE * fp;
  48. php_stream * stream; /* already opened */
  49. if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, &fp, 1) == FAILURE) {
  50. RETURN_FALSE;
  51. }
  52. The prototype is:
  53. PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int
  54. show_err);
  55. The show_err parameter, if non-zero, will cause the function to display an
  56. appropriate error message of type E_WARNING if the cast fails.
  57. castas can be one of the following values:
  58. PHP_STREAM_AS_STDIO - a stdio FILE*
  59. PHP_STREAM_AS_FD - a generic file descriptor
  60. PHP_STREAM_AS_SOCKETD - a socket descriptor
  61. If you ask a socket stream for a FILE*, the abstraction will use fdopen to
  62. create it for you. Be warned that doing so may cause buffered data to be lost
  63. if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
  64. If your system has the fopencookie function, php streams can synthesize a
  65. FILE* on top of any stream, which is useful for SSL sockets, memory based
  66. streams, data base streams etc. etc.
  67. NOTE: There might be situations where this is not desireable, and we need to
  68. provide a flag to inform the casting routine of this.
  69. You can use:
  70. PHPAPI int php_stream_can_cast(php_stream * stream, int castas)
  71. to find out if a stream can be cast, without actually performing the cast, so
  72. to check if a stream is a socket you might use:
  73. if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS) {
  74. /* it's a socket */
  75. }
  76. Stream Internals
  77. ================
  78. There are two main structures associated with a stream - the php_stream
  79. itself, which holds some state information (and possibly a buffer) and a
  80. php_stream_ops structure, which holds the "virtual method table" for the
  81. underlying implementation.
  82. The php_streams ops struct consists of pointers to methods that implement
  83. read, write, close, flush, seek, gets and cast operations. Of these, an
  84. implementation need only implement write, read, close and flush. The gets
  85. method is intended to be used for non-buffered streams if there is an
  86. underlying method that can efficiently behave as fgets. The ops struct also
  87. contains a label for the implementation that will be used when printing error
  88. messages - the stdio implementation has a label of "STDIO" for example.
  89. The idea is that a stream implementation defines a php_stream_ops struct, and
  90. associates it with a php_stream using php_stream_alloc.
  91. As an example, the php_stream_fopen() function looks like this:
  92. PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
  93. {
  94. FILE * fp = fopen(filename, mode);
  95. php_stream * ret;
  96. if (fp) {
  97. ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode);
  98. if (ret)
  99. return ret;
  100. fclose(fp);
  101. }
  102. return NULL;
  103. }
  104. php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
  105. FILE* based streams.
  106. A socket based stream would use code similar to that above to create a stream
  107. to be passed back to fopen_wrapper (or it's yet to be implemented successor).
  108. The prototype for php_stream_alloc is this:
  109. PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
  110. size_t bufsize, int persistent, const char * mode)
  111. ops is a pointer to the implementation,
  112. abstract holds implementation specific data that is relevant to this instance
  113. of the stream,
  114. bufsize is the size of the buffer to use - if 0, then buffering at the stream
  115. level will be disabled (recommended for underlying sources that implement
  116. their own buffering - such a FILE*),
  117. persistent controls how the memory is to be allocated - persistently so that
  118. it lasts across requests, or non-persistently so that it is freed at the end
  119. of a request (it uses pemalloc),
  120. mode is the stdio-like mode of operation - php streams places no real meaning
  121. in the mode parameter, except that it checks for a 'w' in the string when
  122. attempting to write (this may change).
  123. The mode parameter is passed on to fdopen/fopencookie when the stream is cast
  124. into a FILE*, so it should be compatible with the mode parameter of fopen().
  125. Writing your own stream implementation
  126. ======================================
  127. First, you need to figure out what data you need to associate with the
  128. php_stream. For example, you might need a pointer to some memory for memory
  129. based streams, or if you were making a stream to read data from an RDBMS like
  130. mysql, you might want to store the connection and rowset handles.
  131. The stream has a field called abstract that you can use to hold this data.
  132. If you need to store more than a single field of data, define a structure to
  133. hold it, allocate it (use pemalloc with the persistent flag set
  134. appropriately), and use the abstract pointer to refer to it.
  135. For structured state you might have this:
  136. struct my_state {
  137. MYSQL conn;
  138. MYSQL_RES * result;
  139. };
  140. struct my_state * state = pemalloc(sizeof(struct my_state), persistent);
  141. /* initialize the connection, and run a query, using the fields in state to
  142. * hold the results */
  143. state->result = mysql_use_result(&state->conn);
  144. /* now allocate the stream itself */
  145. stream = php_stream_alloc(&my_ops, state, 0, persistent, "r");
  146. /* now stream->abstract == state */
  147. Once you have that part figured out, you can write your implementation and
  148. define the your own php_stream_ops struct (we called it my_ops in the above
  149. example).
  150. For example, for reading from this wierd mysql stream:
  151. static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
  152. {
  153. struct my_state * state = (struct my_state*)stream->abstract;
  154. if (buf == NULL && count == 0) {
  155. /* in this special case, php_streams is asking if we have reached the
  156. * end of file */
  157. if (... at end of file ...)
  158. return EOF;
  159. else
  160. return 0;
  161. }
  162. /* pull out some data from the stream and put it in buf */
  163. ... mysql_fetch_row(state->result) ...
  164. /* we could do something strange, like format the data as XML here,
  165. and place that in the buf, but that brings in some complexities,
  166. such as coping with a buffer size too small to hold the data,
  167. so I won't even go in to how to do that here */
  168. }
  169. Implement the other operations - remember that write, read, close and flush
  170. are all mandatory. The rest are optional. Declare your stream ops struct:
  171. php_stream_ops my_ops = {
  172. php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
  173. php_mysqlop_flush, NULL, NULL, NULL,
  174. "Strange mySQL example"
  175. }
  176. Thats it!
  177. Take a look at the STDIO implementation in streams.c for more information
  178. about how these operations work.
  179. The main thing to remember is that in your close operation you need to release
  180. and free the resources you allocated for the abstract field. In the case of
  181. the example above, you need to use mysql_free_result on the rowset, close the
  182. connection and then use pefree to dispose of the struct you allocated.
  183. You may read the stream->persistent field to determine if your struct was
  184. allocated in persistent mode or not.
  185. vim:tw=78