Good Practices: use stream in PHP
Tags: php, stream, goodpractices, streamwrapper
Published: 2019-10-05 12:18:13

---
title: Good Practices: use stream in PHP
published: true
description: Transfer data the right way using stream, the hidden gem of PHP
tags: php, stream, goodpractices, streamWrapper
cover_image: https://thepracticaldev.s3.amazonaws.com/i/6o28cteidf6s9a1loyk6.jpg
---

Transfer data the right way using stream, the hidden gem of PHP 
 
If you travel often you surely have been spending more time than you would like at airports,

I love travelling but all that waiting, then the checking, &nbsp;then waiting again just bother me.

In one of my last trip, for example, I had to wait for a few hours at my destination just to have my luggage back.

Minutes and minutes next to an empty conveyer belt with the hope that the next suitcase spit by the machine would be mine.

That reminded me of a feature that works amazingly well in PHP.

Streams!

**If you have ever worked on files and archives with your PHP scripts you have used streams.&nbsp;**

They are uncommon but not that complicated.

Today you are here to get to know them.  
 &nbsp;
 
## The series

This is the PHP good practices series,&nbsp;

 

In this series, we are exploring what are the best practices a web developer must take care of when creating or managing PHP code.

 

If you missed the previous episodes follow the links below  
 
[Sanitize, validate and escape](http://anastasionico.uk/blog/good-practices-how-to-sanitize-validate-and-escape-in-php)
[Security and managing passwords](http://anastasionico.uk/blog/good-practices-php-security-manage-password)
[Handling error and exceptions](http://anastasionico.uk/blog/good-practices-handling-error-and-exceptions-in-php)
[Dates and Time](http://anastasionico.uk/blog/good-practices-date-time-time-zones-php)
[Streams](http://anastasionico.uk/blog/Good-practices-stream-wrapper-filter-php)

 
## What are streams
 
Streams were released for the first time in PHP 4.3.

But, maybe because of their niche, or just the lack of content online, they are rarely known.

**In PHP 7, Zend certification study guide Andrew Beak describes them as a “conveyer belt of things that come to your one by one”.**

![php_stream _wrapper.jpg](https://ucarecdn.com/2411c354-d12e-49ec-bf46-7ab84028033f/)

The good news is that using PHP you do not need to wait for the things to arrive you can just go and get them.

So, what are streams?

**A stream is a transfer of data between to places.**

The places can be a file, a ZIP archive, &nbsp;a connection even a process through the command line.

PHP streams have functions that help developers manage different resources,

I am sure you have already seen the functions _fopen(), fwrite(), fgets()_ or _file\_get\_contents()_, well streams provides the implementation that works in the background of these functions.  
 
## The Wrappers

I am sure you understand the difference between open a file or a web page, they both have content but they are two completely different types of streamable data, hence they require different protocols.

For instance, we can open a web page by connecting with remote web servers using HTTP, HTTPS or SSL or read a ZIP or TAR archive.

These protocols are called stream wrappers. They provide a unique interface that encapsulates all these differences.

Every stream is formed from a scheme and a target, here is the format:  
 _scheme://target_

Look familiar?

If not just look up at the address bar of your browser.

PHP include a list of wrapper that are built-in in the language.
- **file://** &nbsp; Accessing local filesystem
- **http://** &nbsp; Accessing HTTP(s) URLs
- **ftp://** &nbsp;Accessing FTP(s) URLs
- **php://** &nbsp;Accessing various I/O streams
- **zlib://** &nbsp;Compression Streams
- **data://** &nbsp;Data (RFC 2397)
- **glob://** &nbsp;Find pathnames matching pattern
- **phar://** &nbsp;PHP Archive
- **ssh2://** &nbsp;Secure Shell 2
- **rar://** &nbsp;RAR
- **ogg://** &nbsp;Audio streams
- **expect://** &nbsp;Process Interaction Streams

In our case _php://_ can access the _php://stdin, php://stdout, php://stderr, php://input, php://output, php://fd, php://memory, php://temp_ and _php://filter._

That is all nice so far but you may be wondering what the real work use of these streams.

Let’s make an example.

How do you read the body of a REST API where the user update some details somewhere on the web?

**That a PUT request and there is not $\_PUT[variable’] in PHP;**

The answer is through a stream

 
```php
// the line reads the content 
$input = file_get_contents('php://input');
// then we parse the input into an array of parameters
parse_str($input, $props);
```
 

In a different example we can use the file wrapper and read a file:

 
```php
// file /etc/host is opened are read
$handle = fopen("file:///etc/host", r);
// If the file in not finished output the line from file pointer
while (feof($handle) !== true) {
    echo fgets($handle);
}
// Eventually the file get closed for security reasons
fclose($handle);
```
 

[If the snippets above look too strange you may want to stop for a moment and hand over the article the basics of PHP](http://anastasionico.uk/blog/php-basics)

 

A common false believe that is that PHP functions like _fopen(), fgets(), fclose()_ are for filesystem only,

 

These functions work perfectly fine with all wrappers that support them.

 

We can use _fopen()_ on files, ZIP archive and Dropbox (with the Dropbox wrapper) and Amazon S3 (with its own wrapper).  
 &nbsp;

 
### &nbsp;
 
### Create your custom wrapper
If your script needs special requirements PHP allows to create a custom wrapper by yourself.

In the snippet below we are going to create a stream wrapper that calls a callback function for reads:

 
```php
/ The following class is the wrapper, it has several parameters and the getContext() stream_open(), stream_read() and stream_eof() methods
class CallbackUrl
{
    const WRAPPER_NAME = 'callback';

    public $context;
    private $_cb;
    private $_eof = false;

    private static $_isRegistered = false;

    public static function getContext($cb)
    {
        if (!self::$_isRegistered) {
            stream_wrapper_register(self::WRAPPER_NAME, get_class());
            self::$_isRegistered = true;
        }
        if (!is_callable($cb)) return false;
        return stream_context_create(array(self::WRAPPER_NAME => array('cb' => $cb)));
    }

    public function stream_open($path, $mode, $options, &$opened_path)
    {
        if (!preg_match('/^r[bt]?$/', $mode) || !$this->context) return false;
        $opt = stream_context_get_options($this->context);
        if (!is_array($opt[self::WRAPPER_NAME]) ||
            !isset($opt[self::WRAPPER_NAME]['cb']) ||
            !is_callable($opt[self::WRAPPER_NAME]['cb'])) return false;
        $this->_cb = $opt[self::WRAPPER_NAME]['cb'];
        return true;
    }

    public function stream_read($count)
    {
        if ($this->_eof || !$count) return '';
        if (($string = call_user_func($this->_cb, $count)) == '') $this->_eof = true;
        return $string;
    }

    public function stream_eof()
    {
        return $this->_eof;
    }
}

// Here is the class that will contain the text object that need to be instanciated
class Text {
    private $_string;
    public function __construct($string)
    {
        $this->$_string = $string;
    }
    public function read($count) {
        return fread($this->$_string, $count);
    }
}


// We create a new Text object and open it using our brand-new wrapper CallbackUrl eventually will loop until the file ends
$text = new Text(fopen('/etc/services', 'r'));
$handle = fopen('callback://', 'r', false, CallbackUrl::getContext(array($text, 'read')));
while(($row = fread($handle, 128)) != '') {
    print $row;
}
```
 

[You can find few other very well explained examples in Matt Allan's post a&nbsp;full stack developer based in Asheville, NC](https://mattallan.me/posts/getting-started-with-php-streams/)
 
## &nbsp;
 
## Stream Filters

According to a lot of developers that use stream often, the real power of this PHP feature is hidden behind the filters.

Stream filters enable to filter and transform data that is currently transiting.

**Imagine you can transform and output all the strings in a file in uppercase, while you are reading the file.**

There are 2 ways to use stream filters

- Using stream\_filter\_append($stream, $filtername, $read\_write)
- Using php://filter stream wrapper
 
### &nbsp;
 
### stream\_filter\_append()
Let’s have a look at an example and I’ll comment it after

 
```php
$handle = fopen('file.txt', 'rb');
stream_filter_append($handle, 'string.toupper');
while(feof($handle) !== true) {
    echo fgets($handle);
}
fclose($handle);
```
In the first line,

we open the _file.txt_ which is supposed to contain string in lowercase that we want to transform in uppercase.

We then, attach (append) the handle of the file to the stream filter _string.toupper_

Now PHP knows that when we loop through the haldle the characters need to be shown as uppercase.

Just like magic!  
 
### php://filter
Sometimes you are going to need to use functions such as _file()_ or _fpassthru()_ that do not give you the chance to attach filters after the functions have been called.


**Which means that you cannot append a stream filter.**

In this case, you use _php://filter_ and use it straight when you open the file.

Snippet is following  


 
```php
$handle = fopen('php://filter/read=string.toupper/resource=file.txt', 'rb');
while(feof($handle) !== true) {
    echo fgets($handle); 
}
fclose($handle);
```
 

Again we attached the filter _string.toupper_ to the _text.txt_ file on reading but this time, we did it on the opening command;

Be aware of the format we are using here: _filter/read= __/resource=__ ://_  

 
### Create a custom stream filters

Stream filters are a powerful tool, however, the built-in filter provided by PHP is rather limited, to offset this limitation PHP lets us create custom stream filter,

**Actually the custom filters are the primary reason we use filters.**

Custom stream filters are just classes that extend the behaviour of _php\_user\_filter()_

That’s all.

This class must have 3 methods implemented.

The first method and the one we are going to look just below is the _filter()_, then we have the _onCreate_ and the _onClose_.

Before dive in and create a stream on our own clarification.

PHP streams divide data into sections of several bytes each, those division are commonly called buckets.

Each stream filter receives and manages one or more of these buckets at a time.

Let’s create a custom stream filter together.

The process requires 3 steps
 
1. Create a filter that implements _php\_user\_filter()_
2. Register this new filter
3. Use the brand new filter in an actual script
 
#### &nbsp;
 
#### Create a filter&nbsp;

In these examples, we are going to create a filter that edits dirty or spamming words from text files.

As said before we need to create a class that needs to implement _php\_user\_filter_ than implement the _filter()_ method.  
 
```php
class DirtyWordsFilter extends php_user_filter
{
   public function filter($in, $out, &$consumed, $closing)
   {
       $words = ['grime', 'dirt', 'grease'];
       $wordData = [];
       
       foreach ($words as $word) {
          $replacement = array_fill(0, mb_strlen($word), '*');
          $wordData[$word] = implode('', $replacement);
       }
      
       $bad = array_keys($wordData);
       $good = array_values($wordData);
      
       // Iterate each bucket
       while ($bucket = stream_bucket_make_writeable($in)) {
          // Censor dirty words
          $bucket->data = str_replace($bad, $good, $bucket->data);
         
          // increment total data consumed
          $consumed += $bucket->datalen;
         
          // send bucket to downstream brigade
          stream_bucket_append($out, $bucket);
     }

    // Return the code that indicates that the userspace filter returned buckets in $out
     return PSFS_PASS_ON;
   }
}
```
 

Now that the filter is ready we need to register it, that quite simple.

 

We just need to use the function _stream\_filter\_register()_ and pass the filter name that identifies our new filter and the name of the class.  
 &nbsp;

 
```php
stream_filter_register('dirty_words_filter', 'DirtyWordsFilter');
```
 

[Here is the page from the official PHP manual](https://www.php.net/manual/en/function.stream-filter-register.php)

 

We are ready to use our filter as we did in the above examples:

 
```php
$handle = fopen('file.txt', 'rb');
stream_filter_append($handle, 'dirty_words_filter');
while(feof($handle) !== true) {
    echo fgets($handle);
}
fclose($handle);
```
 

That’s it, we are safe and not going to have any dirty words anymore.

 
## Stream Contexts
The last section about this topic is about contexts,

**Consider stream contexts like a wrapper for a set of options that can modify the behaviour of a stream.**

To create a stream context you’ll need to use the _stream\_context\_create()_ function.

It takes two parameters, both of them are optional associative arrays.

Let’s use a stream context to something strange like sending an HTTP POST request using _file\_get\_contents()_  
 &nbsp;

 
```php
$request = '{"username":"nico"}';
$context = stream_context_create([
  'http' => [
     'method' => 'POST',
     'header' => "Content-Type: application/json;charset=utf-8;\r\n" . "Content-Length: " . mb_strlen($requestBody),
     'content' => $requestBody
  ]
]);
$response = file_get_contents('https://mywebsite.com/users', false, $content);
```
 

The stream context is an associative array in which the top key is the stream wrapper name.

[Head to the manual for more info about stream context](https://www.php.net/manual/en/function.stream-context-create.php)


---
If you discovered something new, learning more is as easy as tapping into the image below

[![subscribe-Medium.jpg](https://ucarecdn.com/ca9ee240-37d6-4968-b30c-d60809f84c92/)](http://eepurl.com/dIZqjf)
---



## Conclusion
 
Sometimes travelling can be stressful and you may need to get to work in order to relax a bit.

Now if you need to use files in your script or manage archives you know,

PHP streams help you in that.

There aren’t a lot of occasions in which you can use them and, actually, their usability is quite hidden to most web developers.

But they are really easy to use and in my opinion, this content can help you discover this little gem of the PHP language.