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