Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 244 lines (185 sloc) 8.998 kb
e85f4fd Wez Furlong Information about php streams
wez authored
1 An Overview of the PHP Streams abstraction
2 ==========================================
3 $Id$
4
5 Please send comments to: Wez Furlong <wez@thebrainroom.com>
6
7 Note: this doc is preliminary and is intended to give the reader an idea of
8 how streams work and should be used.
9
10 Why Streams?
11 ============
12 You may have noticed a shed-load of issock parameters flying around the PHP
13 code; we don't want them - they are ugly and cumbersome and force you to
14 special case sockets and files everytime you need to work with a "user-level"
15 PHP file pointer.
16 Streams take care of that and present the PHP extension coder with an ANSI
17 stdio-alike API that looks much nicer and can be extended to support non file
18 based data sources.
19
20 Using Streams
21 =============
22 Streams use a php_stream* parameter just as ANSI stdio (fread etc.) use a
23 FILE* parameter.
24
25 The main functions are:
26
27 PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
28 PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t
29 count);
30 PHPAPI int php_stream_eof(php_stream * stream);
31 PHPAPI int php_stream_getc(php_stream * stream);
32 PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen);
33 PHPAPI int php_stream_close(php_stream * stream);
34 PHPAPI int php_stream_flush(php_stream * stream);
35 PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence);
36 PHPAPI off_t php_stream_tell(php_stream * stream);
37
38 These (should) behave in the same way as the ANSI stdio functions with similar
39 names: fread, fwrite, feof, fgetc, fgets, fclose, fflush, fseek, ftell.
40
41 Opening Streams
42 ===============
43 Ultimately, I aim to implement an fopen_wrapper-like call to do this with
44 minimum fuss.
45 Currently, mostly for testing purposes, you can use php_stream_fopen to open a
46 stream on a regular file.
47
48 PHPAPI php_stream * php_stream_fopen(const char * filename, const char *
49 mode);
50
51 This call behaves just like fopen(), except it returns a stream instead of a
52 FILE *
53
54 Casting Streams
55 ===============
56 What if your extension needs to access the FILE* of a user level file pointer?
57 You need to "cast" the stream into a FILE*, and this is how you do it:
58
59 FILE * fp;
60 php_stream * stream; /* already opened */
61
62 if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, &fp, 1) == FAILURE) {
63 RETURN_FALSE;
64 }
65
66 The prototype is:
67
68 PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int
69 show_err);
70
71 The show_err parameter, if non-zero, will cause the function to display an
72 appropriate error message of type E_WARNING if the cast fails.
73
74 castas can be one of the following values:
75 PHP_STREAM_AS_STDIO - a stdio FILE*
76 PHP_STREAM_AS_FD - a generic file descriptor
77 PHP_STREAM_AS_SOCKETD - a socket descriptor
78
79 If you ask a socket stream for a FILE*, the abstraction will use fdopen to
80 create it for you. Be warned that doing so may cause buffered data to be lost
81 if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
82
83 If your system has the fopencookie function, php streams can synthesize a
84 FILE* on top of any stream, which is useful for SSL sockets, memory based
85 streams, data base streams etc. etc.
86 NOTE: There might be situations where this is not desireable, and we need to
87 provide a flag to inform the casting routine of this.
88
89 You can use:
90
91 PHPAPI int php_stream_can_cast(php_stream * stream, int castas)
92
93 to find out if a stream can be cast, without actually performing the cast, so
94 to check if a stream is a socket you might use:
95
96 if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS) {
97 /* it's a socket */
98 }
99
100
101 Stream Internals
102 ================
103
104 There are two main structures associated with a stream - the php_stream
105 itself, which holds some state information (and possibly a buffer) and a
106 php_stream_ops structure, which holds the "virtual method table" for the
107 underlying implementation.
108
109 The php_streams ops struct consists of pointers to methods that implement
110 read, write, close, flush, seek, gets and cast operations. Of these, an
111 implementation need only implement write, read, close and flush. The gets
112 method is intended to be used for non-buffered streams if there is an
113 underlying method that can efficiently behave as fgets. The ops struct also
114 contains a label for the implementation that will be used when printing error
115 messages - the stdio implementation has a label of "STDIO" for example.
116
117 The idea is that a stream implementation defines a php_stream_ops struct, and
118 associates it with a php_stream using php_stream_alloc.
119
120 As an example, the php_stream_fopen() function looks like this:
121
122 PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
123 {
124 FILE * fp = fopen(filename, mode);
125 php_stream * ret;
126
127 if (fp) {
128 ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode);
129 if (ret)
130 return ret;
131
132 fclose(fp);
133 }
134 return NULL;
135 }
136
137 php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
138 FILE* based streams.
139
140 A socket based stream would use code similar to that above to create a stream
141 to be passed back to fopen_wrapper (or it's yet to be implemented successor).
142
143 The prototype for php_stream_alloc is this:
144
145 PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
146 size_t bufsize, int persistent, const char * mode)
147
148 ops is a pointer to the implementation,
149 abstract holds implementation specific data that is relevant to this instance
150 of the stream,
151 bufsize is the size of the buffer to use - if 0, then buffering at the stream
152 level will be disabled (recommended for underlying sources that implement
153 their own buffering - such a FILE*),
154 persistent controls how the memory is to be allocated - persistently so that
155 it lasts across requests, or non-persistently so that it is freed at the end
156 of a request (it uses pemalloc),
157 mode is the stdio-like mode of operation - php streams places no real meaning
158 in the mode parameter, except that it checks for a 'w' in the string when
159 attempting to write (this may change).
160
161 The mode parameter is passed on to fdopen/fopencookie when the stream is cast
162 into a FILE*, so it should be compatible with the mode parameter of fopen().
163
164 Writing your own stream implementation
165 ======================================
166
167 First, you need to figure out what data you need to associate with the
168 php_stream. For example, you might need a pointer to some memory for memory
169 based streams, or if you were making a stream to read data from an RDBMS like
170 mysql, you might want to store the connection and rowset handles.
171
172 The stream has a field called abstract that you can use to hold this data.
173 If you need to store more than a single field of data, define a structure to
174 hold it, allocate it (use pemalloc with the persistent flag set
175 appropriately), and use the abstract pointer to refer to it.
176
177 For structured state you might have this:
178
179 struct my_state {
180 MYSQL conn;
181 MYSQL_RES * result;
182 };
183
184 struct my_state * state = pemalloc(sizeof(struct my_state), persistent);
185
186 /* initialize the connection, and run a query, using the fields in state to
187 * hold the results */
188
189 state->result = mysql_use_result(&state->conn);
190
191 /* now allocate the stream itself */
192 stream = php_stream_alloc(&my_ops, state, 0, persistent, "r");
193
194 /* now stream->abstract == state */
195
196 Once you have that part figured out, you can write your implementation and
197 define the your own php_stream_ops struct (we called it my_ops in the above
198 example).
199
200 For example, for reading from this wierd mysql stream:
201
202 static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
203 {
204 struct my_state * state = (struct my_state*)stream->abstract;
205
206 if (buf == NULL && count == 0) {
207 /* in this special case, php_streams is asking if we have reached the
208 * end of file */
209 if (... at end of file ...)
210 return EOF;
211 else
212 return 0;
213 }
214
215 /* pull out some data from the stream and put it in buf */
216 ... mysql_fetch_row(state->result) ...
217 /* we could do something strange, like format the data as XML here,
218 and place that in the buf, but that brings in some complexities,
219 such as coping with a buffer size too small to hold the data,
220 so I won't even go in to how to do that here */
221 }
222
223 Implement the other operations - remember that write, read, close and flush
224 are all mandatory. The rest are optional. Declare your stream ops struct:
225
226 php_stream_ops my_ops = {
227 php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
228 php_mysqlop_flush, NULL, NULL, NULL,
229 "Strange mySQL example"
230 }
231
232 Thats it!
233
234 Take a look at the STDIO implementation in streams.c for more information
235 about how these operations work.
236 The main thing to remember is that in your close operation you need to release
237 and free the resources you allocated for the abstract field. In the case of
238 the example above, you need to use mysql_free_result on the rowset, close the
239 connection and then use pefree to dispose of the struct you allocated.
240 You may read the stream->persistent field to determine if your struct was
241 allocated in persistent mode or not.
242
243 vim:tw=78
Something went wrong with that request. Please try again.