Skip to content
This repository


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: PHP-5.3.1-RC4
Fetching contributors…

Cannot retrieve contributors at this time

file 379 lines (289 sloc) 15.373 kb
An Overview of the PHP Streams abstraction

WARNING: some prototypes in this file are out of date.
The information contained here is being integrated into
the PHP manual - stay tuned...

Please send comments to: Wez Furlong <>

Why Streams?
special case sockets and files every time you need to work with a "user-level"
PHP file pointer.
based data sources.

Using Streams
FILE* parameter.

The main functions are:

PHPAPI size_t php_stream_printf(php_stream * stream TSRMLS_DC,
        const char * fmt, ...);
PHPAPI int php_stream_eof(php_stream * stream);
PHPAPI int php_stream_getc(php_stream * stream);
PHPAPI int php_stream_close(php_stream * stream);
PHPAPI int php_stream_flush(php_stream * stream);
PHPAPI off_t php_stream_tell(php_stream * stream);
PHPAPI int php_stream_lock(php_stream * stream, int mode);

names: fread, fwrite, fprintf, feof, fgetc, fgets, fclose, fflush, fseek, ftell, flock.

Opening Streams
In most cases, you should use this API:

PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode,
    int options, char **opened_path TSRMLS_DC);

    path is the file or resource to open.
    mode is the stdio compatible mode eg: "wb", "rb" etc.
    options is a combination of the following values:
        IGNORE_PATH (default) - don't use include path to search for the file
        USE_PATH - use include path to search for the file
        IGNORE_URL - do not use plugin wrappers
        REPORT_ERRORS - show errors in a standard format if something
                          goes wrong.
        STREAM_MUST_SEEK - If you really need to be able to seek the stream
                           and don't need to be able to write to the original
                           file/URL, use this option to arrange for the stream
                           to be copied (if needed) into a stream that can
                           be seek()ed.
    opened_path is used to return the path of the actual file opened,
    but if you used STREAM_MUST_SEEK, may not be valid. You are
    responsible for efree()ing opened_path. opened_path may be (and usually
    is) NULL.

If you need to open a specific stream, or convert standard resources into
streams there are a range of functions to do this defined in php_streams.h.
A brief list of the most commonly used functions:

PHPAPI php_stream *php_stream_fopen_from_file(FILE *file, const char *mode);
    Convert a FILE * into a stream.

PHPAPI php_stream *php_stream_fopen_tmpfile(void);
    Open a FILE * with tmpfile() and convert into a stream.

PHPAPI php_stream *php_stream_fopen_temporary_file(const char *dir,
    const char *pfx, char **opened_path TSRMLS_DC);
    Generate a temporary file name and open it.

There are some network enabled relatives in php_network.h:

PHPAPI php_stream *php_stream_sock_open_from_socket(int socket, int persistent);
    Convert a socket into a stream.

PHPAPI php_stream *php_stream_sock_open_host(const char *host, unsigned short port,
int socktype, int timeout, int persistent);
    Open a connection to a host and return a stream.

PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent,
    struct timeval *timeout);
    Open a UNIX domain socket.

Stream Utilities

If you need to copy some data from one stream to another, you will be please
to know that the streams API provides a standard way to do this:

PHPAPI size_t php_stream_copy_to_stream(php_stream *src,
    php_stream *dest, size_t maxlen);

If you want to copy all remaining data from the src stream, pass
PHP_STREAM_COPY_ALL as the maxlen parameter, otherwise maxlen indicates the
number of bytes to copy.
This function will try to use mmap where available to make the copying more

If you want to read the contents of a stream into an allocated memory buffer,
you should use:

PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf,
    size_t maxlen, int persistent);

This function will set buf to the address of the buffer that it allocated,
which will be maxlen bytes in length, or will be the entire length of the
data remaining on the stream if you set maxlen to PHP_STREAM_COPY_ALL.
The buffer is allocated using pemalloc(); you need to call pefree() to
release the memory when you are done.
As with copy_to_stream, this function will try use mmap where it can.

If you have an existing stream and need to be able to seek() it, you
can use this function to copy the contents into a new stream that can
be seek()ed:

PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream);

It returns one of the following values:
#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */
#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */
#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */
#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */

make_seekable will always set newstream to be the stream that is valid
if the function succeeds.
When you have finished, remember to close the stream.

NOTE: If you only need to seek forward, there is no need to call this
function, as the php_stream_seek can emulate forward seeking when the
whence parameter is SEEK_CUR.

NOTE: Writing to the stream may not affect the original source, so it
only makes sense to use this for read-only use.

NOTE: If the origstream is network based, this function will block
until the whole contents have been downloaded.

NOTE: Never call this function with an origstream that is referenced
as a resource! It will close the origstream on success, and this
can lead to a crash when the resource is later used/released.

NOTE: If you are opening a stream and need it to be seekable, use the
STREAM_MUST_SEEK option to php_stream_open_wrapper();

PHPAPI int php_stream_supports_lock(php_stream * stream);

This function will return either 1 (success) or 0 (failure) indicating whether or
not a lock can be set on this stream. Typically you can only set locks on stdio streams.

Casting Streams

FILE * fp;
php_stream * stream; /* already opened */

if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void*)&fp, REPORT_ERRORS) == FAILURE) {

The prototype is:


appropriate error message of type E_WARNING if the cast fails.

castas can be one of the following values:
PHP_STREAM_AS_FD - a generic file descriptor
PHP_STREAM_AS_SOCKETD - a socket descriptor


streams, data base streams etc. etc.

In situations where this is not desirable, you should query the stream
to see if it naturally supports FILE *. You can use this code snippet
for this purpose:

    if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
        /* can safely cast to FILE* with no adverse side effects */

You can use:

PHPAPI int php_stream_can_cast(php_stream * stream, int castas)

to check if a stream is a socket you might use:

    /* it can be a socket */

Please note the difference between php_stream_is and php_stream_can_cast;
stream_is tells you if the stream is a particular type of stream, whereas
can_cast tells you if the stream can be forced into the form you request.
The former doesn't change anything, while the later *might* change some
state in the stream.

Stream Internals

There are two main structures associated with a stream - the php_stream
underlying implementation.

The php_streams ops struct consists of pointers to methods that implement
method is intended to be used for streams if there is an underlying method
that can efficiently behave as fgets. The ops struct also contains a label
for the implementation that will be used when printing error messages - the
stdio implementation has a label of "STDIO" for example.

associates it with a php_stream using php_stream_alloc.


    FILE * fp = fopen(filename, mode);
    php_stream * ret;
    if (fp) {
        if (ret)
            return ret;

    return NULL;

php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
FILE* based streams.


The prototype for php_stream_alloc is this:

        size_t bufsize, int persistent, const char * mode)

ops is a pointer to the implementation,
abstract holds implementation specific data that is relevant to this instance
of the stream,
level will be disabled (recommended for underlying sources that implement
their own buffering - such a FILE*),
of a request (it uses pemalloc),
attempting to write (this may change).


Writing your own stream implementation

RULE #1: when writing your own streams: make sure you have configured PHP with
I've taken some great pains to hook into the Zend memory manager to help track
down allocation problems. It will also help you spot incorrect use of the
STREAMS_DC, STREAMS_CC and the semi-private STREAMS_REL_CC macros for function

RULE #2: Please use the stdio stream as a reference; it will help you
understand the semantics of the stream operations, and it will always
be more up to date than these docs :-)

MySQL, you might want to store the connection and rowset handles.


For structured state you might have this:

struct my_state {
    MYSQL conn;
    MYSQL_RES * result;


 * hold the results */


/* now allocate the stream itself */

/* now stream->abstract == state */


For example, for reading from this weird MySQL stream:


         * end of file */
            return EOF;
            return 0;


php_stream_ops my_ops = {
    php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
    php_mysqlop_flush, NULL, NULL, NULL,
    "Strange MySQL example"

Thats it!

Take a look at the STDIO implementation in streams.c for more information
about how these operations work.
allocated in persistent mode or not.

Something went wrong with that request. Please try again.