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

1.1     ! misho       1: An Overview of the PHP Streams abstraction
        !             2: ==========================================
        !             3: $Id: README.STREAMS 242949 2007-09-26 15:44:16Z cvs2svn $
        !             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>