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