by

Introduction to PHP streams

Streams is a feature of PHP that was introduced in PHP 4.3 to unify the methods for working on files, sockets, and other similar resources. PHP 4.3 came out a long time ago, but many PHP developers never learned how to properly use streams in PHP, much to my dismay. Many use cURL for accessing HTTP resources, but I’m not a huge fan of cURL, because it has an awful interface in PHP and it presents yet another dependency. While the HTTP stream handler in PHP isn’t perfect, it works very well for most situations.

To begin, let’s consider what examples of streams could be:

  • A file on a hard drive
  • A HTTP connection to a website
  • A UDP connection to a server
  • A ZIP file
  • A SSH tunnel
  • A Vorbis/OGG file

What are some common operations on all of those? Primarily, they share the ability to be read from and written to. The power of PHP’s streams is that you can access all of them using the same set of functions, and if there’s something you wish to “stream-ify,” you can write your own “stream wrapper.” In addition to reading and writing, the streams framework in PHP also allows for other operations, such as renaming and deleting.

To use streams, you will have to call a PHP function that utilizes them. Stream-supporting functions that you are already familiar with include fopen(), file_get_contents(), and file(). In fact, you have already been using file streams all this time, completely transparently.

However, if you want to work with a different type of stream, you must specify it by prepending your URI with wrapper:// to indicate the stream wrapper/protocol that you wish to use. For example, you may also be familiar with this, which access a HTTP stream (a webpage):

echo file_get_contents("http://www.example.com");

After wrapper:// is something that identifies what you want to open in particular. In this case, because http:// refers to websites, you enter the rest of the URL. However, for a ZIP file, it might be zip:///path/to/file.zip. It could be something really exotic such as unknownwrapper://access=folder4;name=Frank. Here are some more examples:

  • file:///path/to/file.ext
  • ftp://user:password@example.com/pub/file.txt
  • data://text/plain;base64,SSBsb3ZlIFBIUAo=
  • ogg://http://www.example.com/path/to/soundstream.ogg
  • ssh2.tunnel://user:pass@example.com:22/192.168.0.1:14

Before we continue, glance through the list of supported protocols and wrappers. Be aware that support for some wrappers depends on your configuration.

Stream Contexts

There comes a time when you need to specify a few more options than you can fit into the URL syntax. In our HTTP example above, we could provide the URL, but there was no mechanism to provide a list of headers to send. What about POST and POST variables? Stream contexts solve that problem by allowing additional options to be specified. With many of the stream-capable functions, there is a parameter to pass in a context. Let’s take a look at file_get_contents:

string file_get_contents ( string $filename [, int $flags = 0 [, resource $context [, int $offset = -1 [, int $maxlen = -1 ]]]] )

There’s a $context parameter that you may not have noticed previously.

Contexts are created with stream_context_create, which takes in an array and returns a context resource.

$opts = array(
  'http' => array(
    'method' => "GET",
    'header' => "Accept-language: en\r\n" .
                "Cookie: foo=bar\r\n"
  )
);

$context = stream_context_create($opts);

Using that with file_get_contents…

echo file_get_contents("http://www.example.com/", 0, $context);

To find out what the supported options are, check out the list of supported context options and parameters.

Stream Metadata

Now that we were able to pass in extra data, how about getting extra data out? If you tried the original piece of example code, it should have printed out the contents of example.com, but it did not show you the headers received. Those headers are considered a part of the metadata, and the streams framework has a function for getting that information. Before we get too ahead of ourselves, recall that file_get_contents() returns a string containing the response. We can’t “work on” a string, so we will have to be ditching our beloved friend, file_get_contents().

And say helllllo to fopen(). That returns a file pointer, and in the case of streams, a streams resource. Let’s convert that last example to use fopen():

$opts = array(
  'http' => array(
    'method' => "GET",
    'header' => "Accept-language: en\r\n" .
                "Cookie: foo=bar\r\n"
  )
);

$context = stream_context_create($opts);

$fp = fopen("http://www.example.com/", "rb", false, $context);
echo stream_get_contents($fp);

