Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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