xref: /PHP-7.3/README.STREAMS (revision 3362620b)
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