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>