Annotation of embedaddon/php/README.STREAMS, revision 1.1.1.2

1.1       misho       1: An Overview of the PHP Streams abstraction
                      2: ==========================================
1.1.1.2 ! misho       3: $Id$
1.1       misho       4: 
                      5: WARNING: some prototypes in this file are out of date.
                      6: The information contained here is being integrated into
                      7: the PHP manual - stay tuned...
                      8: 
                      9: Please send comments to: Wez Furlong <wez@thebrainroom.com>
                     10: 
                     11: Why Streams?
                     12: ============
                     13: You may have noticed a shed-load of issock parameters flying around the PHP
                     14: code; we don't want them - they are ugly and cumbersome and force you to
                     15: special case sockets and files every time you need to work with a "user-level"
                     16: PHP file pointer.
                     17: Streams take care of that and present the PHP extension coder with an ANSI
                     18: stdio-alike API that looks much nicer and can be extended to support non file
                     19: based data sources.
                     20: 
                     21: Using Streams
                     22: =============
                     23: Streams use a php_stream* parameter just as ANSI stdio (fread etc.) use a
                     24: FILE* parameter.
                     25: 
                     26: The main functions are:
                     27: 
                     28: PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
                     29: PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t
                     30:         count);
                     31: PHPAPI size_t php_stream_printf(php_stream * stream TSRMLS_DC, 
                     32:         const char * fmt, ...);
                     33: PHPAPI int php_stream_eof(php_stream * stream);
                     34: PHPAPI int php_stream_getc(php_stream * stream);
                     35: PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen);
                     36: PHPAPI int php_stream_close(php_stream * stream);
                     37: PHPAPI int php_stream_flush(php_stream * stream);
                     38: PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence);
                     39: PHPAPI off_t php_stream_tell(php_stream * stream);
                     40: PHPAPI int php_stream_lock(php_stream * stream, int mode);
                     41: 
                     42: These (should) behave in the same way as the ANSI stdio functions with similar
                     43: names: fread, fwrite, fprintf, feof, fgetc, fgets, fclose, fflush, fseek, ftell, flock.
                     44: 
                     45: Opening Streams
                     46: ===============
                     47: In most cases, you should use this API:
                     48: 
                     49: PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode,
                     50:     int options, char **opened_path TSRMLS_DC);
                     51: 
                     52: Where:
                     53:     path is the file or resource to open.
                     54:     mode is the stdio compatible mode eg: "wb", "rb" etc.
                     55:     options is a combination of the following values:
                     56:         IGNORE_PATH  (default) - don't use include path to search for the file
                     57:         USE_PATH        - use include path to search for the file
                     58:         IGNORE_URL      - do not use plugin wrappers
                     59:         REPORT_ERRORS   - show errors in a standard format if something
                     60:                           goes wrong.
                     61:         STREAM_MUST_SEEK - If you really need to be able to seek the stream
                     62:                            and don't need to be able to write to the original
                     63:                            file/URL, use this option to arrange for the stream
                     64:                            to be copied (if needed) into a stream that can
                     65:                            be seek()ed.
                     66:                            
                     67:     opened_path is used to return the path of the actual file opened,
                     68:     but if you used STREAM_MUST_SEEK, may not be valid.  You are
                     69:     responsible for efree()ing opened_path.  opened_path may be (and usually
                     70:     is) NULL.
                     71: 
                     72: If you need to open a specific stream, or convert standard resources into
                     73: streams there are a range of functions to do this defined in php_streams.h.
                     74: A brief list of the most commonly used functions:
                     75: 
                     76: PHPAPI php_stream *php_stream_fopen_from_file(FILE *file, const char *mode);
                     77:     Convert a FILE * into a stream.
                     78: 
                     79: PHPAPI php_stream *php_stream_fopen_tmpfile(void);
                     80:     Open a FILE * with tmpfile() and convert into a stream.
                     81: 
                     82: PHPAPI php_stream *php_stream_fopen_temporary_file(const char *dir,
                     83:     const char *pfx, char **opened_path TSRMLS_DC);
                     84:     Generate a temporary file name and open it.
                     85: 
                     86: There are some network enabled relatives in php_network.h:
                     87: 
                     88: PHPAPI php_stream *php_stream_sock_open_from_socket(int socket, int persistent);
                     89:     Convert a socket into a stream.
                     90: 
                     91: PHPAPI php_stream *php_stream_sock_open_host(const char *host, unsigned short port,
                     92:                int socktype, int timeout, int persistent);
                     93:     Open a connection to a host and return a stream.
                     94: 
                     95: PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent,
                     96:     struct timeval *timeout);
                     97:     Open a UNIX domain socket.
                     98:    
                     99: 
                    100: Stream Utilities
                    101: ================
                    102: 
                    103: If you need to copy some data from one stream to another, you will be please
                    104: to know that the streams API provides a standard way to do this:
                    105: 
                    106: PHPAPI size_t php_stream_copy_to_stream(php_stream *src,
                    107:     php_stream *dest, size_t maxlen);
                    108: 
                    109: If you want to copy all remaining data from the src stream, pass
                    110: PHP_STREAM_COPY_ALL as the maxlen parameter, otherwise maxlen indicates the
                    111: number of bytes to copy.
                    112: This function will try to use mmap where available to make the copying more
                    113: efficient.
                    114: 
                    115: If you want to read the contents of a stream into an allocated memory buffer,
                    116: you should use:
                    117: 
                    118: PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf,
                    119:     size_t maxlen, int persistent);
                    120: 
                    121: This function will set buf to the address of the buffer that it allocated,
                    122: which will be maxlen bytes in length, or will be the entire length of the
                    123: data remaining on the stream if you set maxlen to PHP_STREAM_COPY_ALL.
                    124: The buffer is allocated using pemalloc(); you need to call pefree() to
                    125: release the memory when you are done.
                    126: As with copy_to_stream, this function will try use mmap where it can.
                    127: 
                    128: If you have an existing stream and need to be able to seek() it, you
                    129: can use this function to copy the contents into a new stream that can
                    130: be seek()ed:
                    131: 
                    132: PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream);
                    133: 
                    134: It returns one of the following values:
                    135: #define PHP_STREAM_UNCHANGED   0 /* orig stream was seekable anyway */
                    136: #define PHP_STREAM_RELEASED            1 /* newstream should be used; origstream is no longer valid */
                    137: #define PHP_STREAM_FAILED              2 /* an error occurred while attempting conversion */
                    138: #define PHP_STREAM_CRITICAL            3 /* an error occurred; origstream is in an unknown state; you should close origstream */
                    139: 
                    140: make_seekable will always set newstream to be the stream that is valid
                    141: if the function succeeds.
                    142: When you have finished, remember to close the stream.
                    143: 
                    144: NOTE: If you only need to seek forward, there is no need to call this
                    145: function, as the php_stream_seek can emulate forward seeking when the
                    146: whence parameter is SEEK_CUR.
                    147: 
                    148: NOTE: Writing to the stream may not affect the original source, so it
                    149: only makes sense to use this for read-only use.
                    150: 
                    151: NOTE: If the origstream is network based, this function will block
                    152: until the whole contents have been downloaded.
                    153: 
                    154: NOTE: Never call this function with an origstream that is referenced
                    155: as a resource! It will close the origstream on success, and this
                    156: can lead to a crash when the resource is later used/released.
                    157: 
                    158: NOTE: If you are opening a stream and need it to be seekable, use the
                    159: STREAM_MUST_SEEK option to php_stream_open_wrapper();
                    160: 
                    161: PHPAPI int php_stream_supports_lock(php_stream * stream);
                    162: 
                    163: This function will return either 1 (success) or 0 (failure) indicating whether or
                    164: not a lock can be set on this stream. Typically you can only set locks on stdio streams.
                    165: 
                    166: Casting Streams
                    167: ===============
                    168: What if your extension needs to access the FILE* of a user level file pointer?
                    169: You need to "cast" the stream into a FILE*, and this is how you do it:
                    170: 
                    171: FILE * fp;
                    172: php_stream * stream; /* already opened */
                    173: 
                    174: if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void*)&fp, REPORT_ERRORS) == FAILURE)    {
                    175:     RETURN_FALSE;
                    176: }
                    177: 
                    178: The prototype is:
                    179: 
                    180: PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int
                    181:         show_err);
                    182: 
                    183: The show_err parameter, if non-zero, will cause the function to display an
                    184: appropriate error message of type E_WARNING if the cast fails.
                    185: 
                    186: castas can be one of the following values:
                    187: PHP_STREAM_AS_STDIO - a stdio FILE*
                    188: PHP_STREAM_AS_FD - a generic file descriptor
                    189: PHP_STREAM_AS_SOCKETD - a socket descriptor
                    190: 
                    191: If you ask a socket stream for a FILE*, the abstraction will use fdopen to
                    192: create it for you.  Be warned that doing so may cause buffered data to be lost
                    193: if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
                    194: 
                    195: If your system has the fopencookie function, php streams can synthesize a
                    196: FILE* on top of any stream, which is useful for SSL sockets, memory based
                    197: streams, data base streams etc. etc.
                    198: 
                    199: In situations where this is not desirable, you should query the stream
                    200: to see if it naturally supports FILE *.  You can use this code snippet
                    201: for this purpose:
                    202: 
                    203:     if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
                    204:         /* can safely cast to FILE* with no adverse side effects */
                    205:     }
                    206: 
                    207: You can use:
                    208: 
                    209: PHPAPI int php_stream_can_cast(php_stream * stream, int castas)
                    210: 
                    211: to find out if a stream can be cast, without actually performing the cast, so
                    212: to check if a stream is a socket you might use:
                    213: 
                    214: if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS)  {
                    215:     /* it can be a socket */
                    216: }
                    217: 
                    218: Please note the difference between php_stream_is and php_stream_can_cast;
                    219: stream_is tells you if the stream is a particular type of stream, whereas
                    220: can_cast tells you if the stream can be forced into the form you request.
                    221: The former doesn't change anything, while the later *might* change some
                    222: state in the stream.
                    223: 
                    224: Stream Internals
                    225: ================
                    226: 
                    227: There are two main structures associated with a stream - the php_stream
                    228: itself, which holds some state information (and possibly a buffer) and a
                    229: php_stream_ops structure, which holds the "virtual method table" for the
                    230: underlying implementation.
                    231: 
                    232: The php_streams ops struct consists of pointers to methods that implement
                    233: read, write, close, flush, seek, gets and cast operations.  Of these, an
                    234: implementation need only implement write, read, close and flush.  The gets
                    235: method is intended to be used for streams if there is an underlying method
                    236: that can efficiently behave as fgets.  The ops struct also contains a label
                    237: for the implementation that will be used when printing error messages - the
                    238: stdio implementation has a label of "STDIO" for example.
                    239: 
                    240: The idea is that a stream implementation defines a php_stream_ops struct, and
                    241: associates it with a php_stream using php_stream_alloc.
                    242: 
                    243: As an example, the php_stream_fopen() function looks like this:
                    244: 
                    245: PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
                    246: {
                    247:     FILE * fp = fopen(filename, mode);
                    248:     php_stream * ret;
                    249:     
                    250:     if (fp) {
                    251:         ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode);
                    252:         if (ret)
                    253:             return ret;
                    254: 
                    255:         fclose(fp);
                    256:     }
                    257:     return NULL;
                    258: }
                    259: 
                    260: php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
                    261: FILE* based streams.
                    262: 
                    263: A socket based stream would use code similar to that above to create a stream
                    264: to be passed back to fopen_wrapper (or it's yet to be implemented successor).
                    265: 
                    266: The prototype for php_stream_alloc is this:
                    267: 
                    268: PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
                    269:         size_t bufsize, int persistent, const char * mode)
                    270: 
                    271: ops is a pointer to the implementation,
                    272: abstract holds implementation specific data that is relevant to this instance
                    273: of the stream,
                    274: bufsize is the size of the buffer to use - if 0, then buffering at the stream
                    275: level will be disabled (recommended for underlying sources that implement
                    276: their own buffering - such a FILE*),
                    277: persistent controls how the memory is to be allocated - persistently so that
                    278: it lasts across requests, or non-persistently so that it is freed at the end
                    279: of a request (it uses pemalloc),
                    280: mode is the stdio-like mode of operation - php streams places no real meaning
                    281: in the mode parameter, except that it checks for a 'w' in the string when
                    282: attempting to write (this may change).
                    283: 
                    284: The mode parameter is passed on to fdopen/fopencookie when the stream is cast
                    285: into a FILE*, so it should be compatible with the mode parameter of fopen().
                    286: 
                    287: Writing your own stream implementation
                    288: ======================================
                    289: 
                    290: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                    291: RULE #1: when writing your own streams: make sure you have configured PHP with
                    292: --enable-debug.
                    293: I've taken some great pains to hook into the Zend memory manager to help track
                    294: down allocation problems.  It will also help you spot incorrect use of the
                    295: STREAMS_DC, STREAMS_CC and the semi-private STREAMS_REL_CC macros for function
                    296: definitions.
                    297: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                    298: 
                    299: RULE #2: Please use the stdio stream as a reference; it will help you
                    300: understand the semantics of the stream operations, and it will always
                    301: be more up to date than these docs :-)
                    302: 
                    303: First, you need to figure out what data you need to associate with the
                    304: php_stream.  For example, you might need a pointer to some memory for memory
                    305: based streams, or if you were making a stream to read data from an RDBMS like
                    306: MySQL, you might want to store the connection and rowset handles.
                    307: 
                    308: The stream has a field called abstract that you can use to hold this data.
                    309: If you need to store more than a single field of data, define a structure to
                    310: hold it, allocate it (use pemalloc with the persistent flag set
                    311: appropriately), and use the abstract pointer to refer to it.
                    312: 
                    313: For structured state you might have this:
                    314: 
                    315: struct my_state {
                    316:     MYSQL conn;
                    317:     MYSQL_RES * result;
                    318: };
                    319: 
                    320: struct my_state * state = pemalloc(sizeof(struct my_state), persistent);
                    321: 
                    322: /* initialize the connection, and run a query, using the fields in state to
                    323:  * hold the results */
                    324: 
                    325: state->result = mysql_use_result(&state->conn);
                    326: 
                    327: /* now allocate the stream itself */
                    328: stream = php_stream_alloc(&my_ops, state, 0, persistent, "r");
                    329: 
                    330: /* now stream->abstract == state */
                    331: 
                    332: Once you have that part figured out, you can write your implementation and
                    333: define the your own php_stream_ops struct (we called it my_ops in the above
                    334: example).
                    335: 
                    336: For example, for reading from this weird MySQL stream:
                    337: 
                    338: static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
                    339: {
                    340:     struct my_state * state = (struct my_state*)stream->abstract;
                    341: 
                    342:     if (buf == NULL && count == 0)  {
                    343:         /* in this special case, php_streams is asking if we have reached the
                    344:          * end of file */
                    345:         if (... at end of file ...)
                    346:             return EOF;
                    347:         else
                    348:             return 0;
                    349:     }
                    350:     
                    351:     /* pull out some data from the stream and put it in buf */
                    352:     ... mysql_fetch_row(state->result) ...
                    353:     /* we could do something strange, like format the data as XML here,
                    354:         and place that in the buf, but that brings in some complexities,
                    355:         such as coping with a buffer size too small to hold the data,
                    356:         so I won't even go in to how to do that here */
                    357: }
                    358: 
                    359: Implement the other operations - remember that write, read, close and flush
                    360: are all mandatory.  The rest are optional.  Declare your stream ops struct:
                    361: 
                    362: php_stream_ops my_ops = {
                    363:     php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
                    364:     php_mysqlop_flush, NULL, NULL, NULL,
                    365:     "Strange MySQL example"
                    366: }
                    367: 
                    368: Thats it!
                    369: 
                    370: Take a look at the STDIO implementation in streams.c for more information
                    371: about how these operations work.
                    372: The main thing to remember is that in your close operation you need to release
                    373: and free the resources you allocated for the abstract field.  In the case of
                    374: the example above, you need to use mysql_free_result on the rowset, close the
                    375: connection and then use pefree to dispose of the struct you allocated.
                    376: You may read the stream->persistent field to determine if your struct was
                    377: allocated in persistent mode or not.
                    378: 
                    379: vim:tw=78:et

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>