Understanding Streams in PHP

Share this article

Streams are resources provided by PHP that we often use transparently, but which can also be very powerful tools. By learning how to harness their power, we can take our applications to a higher level. The PHP manual has a great description of streams:

Streams were introduced with PHP 4.3.0 as a way of generalizing file, network, data compression, and other operations which share a common set of functions and uses. In its simplest definition, a stream is a resource object which exhibits streamable behavior. That is, it can be read from or written to in a linear fashion, and may be able to fseek() to an arbitrary locations within the stream.
Every stream has a implementation wrapper which has the additional code necessary to handle the specific protocol or encoding. PHP provides some built-in wrappers and we can easily create and register custom ones. We can even modify or enhance the behavior of wrappers using contexts and filters.

Stream Basics

A stream is referenced as <scheme>://<target>. <scheme> is the name of the wrapper, and <target> will vary depending on the wrapper’s syntax. The default wrapper is file:// which means we use a stream every time we access the filesystem. We can either write readfile('/path/to/somefile.txt') for example or readfile('file:///path/to/somefile.txt') and obtain the same result. If we instead use readfile('http://google.com/') then we’re telling PHP to use the HTTP stream wrapper. As I said before, PHP provides some built-in wrappers, protocols, and filters. To know which wrappers are installed on our machine we can use:
<?php
print_r(stream_get_transports());
print_r(stream_get_wrappers());
print_r(stream_get_filters());
My installation outputs the following:
Array
(
    [0] => tcp
    [1] => udp
    [2] => unix
    [3] => udg
    [4] => ssl
    [5] => sslv3
    [6] => sslv2
    [7] => tls
)
Array
(
    [0] => https
    [1] => ftps
    [2] => compress.zlib
    [3] => compress.bzip2
    [4] => php
    [5] => file
    [6] => glob
    [7] => data
    [8] => http
    [9] => ftp
    [10] => zip
    [11] => phar
)
Array
(
    [0] => zlib.*
    [1] => bzip2.*
    [2] => convert.iconv.*
    [3] => string.rot13
    [4] => string.toupper
    [5] => string.tolower
    [6] => string.strip_tags
    [7] => convert.*
    [8] => consumed
    [9] => dechunk
    [10] => mcrypt.*
    [11] => mdecrypt.*
)
A nice set, don’t you think? In addition we can write or use third-party streams for Amazon S3, MS Excel, Google Storage, Dropbox
and even Twitter.

The php:// Wrapper

PHP has its own wrapper to access the language’s I/O streams. There are the basic php://stdin, php://stdout, and php://stderr wrappers that map the default I/O resources, and we have php://input that is a read-only stream with the raw body of a POST request. This is handy when we’re dealing with remote services that put data payloads inside the body of a POST request. Let’s do a quick test using cURL:
curl -d "Hello World" -d "foo=bar&name=John" http://localhost/dev/streams/php_input.php
The result of a print_r($_POST) in the responding PHP script would be:
Array
(
    [foo] => bar
    [name] => John
)
Notice that the first data pack isn’t accessible from the $_POST array. But if we use readfile('php://input') instead we get:
Hello World&foo=bar&name=John
PHP 5.1 introduced the php://memory and php://temp stream wrappers which are used to read and write temporary data. As the names imply, the data is stored respectively in memory or in a temporary file managed by the underlying system. There’s also php://filter, a meta-wrapper designed to apply filters when opening a stream with function like readfile()
or file_get_contents()/stream_get_contents().
<?php
// Write encoded data
file_put_contents("php://filter/write=string.rot13/resource=file:///path/to/somefile.txt","Hello World");

// Read data and encode/decode
readfile("php://filter/read=string.toupper|string.rot13/resource=http://www.google.com");
The first example uses a filter to encode data written to disk while the second applies two cascading filters reading from a remote URL. The outcome can be from very basic to very powerful in our applications.

Stream Contexts