Note the introduction of stream_get_contents(). We can’t directly print $fp, since it’s a streams resource. stream_get_contents() gets the data.

To get the metadata of that request, we use stream_get_meta_data():

print_r(stream_get_meta_data($fp));

Which prints:

[cc]Array
(
[wrapper_data] => Array
(
[0] => HTTP/1.1 200 OK
[1] => Server: Apache/2.2.3 (CentOS)
[2] => Last-Modified: Tue, 15 Nov 2005 13:24:10 GMT
[3] => ETag: “24ec5-1b6-4059a80bfd280”
[4] => Accept-Ranges: bytes
[5] => Content-Type: text/html; charset=UTF-8
[6] => Connection: close
[7] => Date: Sun, 04 Apr 2010 02:39:50 GMT
[8] => Age: 1774
[9] => Content-Length: 438
)

[wrapper_type] => http
[stream_type] => tcp_socket
[mode] => rb
[unread_bytes] => 0
[seekable] =>
[uri] => http://www.example.com/
[timed_out] =>
[blocked] => 1
[eof] => 1
)

For a description of the fields, check out the documentation for stream_get_meta_data().

Stream Filters

Filters allow you to transform the data while reading or writing it. The stream_filter_append() and stream_filter_prepend() functions attach a filter to a stream (the order that filters are applied in matters). Let’s look at an example of its use to ROT13 data that passes through a stream:

stream_filter_append($fp, "string.rot13", STREAM_FILTER_READ);

The last parameter specifies whether the filter should act on data that is being written, being read, or both.

Here’s an example (run it for fun results!):

$opts = array(
  'http' => array(
    'method' => "GET",
    'header' => "Accept-language: en\r\n" .
                "Cookie: foo=bar\r\n"
  )
);

$context = stream_context_create($opts);

$fp = fopen("http://www.example.com/", "rb", false, $context);
stream_filter_append($fp, "string.rot13", STREAM_FILTER_READ);
echo stream_get_contents($fp);

There are built-in filters to do simple transformations, encoding and decoding, compressing, and even encryption. See the manual for a list of available filters.

Socket Transports

There is a special class of streams that are socket transports, that differ a little bit from the other “regular” stream wrappers. Examples include:

  • TCP
  • UDP
  • SSL
  • TLS
  • Unix domain sockets

They all deal with networking, you have to use stream-based socket functions such as fsockopen() or stream_get_transports() to use those transports (file() and file_get_contents() won’t do). You should be able to figure out how to use these socket transports by reading the manual, although you naturally need to be familiar with Berkeley sockets (which are not a streams concept).

There is a list of supported stream transports.

Creating Wrappers

It’s possible to create your own wrappers. I won’t go into detail about it, but the gist is that you create a class with a certain set of methods. See the manual for the methods that you need to implement.

Creating Filters

Custom filters can also be created. The manual is very detailed on this, so check it out.

POST With Streams

Here’s an example of a POST request using streams:

$postdata = array(
  'var1' => 'value1',
  'var2' => 'value2',
);

$opts = array('http' =>
  array(
    'method'  => 'POST',
    'header'  => 'Content-type: application/x-www-form-urlencoded',
    'content' => http_build_query($postdata, '', '&'),
    'timeout' => 5,
  )
);

$context = stream_context_create($opts);

echo file_get_contents("http://example.com", 0, $context);
  • http://www.davidtan.org David Tan

    thanks for the examples, this should be part of the php manual!

  • http://briant.me Brian Teachman

    Thanks fer[sic] the rundown.

  • http://www.collegehumor.com/user/6480744 Ydqaroxh

    I’ll text you later ls land nude lolitas 660794

  • Lucas Weeks

    Thanks a lot.

  • arun

    Thank you very much for the wonderful tutorial.You are right there are very few tutorials for streams online.All php books just skip this topic or just gives a brief overview.Well done.

  • Fabrizio Sabato

    Nice Tip mate.
    It’s the first time I can read something understandable about this topic.

  • Pepe Yáñez

    I’ve never seen this type of php. does’t look like php. I mean I didn’t undertand, can some one tell what was that. I am an advance beginner