Writing stream wrappers

Last updated on
14 October 2016

This document will first discuss the implementation of a simple local stream wrapper, similar to the public wrapper included with Drupal core. Following that, the Drupal stream wrapper registry will be introduced and you will learn how to register your custom wrapper.

Sample wrapper (foobar://)

Example below implements stream for default Drupal distribution files, which should be located separately from public files dir, since it's not existent when distro is installed, but some files should exist by that time anyway (like image fields in sample content of a distro).

/**
 * Implements hook_stream_wrappers().
 */
function MYMODULE_stream_wrappers() {
  return array(
    'foobar' => array(
      'name' => t('Default distribution files'),
      'class' => 'FooBarStreamWrapper',
      'description' => t('Provides read-only paths to default distribution files.'),
      'type' => STREAM_WRAPPERS_READ_VISIBLE,
    ),
  );
}

/**
 * Default files (foobar://) stream wrapper class.
 */
class FooBarStreamWrapper extends DrupalPublicStreamWrapper {
  public function getDirectoryPath() {
    return 'profiles/mydistro/files';
  }
}

After this you could just replace public:// paths in database with foobar:// paths.

Stream wrapper registry

The stream wrapper registry in Drupal keeps track of the scheme and implementation class of each registered wrapper. It is important to understand that the Drupal stream wrapper registry differs from PHP's native registry. The Drupal registry builds on top of PHPs native support and adds more functionality. For example, PHP does not provide native capabilities to ask "what wrapper class is responsible for handling this URI or scheme?" This ability is needed in order to provide functions specific to Drupal such as drupal_chmod().

The wrapper registry does not register PHP's built-in wrappers such as http or ssl. These wrappers may still be used with PHP functions normally, but any functions that rely on the Drupal registry will fail because the wrapper will not be found. For example, the call file_stream_wrapper_valid_scheme('http') would return false despite PHP's built-in http wrapper being available. In most cases this will not be an issue because PHP's wrappers do not contain attributes specific to Drupal.

Registering wrappers

This is a pretty straightforward procedure involving hook_stream_wrappers() which returns an array of wrappers. Each wrapper having a name, description, type and class. Class being the actual PHP class of the wrapper and type being a constant with these possible values:

  • STREAM_WRAPPERS_HIDDEN: not visible in the UI or accessible via web, but readable and writable. E.g. the temporary directory for uploads.
  • STREAM_WRAPPERS_LOCAL_HIDDEN: hidden, readable and writeable using local files.
  • STREAM_WRAPPERS_LOCAL_NORMAL: visible, readable and writeable using local files.
  • STREAM_WRAPPERS_NORMAL: The default when type is omitted, do not include the STREAM_WRAPPERS_LOCAL flag. Read more about it here
  • STREAM_WRAPPERS_READ_VISIBLE: visible and read-only.
  • STREAM_WRAPPERS_WRITE_VISIBLE: visible, readable and writeable.

You can read more about all of these at the stream_wrappers.inc api page.

Example:

function flickrview_stream_wrappers() {
  return array(
    'flickr' => array(
      'name' => t('Flickr'),
      'class' => 'FlickrStreamWrapper',
      'description' => t('Stream wrapper for reading files from flickr'),
      'type' => STREAM_WRAPPERS_READ_VISIBLE,
    ),
  );
}