A context is a stream-specific set of parameters or options which can modify and enhance the behavior of our wrappers. A common use context is modifying the HTTP wrapper. This lets us avoid the use of cURL for simple network operations.
<?php
$opts = array(
'http'=>array(
'method'=>"POST",
'header'=> "Auth: SecretAuthTokenrn" .
"Content-type: application/x-www-form-urlencodedrn" .
"Content-length: " . strlen("Hello World"),
'content' => 'Hello World'
)
);
$default = stream_context_get_default($opts);
readfile('http://localhost/dev/streams/php_input.php');
First we define our options array, an array of arrays with the format $array['wrapper']['option_name'] (the available context options vary depending on the specific wrapper). Then we call stream_context_get_default() which returns the default context and accepts an optional array of options to apply. The readfile() statement uses these settings to fetch the content. In the example, the content is sent inside the body of the request so the remote script will use php://input to read it. We can access the headers using apache_request_headers() and obtain:
Array
(
    [Host] => localhost
    [Auth] => SecretAuthToken
    [Content-type] => application/x-www-form-urlencoded
    [Content-length] => 11
)
We’ve modified the default context options, but we can create alternative contexts to be used separately as well.
<?php
$alternative = stream_context_create($other_opts);
readfile('http://localhost/dev/streams/php_input.php', false, $alternative);

Conclusion

How can we harness the power of streams in the real world? And where can we go from here? As we’ve seen, streams share some or all of the filesystem related functions, so the first use that comes to my mind is a series of virtual filesystem wrappers to use with PaaS providers like Heroku or AppFog that don’t have a real filesystem. With little or no effort we can port our apps from standard hosting services to these cloud services and enjoy the benefits. Also – and I’ll show in a follow-up article – we can build custom wrappers and filters for our applications that implementing custom file formats and encoding.

Frequently Asked Questions (FAQs) about Understanding Streams in PHP

What are the different types of streams in PHP?

In PHP, there are three main types of streams: File streams, Data streams, and Network streams. File streams are used for file-related operations, such as reading from or writing to a file. Data streams are used for manipulating data in memory, such as serialization and compression. Network streams are used for network-related operations, such as sending or receiving data over a network.

How can I use streams for file manipulation in PHP?

In PHP, you can use file streams to read from or write to a file. To open a file for reading or writing, you can use the fopen() function, which returns a file pointer resource on success, or FALSE on error. Once the file is open, you can use functions like fread() or fwrite() to read from or write to the file. Don’t forget to close the file using fclose() when you’re done.

How can I use streams for data manipulation in PHP?

In PHP, you can use data streams to manipulate data in memory. For example, you can use the serialize() function to convert a PHP value into a storable representation, or the unserialize() function to convert a stored representation back into a PHP value. You can also use functions like gzcompress() or gzuncompress() to compress or decompress data.

How can I use streams for network operations in PHP?

In PHP, you can use network streams to send or receive data over a network. For example, you can use the fsockopen() function to open a network connection, or the stream_socket_client() function to create a network socket client. Once the connection is open, you can use functions like fread() or fwrite() to send or receive data.

What are the benefits of using streams in PHP?

Using streams in PHP can provide several benefits. First, streams can handle large amounts of data efficiently, as they only need to load a small portion of the data into memory at a time. Second, streams can handle different types of data sources uniformly, whether they are files, network connections, or in-memory data. Third, streams can apply filters to transform the data as it is being read or written.

How can I apply filters to a stream in PHP?

In PHP, you can apply filters to a stream using the stream_filter_append() function. This function attaches a filter to a stream, which will then be applied whenever data is read from or written to the stream. You can use predefined filters, such as string.toupper to convert all data to uppercase, or you can define your own filters using the stream_filter_register() function.

How can I handle errors when using streams in PHP?

In PHP, you can handle errors when using streams by checking the return values of the stream functions. Most stream functions return FALSE on error, and you can use the error_get_last() function to get information about the last error that occurred. You can also use the set_error_handler() function to set a custom error handler.

How can I read from a stream in PHP?

In PHP, you can read from a stream using the fread() function. This function reads up to a specified number of bytes from a stream and returns the read data. You can also use the fgets() function to read a line from a stream, or the stream_get_contents() function to read all remaining data from a stream.

How can I write to a stream in PHP?

In PHP, you can write to a stream using the fwrite() function. This function writes a string to a stream and returns the number of bytes written. You can also use the fputs() function, which is an alias of fwrite(), or the stream_put_contents() function to write data to a stream.

How can I close a stream in PHP?

In PHP, you can close a stream using the fclose() function. This function closes a stream and releases the resources associated with it. It’s important to always close a stream when you’re done with it to prevent resource leaks.

Vito TardiaVito Tardia
View Author

Vito Tardia (a.k.a. Ragman), is a web designer and full stack developer with 20+ years experience. He builds websites and applications in London, UK. Vito is also a skilled guitarist and music composer and enjoys writing music and jamming with local (hard) rock bands. In 2019 he started the BlueMelt instrumental guitar rock project.

Intermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week