]> granicus.if.org Git - php/commitdiff
Information about php streams
authorWez Furlong <wez@php.net>
Wed, 18 Apr 2001 10:43:21 +0000 (10:43 +0000)
committerWez Furlong <wez@php.net>
Wed, 18 Apr 2001 10:43:21 +0000 (10:43 +0000)
README.STREAMS [new file with mode: 0644]

diff --git a/README.STREAMS b/README.STREAMS
new file mode 100644 (file)
index 0000000..2ae89b7
--- /dev/null
@@ -0,0 +1,243 @@
+An Overview of the PHP Streams abstraction
+==========================================
+$Id$
+
+Please send comments to: Wez Furlong <wez@thebrainroom.com>
+
+Note: this doc is preliminary and is intended to give the reader an idea of
+how streams work and should be used.
+
+Why Streams?
+============
+You may have noticed a shed-load of issock parameters flying around the PHP
+code; we don't want them - they are ugly and cumbersome and force you to
+special case sockets and files everytime you need to work with a "user-level"
+PHP file pointer.
+Streams take care of that and present the PHP extension coder with an ANSI
+stdio-alike API that looks much nicer and can be extended to support non file
+based data sources.
+
+Using Streams
+=============
+Streams use a php_stream* parameter just as ANSI stdio (fread etc.) use a
+FILE* parameter.
+
+The main functions are:
+
+PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
+PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t
+               count);
+PHPAPI int php_stream_eof(php_stream * stream);
+PHPAPI int php_stream_getc(php_stream * stream);
+PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen);
+PHPAPI int php_stream_close(php_stream * stream);
+PHPAPI int php_stream_flush(php_stream * stream);
+PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence);
+PHPAPI off_t php_stream_tell(php_stream * stream);
+
+These (should) behave in the same way as the ANSI stdio functions with similar
+names: fread, fwrite, feof, fgetc, fgets, fclose, fflush, fseek, ftell.
+
+Opening Streams
+===============
+Ultimately, I aim to implement an fopen_wrapper-like call to do this with
+minimum fuss.
+Currently, mostly for testing purposes, you can use php_stream_fopen to open a
+stream on a regular file.
+
+PHPAPI php_stream * php_stream_fopen(const char * filename, const char *
+               mode);
+
+This call behaves just like fopen(), except it returns a stream instead of a
+FILE *
+
+Casting Streams
+===============
+What if your extension needs to access the FILE* of a user level file pointer?
+You need to "cast" the stream into a FILE*, and this is how you do it:
+
+FILE * fp;
+php_stream * stream; /* already opened */
+
+if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, &fp, 1) == FAILURE)   {
+       RETURN_FALSE;
+}
+
+The prototype is:
+
+PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int
+               show_err);
+
+The show_err parameter, if non-zero, will cause the function to display an
+appropriate error message of type E_WARNING if the cast fails.
+
+castas can be one of the following values:
+PHP_STREAM_AS_STDIO - a stdio FILE*
+PHP_STREAM_AS_FD - a generic file descriptor
+PHP_STREAM_AS_SOCKETD - a socket descriptor
+
+If you ask a socket stream for a FILE*, the abstraction will use fdopen to
+create it for you.  Be warned that doing so may cause buffered data to be lost
+if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
+
+If your system has the fopencookie function, php streams can synthesize a
+FILE* on top of any stream, which is useful for SSL sockets, memory based
+streams, data base streams etc. etc.
+NOTE: There might be situations where this is not desireable, and we need to
+provide a flag to inform the casting routine of this.
+
+You can use:
+
+PHPAPI int php_stream_can_cast(php_stream * stream, int castas)
+
+to find out if a stream can be cast, without actually performing the cast, so
+to check if a stream is a socket you might use:
+
+if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS)     {
+       /* it's a socket */
+}
+
+
+Stream Internals
+================
+
+There are two main structures associated with a stream - the php_stream
+itself, which holds some state information (and possibly a buffer) and a
+php_stream_ops structure, which holds the "virtual method table" for the
+underlying implementation.
+
+The php_streams ops struct consists of pointers to methods that implement
+read, write, close, flush, seek, gets and cast operations.  Of these, an
+implementation need only implement write, read, close and flush.  The gets
+method is intended to be used for non-buffered streams if there is an
+underlying method that can efficiently behave as fgets.  The ops struct also
+contains a label for the implementation that will be used when printing error
+messages - the stdio implementation has a label of "STDIO" for example.
+
+The idea is that a stream implementation defines a php_stream_ops struct, and
+associates it with a php_stream using php_stream_alloc.
+
+As an example, the php_stream_fopen() function looks like this:
+
+PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
+{
+       FILE * fp = fopen(filename, mode);
+       php_stream * ret;
+       
+       if (fp) {
+               ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode);
+               if (ret)
+                       return ret;
+
+               fclose(fp);
+       }
+       return NULL;
+}
+
+php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
+FILE* based streams.
+
+A socket based stream would use code similar to that above to create a stream
+to be passed back to fopen_wrapper (or it's yet to be implemented successor).
+
+The prototype for php_stream_alloc is this:
+
+PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
+               size_t bufsize, int persistent, const char * mode)
+
+ops is a pointer to the implementation,
+abstract holds implementation specific data that is relevant to this instance
+of the stream,
+bufsize is the size of the buffer to use - if 0, then buffering at the stream
+level will be disabled (recommended for underlying sources that implement
+their own buffering - such a FILE*),
+persistent controls how the memory is to be allocated - persistently so that
+it lasts across requests, or non-persistently so that it is freed at the end
+of a request (it uses pemalloc),
+mode is the stdio-like mode of operation - php streams places no real meaning
+in the mode parameter, except that it checks for a 'w' in the string when
+attempting to write (this may change).
+
+The mode parameter is passed on to fdopen/fopencookie when the stream is cast
+into a FILE*, so it should be compatible with the mode parameter of fopen().
+
+Writing your own stream implementation
+======================================
+
+First, you need to figure out what data you need to associate with the
+php_stream.  For example, you might need a pointer to some memory for memory
+based streams, or if you were making a stream to read data from an RDBMS like
+mysql, you might want to store the connection and rowset handles.
+
+The stream has a field called abstract that you can use to hold this data.
+If you need to store more than a single field of data, define a structure to
+hold it, allocate it (use pemalloc with the persistent flag set
+appropriately), and use the abstract pointer to refer to it.
+
+For structured state you might have this:
+
+struct my_state        {
+       MYSQL conn;
+       MYSQL_RES * result;
+};
+
+struct my_state * state = pemalloc(sizeof(struct my_state), persistent);
+
+/* initialize the connection, and run a query, using the fields in state to
+ * hold the results */
+
+state->result = mysql_use_result(&state->conn);
+
+/* now allocate the stream itself */
+stream = php_stream_alloc(&my_ops, state, 0, persistent, "r");
+
+/* now stream->abstract == state */
+
+Once you have that part figured out, you can write your implementation and
+define the your own php_stream_ops struct (we called it my_ops in the above
+example).
+
+For example, for reading from this wierd mysql stream:
+
+static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
+{
+       struct my_state * state = (struct my_state*)stream->abstract;
+
+       if (buf == NULL && count == 0)  {
+               /* in this special case, php_streams is asking if we have reached the
+                * end of file */
+               if (... at end of file ...)
+                       return EOF;
+               else
+                       return 0;
+       }
+       
+       /* pull out some data from the stream and put it in buf */
+       ... mysql_fetch_row(state->result) ...
+       /* we could do something strange, like format the data as XML here,
+               and place that in the buf, but that brings in some complexities,
+               such as coping with a buffer size too small to hold the data,
+               so I won't even go in to how to do that here */
+}
+
+Implement the other operations - remember that write, read, close and flush
+are all mandatory.  The rest are optional.  Declare your stream ops struct:
+
+php_stream_ops my_ops = {
+       php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
+       php_mysqlop_flush, NULL, NULL, NULL,
+       "Strange mySQL example"
+}
+
+Thats it!
+
+Take a look at the STDIO implementation in streams.c for more information
+about how these operations work.
+The main thing to remember is that in your close operation you need to release
+and free the resources you allocated for the abstract field.  In the case of
+the example above, you need to use mysql_free_result on the rowset, close the
+connection and then use pefree to dispose of the struct you allocated.
+You may read the stream->persistent field to determine if your struct was
+allocated in persistent mode or not.
+
+vim:tw=78