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