xref: /PHP-5.5/README.STREAMS (revision 6efd9ea5)
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(char *path, 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