Share
Share on facebook
Share on twitter
Share on linkedin

PHP Streams and Wrappers

If you use PHP you have certainly used its streams and wrappers even if you haven’t noticed since they are neatly abstracted away from first sight.

Introduction to streams

In computer science, a stream is a sequence of data elements made available over time. Streams can potentially contain an infinite amount of data and are formally considered codata. Also due to various hardware, software, and physics limitations, not all data can be transferred instantly and will, instead, need to be sent in pieces. This makes streams incompatible with normal data functions. Instead, separate functions that operate on streams are needed. When they produce other streams as a result, they are called filters and can be connected to streams or even to each other in pipelines.

available over time. Streams can potentially contain an infinite amount of data and are formally considered codata. Also due to various hardware, software, and physics limitations, not all data can be transferred instantly and will, instead, need to be sent in pieces. This makes streams incompatible with normal data functions. Instead, separate functions that operate on streams are needed. When they produce other streams as a result, they are called filters and can be connected to streams or even to each other in pipelines.

Streams in PHP

Streams are directly used in lower-level programming languages like C while being abstracted away in higher-level languages like PHP. There, streams are referenced by an URL-style string consisting of a scheme and a target delimited with “://”. The scheme specifies the stream wrapper to be used for reading, while the target contains instructions for the wrapper to be able to access the stream.

Guaranteed Software services include PHP development, support in legacy 5.x code bases, as well as modern 7.x and 8.x endeavors are included.

Supported Protocols and Wrappers

PHP by default supports the following stream types:

  • file:// — Accessing local filesystem
  • http:// — Accessing HTTP(s) URLs
  • ftp:// — Accessing FTP(s) URLs
  • php:// — Accessing various I/O streams
  • zlib:// — Compression Streams
  • data:// — Data manipulation defined in RFC 2397
  • glob:// — Find pathnames matching pattern
  • phar:// — PHP Archive manipulation
  • ssh2:// — Secure Shell 2 connections
  • rar:// — RAR archive manipulation
  • ogg:// — Compressed audio files manipulation

Custom Wrappers

In addition to the built-in PHP stream wrappers, it is possible to register custom ones using the stream_wrapper_register() function. Custom PHP stream wrapper class should implement all methods specified in the stream wrapper prototype, but depending on future usage, many of the methods can remain unimplemented as they are never executed.

<?php

class constWrapper
{
    /**
     * @var int Length of data to be returned from stream
     */
    protected $_length;

    /**
     * @var int Current position of the stream
     */
    protected $_index = 0;

    /**
     * @var string Stream content
     */
    private $const;

    function stream_open($path, $mode, $options, &$opened_path)
    {
        $this->const = explode('://', $path)[1];
        return true;
    }

    /**
     * Reads $count number of bytes from the stream
     *
     * @param int $count of data bytes from the current position
     *                   to be returned
     *
     * @return string up to $count number of bytes from $const
     *                if available
     */
    function stream_read($count)
    {
        if (is_null($this->_length)) {
            $this->_length = strlen($this->const);
        }
        $length = min($count, $this->_length - $this->_index);
        $data = substr($this->const, $this->_index);
        $this->_index = $this->_index + $length;
        return $data;
    }

    /**
     * Tests for end-of-file on a file pointer
     * Instead of file we check for end of data stored in $const
     *
     * @return bool true if all data from $const was returned
     *              otherwise false
     */
    public function stream_eof()
    {
        return $this->_index >= strlen($this->const);
    }

    /**
     * Retrieve information about a file resource.
     *
     * @see stat
     *
     * @return array
     */
    public function stream_stat()
    {
        return [];
    }
}

stream_register_wrapper('const', constWrapper::class);
echo file_get_contents('const://this is a constant');
//this is a constant

Along with custom PHP stream wrappers, built-in ones can also be overridden to use custom implementation instead. This is achieved by first calling stream_wrapper_unregister() and then registering new wrapper for the same protocol. If you need the original PHP stream wrapper back, you can use stream_wrapper_restore() to restore it.

<?php

class streamWrapper
{
    /**
     * @var bool whether the stream is read
     */
    private $read = false;

    /**
     * This method is called immediately after the wrapper is
     * initialized (f.e. by fopen() and file_get_contents())
     *
     * @param string $path        URL that was passed to the
     *                            original function
     * @param string $mode        used to open the file, as
     *                            detailed for fopen()
     * @param int    $options     additional flags set by the
     *                            streams API
     * @param string $opened_path full path of the file/resource
     *                            that was actually opened
     *
     * @return bool always true because we are stubbing any
     *              stream
     */
    function stream_open($path, $mode, $options, &$opened_path)
    {
        return true;
    }

    /**
     * @param int $count of data bytes from the current position
     *                   to be returned
     *
     * @return string up to $count number of bytes from $content
     *                if available
     */
    function stream_read($count)
    {
        if (!$this->read) {
            $this->read = true;
            return '127.0.0.1';
        }
        return '';
    }

    /**
     * Tests for end-of-file on a file pointer
     * Instead of file we check {@see \streamWrapper::$read}
     *
     * @return bool
     */
    function stream_eof()
    {
        return $this->read;
    }

    /**
     * Retrieve information about a file resource.
     *
     * @see stat
     *
     * @return array
     */
    public function stream_stat()
    {
        return [];
    }
}

echo file_get_contents('http://icanhazip.com') . PHP_EOL;
stream_wrapper_unregister('http');
stream_wrapper_register('http', streamWrapper::class);
echo file_get_contents('http://icanhazip.com') . PHP_EOL;
stream_wrapper_restore('http');
echo file_get_contents('http://icanhazip.com') . PHP_EOL;
/**
 * XXX.XXX.XXX.XXX
 * 127.0.0.1
 * XXX.XXX.XXX.XXX
 */
Sinisa Colic

Sinisa Colic

Related Posts
Comments