Accessing Streams
After you have a stream opened up, it's time to start performing I/O operations on it. It doesn't matter what protocol wrapper, transport, or "special" API call was used to create the stream; the set of API calls used to access it will be the same.
Reading
Stream readingand writingcan be performed using any combination of the following API functions, many of which follow the conventions of their POSIX I/O counterparts:
int php_stream_getc(php_stream *stream);
Retrieve a single character from the data stream. If no more data is available on the stream, EOF is returned instead.
size_t php_stream_read(php_stream *stream, char *buf, size_t count);
Read a specific number of bytes from the stream. buf must be preallocated to a size of at least count bytes. The function will return the number of bytes actually populated into buf from the data stream.
Note
php_stream_read() differs from other stream read functions in one surprising way. If the stream in use is not a plain files stream, only one call to the underlying stream implementation's read function will be made, even if more data was requested and more is actually available to return. This is a compromise to let packet-based protocols such as UDP function cleanly without blocking.
char *php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, size_t *returned_len); char *php_stream_gets(php_stream *stream, char *buf, size_t maxlen);
This code reads from stream up to a maximum of maxlen characters until a newline is encountered or the end of stream is reached. buf might be either a pointer to a preallocated buffer of at least maxlen bytes, or NULL, in which case a dynamically sized buffer will be created to fit the amount of data actually read from the stream. In either case, a pointer to the buffer is returned on success, or NULL on failure. If returned_len is passed with a non-NULL value, it will be populated according to the amount of data read from stream.
char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC);
Like php_stream_get_line(), this method will read up to maxlen, EOF, or an end or line marker, whichever comes first. Unlike php_stream_get_line(), however, this method allows the specification of an arbitrary marker to stop reading at.
Reading Directory Entries
Reading a directory entry from a PHP stream is, at the end of the day, identical to reading ordinary data from an ordinary file. The trick is that this data is delivered in fixed block sizes called dirents, or Directory Entries. Internally a php_stream_dirent structure has the following simple format, which is consistent with the POSIX definition of a dirent struct:
typedef struct _php_stream_dirent { char d_name[MAXPATHLEN]; } php_stream_dirent;
In practice you could simply read into this struct using the php_stream_read() method that you've already seen:
{ struct dirent entry; if (php_stream_read(stream, (char*)&entry, sizeof(entry)) == sizeof(entry)) { /* Successfully read an entry from a dirstream */ php_printf("File: %s ", entry.d_name); } }
Because reading from directory streams is common, the PHP streams layer exposes an API call to handle the record-size checking and typecasting issues in a single call:
php_stream_dirent *php_stream_readdir(php_stream *dirstream, php_stream_dirent *entry);
If a directory entry is successfully read, the pointer passed in for enTRy will be returned; otherwise, NULL is used to indicate an error condition. It's important to use this purposebuilt method rather than attempting to read directly from the directory stream so that future changes to the streams API won't conflict with your code.
Writing
Similar to reading, writing to a stream simply requires passing a buffer and a buffer length to a stream.
size_t php_stream_write(php_stream *stream, char *buf, size_t count); size_t php_stream_write_string(php_stream *stream, char *stf);
The write_string version here is actually a convenience macro that allows writing a simple NULL terminated string without having to explicitly provide the length. The actual number of bytes written on the stream will be returned. Take careful note that if an attempt to write a large amount of data would cause the stream to blocksuch as with a socket streamand the stream is marked non-blocking, the actual amount of data written may be less that what was passed into the function.
int php_stream_putc(php_stream *stream, int c); int php_stream_puts(php_string *stream, char *buf);
Alternatively, php_stream_putc() and php_stream_puts() may be used to write a character or string of characters to the stream respectively. Note that php_stream_puts() differs from php_stream_write_string()which has a nearly identical prototypein that a newline character will automatically be written to the stream following the value in buf.
size_t php_stream_printf(php_stream *stream TSRMLS_DC, const char *format, ...);
Similar to fprintf() in form and function, this API call allows easy writing of compound strings without having to create temporary buffers to construct the data in. The one obvious difference to watch out for is the atypical addition of the TSRMLS_CC macro needed for thread safety.
Seeking, Telling, and Flushing
File-based streams, as well as a few other stream types, are capable of random access. That is, after reading data in one portion of the stream, the file pointer can be sought backwards or forwards within the data to read another section in a nonlinear order.
If your streams-using code expects the underlying stream to support seeking, it should pass the STREAM_MUST_SEEK option during opening. For streams where seekability is available, this will usuallybut not alwayshave no net effect because the stream would have been seekable anyway. For non-seekable streams, such as network I/O or linear access files such as FIFO pipes, this hint allows the calling program a chance to fail more gracefully, before the stream's data has been consumed or acted upon.
After you have a working, seekable stream resource, the following call serves to seek to an arbitrary location:
int php_stream_seek(php_stream *stream, off_t offset, int whence); int php_stream_rewind(php_stream *stream);
offset is a byte count relative to the stream location indicated by whence that can be any of the following three values:
SEEK_SET |
offset is relative to the beginning of the file. The php_stream_rewind() API call is actually a macro that resolves to php_stream_seek(stream, 0, SEEK_SET) indicating zero bytes from the beginning of the file. Passing a negative value for offset when using SEEK_SET is considered an error and will result in undefined behavior. Seeking past the end of the stream is also undefined but usually results in an error or the file being enlarged to satisfy the offset specified. |
SEEK_CUR |
offset is relative to the current position within the file. Calling php_stream_seek(stream, offset, SEEK_CUR) is generally equivalent to php_stream_seek(stream, php_stream_tell() + offset, SEEK_SET). |
SEEK_END |
offset is relative to the current EOF location. offset values should usually be negative to indicate some position prior to EOF; however, positive values might work for certain stream implementations according to the same semantics as described for SEEK_SET. |
int php_stream_rewinddir(php_stream *dirstream);
When seeking on directory streams, only the php_stream_rewinddir() method should be used. Using the underlying php_stream_seek() method will result in undefined behavior. All seek family functions just mentioned return either 0 to indicate success, or -1 to indicate failure.
off_t php_stream_tell(php_stream *stream);
As you saw a moment ago, php_stream_tell() will return the current offset from the beginning of the file in bytes.
int php_stream_flush(php_stream *stream);
Calling the flush() method will force any data held by internal buffers such as stream filters to be output to the final resource. Upon closing a stream resource, the flush() method is called automatically, and most unfiltered stream resources perform no internal buffering that would require flushing. Explicitly calling this method is therefore uncommon and usually not needed.
int php_stream_stat(php_stream *stream, php_stream_statbuf *ssb);
Additional information about a stream instance can be obtained using the php_stream_stat() call, which behaves similarly to the fstat() function. In fact, the php_stream_statbuf structure currently only contains one element: struct statbuf sb; therefore, the php_stream_stat() call can be dropped directly in place of a traditional fstat() operation as in the following example, which translates a posix stat operation into a streams compatible one:
int php_sample4_fd_is_fifo(int fd) { struct statbuf sb; fstat(fd, &sb); return S_ISFIFO(sb.st_mode); } int php_sample4_stream_is_fifo(php_stream *stream) { php_stream_statbuf ssb; php_stream_stat(stream, &ssb); return S_ISFIFO(ssb.sb.st_mode); }
Closing
All stream closing is handled through the php_stream_free() method, which has the following prototype:
int php_stream_free(php_stream *stream, int options);
The permitted values for options in this method call are a bitwise OR combination of PHP_STREAM_FREE_f values, where f is one of the following:
CALL_DTOR |
The stream implementation's destructor method should be called. This provides an opportunity for any resources specific to the stream type to be explicitly freed. |
RELEASE_STREAM |
Free the memory allocated for the php_stream structure. |
PRESERVE_HANDLE |
Instruct the stream's destructor method to not close its underlying descriptor handle. |
RSRC_DTOR |
Used internally by the streams layer to manage the resource list garbage collection. |
PERSISTENT |
When used on a persistent stream, actions will be permanent and not localized to the current request. |
CLOSE |
Combination of CALL_DTOR and RELEASE_STREAM. This is the normal options value for closing a non-persistent stream. |
CLOSE_CASTED |
Combination of CLOSE options plus PRESERVE_HANDLE. |
CLOSE_PERSISTENT |
Combination of CLOSE options plus the PERSISTENT flag. This is the normal options value for closing persistent streams permanently. |
In practice, you'll never need to call the php_stream_free() method directly. Instead, you'll use one of the following two macros when closing your stream:
#define php_stream_close(stream) php_stream_free((stream), PHP_STREAM_FREE_CLOSE) #define php_stream_pclose(stream) php_stream_free((stream), PHP_STREAM_FREE_CLOSE_PERSISTENT)
Exchanging Streams for zvals
Because streams are often mapped to zvals and vice versa, a set of macros exists to make the operations cleaner, simpler, and more uniform:
#define php_stream_to_zval(stream, pzval) ZVAL_RESOURCE((pzval), (stream)->rsrc_id);
Notice here that ZEND_REGISTER_RESOURCE() was not called. This is because when the stream was opened it was automatically registered as a resource, thus taking advantage of the engine's built-in garbage collection and shutdown system. It's important that you use this macro rather than attempting to manually (re)register the stream as a new resource ID; doing so will ultimately result in the stream being closed twice and the engine crashing.
#define php_stream_from_zval(stream, ppzval) ZEND_FETCH_RESOURCE2((stream), php_stream*, (ppzval), -1, "stream", php_file_le_stream(), php_file_le_pstream()) #define php_stream_from_zval_no_verify(stream, ppzval) (stream) = (php_stream*)zend_fetch_resource((ppzval) TSRMLS_CC, -1, "stream", NULL, 2, php_file_le_stream(), php_file_le_pstream())
Fetching the php_stream* back from a passed-in zval* uses a similar macro. As you can see, this macro simply wraps the resource fetching functions that you're already familiar with from Chapter 9, "The Resource Data Type." You'll recall that the ZEND_FETCH_RESOURCE2() macro, which is wrapped in the first php_stream_from_zval() macro, will throw a warning and attempt to return from a function implementation if the resource type does not match. If you'll be fetching a php_stream* from a passed zval* but don't want the automatic error handling, be sure to use php_stream_from_zval_no_verify() and check the resulting value manually instead.