Zend PHP 7 Certification – Input/Output – Streams

This post covers the Streams section of the Input/Output chapter when studying for the Zend PHP 7 Certification.

Streams are a way of generalising file, network, data compression, and other operations which share a common set of functions and uses.

A stream is usually referenced as scheme://target where scheme is the wrapper or protocol being used. target is usually a file name or a URL, but it depends on the scheme defined.

A list of supported protocols and wrappers can be seen below.

  • 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 (RFC 2397)
  • glob:// — Find pathnames matching pattern
  • phar:// — PHP Archive
  • ssh2:// — Secure Shell 2
  • rar:// — RAR
  • ogg:// — Audio streams
  • expect:// — Process Interaction Streams

The most common form of reading a file probably involves using the file_get_contents() function where the file name is passed in as an argument.

<?php
$localfile = file_get_contents("/home/bar/foo.txt");

However streams can also be specified. For example, the below is identical to the above except that it used the file:// protocol.

$localfile = file_get_contents("file:///home/bar/foo.txt");

Other example of protocols in use can be seen below:

/* Read remote file from www.example.com using HTTP */
$httpfile  = file_get_contents("http://www.example.com/foo.txt");

/* Read remote file from www.example.com using HTTPS */
$httpsfile = file_get_contents("https://www.example.com/foo.txt");

/* Read remote file from ftp.example.com using FTP */
$ftpfile   = file_get_contents("ftp://user:pass@ftp.example.com/foo.txt");

/* Read remote file from ftp.example.com using FTPS */
$ftpsfile  = file_get_contents("ftps://user:pass@ftp.example.com/foo.txt");

PHP gives you the ability to create custom stream wrappers by using stream_wrapper_register() function. This function takes three parameters:

  • Protocol – The given name of the protocol
  • Classname – The class name that implements the protocol
  • Flag – Should be set to STREAM_IS_URL if protocol is a URL protocol. Default is 0, local stream.
<?php
$existed = in_array("var", stream_get_wrappers());
if ($existed) {
    stream_wrapper_unregister("var");
}
stream_wrapper_register("var", "VariableStream");
$myvar = "";

$fp = fopen("var://myvar", "r+");

Stream contexts are a set of parameters and wrapper options that can modify a stream’s behaviour.

In this case, the stream context can be passed as a parameter in fopen() and file_get_contents() to tell the functions to send that authorisation header and those options to the wrapper.

You can create a stream context using the stream_context_create() function.

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

$context = stream_context_create($opts);

/* Sends an http request to www.example.com
   with additional headers shown above */
$fp = fopen('http://www.example.com', 'r', false, $context);
fpassthru($fp);
fclose($fp);

The stream_context_set_params() function sets parameters for a stream/wrapper/context.

bool stream_context_set_params ( resource $stream_or_context , array $params )

The stream_copy_to_stream() function copies data from one stream to another. There can be up to 4 parameters passed in this function.

  • Source file – The file to copy data from
  • Destination file – The file to copy data to
  • Bytes – The maximum number of bytes to copy
  • Offset – The offset where to start to copy data

The return value is the number of bytes copied.

<?php
$src = fopen('http://www.example.com', 'r');
$dest1 = fopen('destination.txt', 'w');

echo stream_copy_to_stream($src, $dest1, 1024) . " bytes copied to destination.txt";

Stream Filters

A filter is a final piece of code which may perform operations on data as it is being read from or written to a stream. Any number of filters may be stacked onto a stream. Current filters can be seen using the stream_get_filters() function.

$streamlist = stream_get_filters();
print_r($streamlist);

// Outputs:
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.* )

Custom filters can be registered using the stream_filter_register() function. The second parameter (class name) must extend the php_user_filter class.

<?php

/* Define our filter class */
class strtoupper_filter extends php_user_filter {
  function filter($in, $out, &$consumed, $closing)
  {
    while ($bucket = stream_bucket_make_writeable($in)) {
      $bucket->data = strtoupper($bucket->data);
      $consumed += $bucket->datalen;
      stream_bucket_append($out, $bucket);
    }
    return PSFS_PASS_ON;
  }
}

/* Register our filter with PHP */
stream_filter_register("strtoupper", "strtoupper_filter")
    or die("Failed to register filter");

You can then add the custom filter to the stream using the stream_filter_append() function.

$fp = fopen("foo-bar.txt", "w");

/* Attach the registered filter to the stream just opened */
stream_filter_append($fp, "strtoupper");

Note: This article is based on PHP version 7.1.