You are here

abstract class LocalStream in Drupal 9

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/StreamWrapper/LocalStream.php \Drupal\Core\StreamWrapper\LocalStream

Defines a Drupal stream wrapper base class for local files.

This class provides a complete stream wrapper implementation. URIs such as "public://example.txt" are expanded to a normal filesystem path such as "sites/default/files/example.txt" and then PHP filesystem functions are invoked.

Drupal\Core\StreamWrapper\LocalStream implementations need to implement at least the getDirectoryPath() and getExternalUrl() methods.

Hierarchy

Expanded class hierarchy of LocalStream

3 files declare their use of LocalStream
DummyStreamWrapper.php in core/modules/file/tests/file_test/src/StreamWrapper/DummyStreamWrapper.php
FileCopy.php in core/modules/migrate/src/Plugin/migrate/process/FileCopy.php
TranslationsStream.php in core/modules/locale/src/StreamWrapper/TranslationsStream.php

File

core/lib/Drupal/Core/StreamWrapper/LocalStream.php, line 16

Namespace

Drupal\Core\StreamWrapper
View source
abstract class LocalStream implements StreamWrapperInterface {

  /**
   * Stream context resource.
   *
   * @var resource
   */
  public $context;

  /**
   * A generic resource handle.
   *
   * @var resource
   */
  public $handle = NULL;

  /**
   * Instance URI (stream).
   *
   * A stream is referenced as "scheme://target".
   *
   * @var string
   */
  protected $uri;

  /**
   * {@inheritdoc}
   */
  public static function getType() {
    return StreamWrapperInterface::NORMAL;
  }

  /**
   * Gets the path that the wrapper is responsible for.
   *
   * @todo Review this method name in D8 per https://www.drupal.org/node/701358.
   *
   * @return string
   *   String specifying the path.
   */
  public abstract function getDirectoryPath();

  /**
   * {@inheritdoc}
   */
  public function setUri($uri) {
    $this->uri = $uri;
  }

  /**
   * {@inheritdoc}
   */
  public function getUri() {
    return $this->uri;
  }

  /**
   * Returns the local writable target of the resource within the stream.
   *
   * This function should be used in place of calls to realpath() or similar
   * functions when attempting to determine the location of a file. While
   * functions like realpath() may return the location of a read-only file, this
   * method may return a URI or path suitable for writing that is completely
   * separate from the URI used for reading.
   *
   * @param string $uri
   *   Optional URI.
   *
   * @return string|bool
   *   Returns a string representing a location suitable for writing of a file,
   *   or FALSE if unable to write to the file such as with read-only streams.
   */
  protected function getTarget($uri = NULL) {
    if (!isset($uri)) {
      $uri = $this->uri;
    }
    list(, $target) = explode('://', $uri, 2);

    // Remove erroneous leading or trailing, forward-slashes and backslashes.
    return trim($target, '\\/');
  }

  /**
   * {@inheritdoc}
   */
  public function realpath() {
    return $this
      ->getLocalPath();
  }

  /**
   * Returns the canonical absolute path of the URI, if possible.
   *
   * @param string $uri
   *   (optional) The stream wrapper URI to be converted to a canonical
   *   absolute path. This may point to a directory or another type of file.
   *
   * @return string|bool
   *   If $uri is not set, returns the canonical absolute path of the URI
   *   previously set by the
   *   Drupal\Core\StreamWrapper\StreamWrapperInterface::setUri() function.
   *   If $uri is set and valid for this class, returns its canonical absolute
   *   path, as determined by the realpath() function. If $uri is set but not
   *   valid, returns FALSE.
   */
  protected function getLocalPath($uri = NULL) {
    if (!isset($uri)) {
      $uri = $this->uri;
    }
    $path = $this
      ->getDirectoryPath() . '/' . $this
      ->getTarget($uri);

    // In PHPUnit tests, the base path for local streams may be a virtual
    // filesystem stream wrapper URI, in which case this local stream acts like
    // a proxy. realpath() is not supported by vfsStream, because a virtual
    // file system does not have a real filepath.
    if (strpos($path, 'vfs://') === 0) {
      return $path;
    }
    $realpath = realpath($path);
    if (!$realpath) {

      // This file does not yet exist.
      $realpath = realpath(dirname($path)) . '/' . \Drupal::service('file_system')
        ->basename($path);
    }
    $directory = realpath($this
      ->getDirectoryPath());
    if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
      return FALSE;
    }
    return $realpath;
  }

  /**
   * {@inheritdoc}
   */
  public function stream_open($uri, $mode, $options, &$opened_path) {
    $this->uri = $uri;
    $path = $this
      ->getLocalPath();
    if ($path === FALSE) {
      if ($options & STREAM_REPORT_ERRORS) {
        trigger_error('stream_open() filename cannot be empty', E_USER_WARNING);
      }
      return FALSE;
    }
    $this->handle = $options & STREAM_REPORT_ERRORS ? fopen($path, $mode) : @fopen($path, $mode);
    if ((bool) $this->handle && $options & STREAM_USE_PATH) {
      $opened_path = $path;
    }
    return (bool) $this->handle;
  }

  /**
   * {@inheritdoc}
   */
  public function stream_lock($operation) {
    if (in_array($operation, [
      LOCK_SH,
      LOCK_EX,
      LOCK_UN,
      LOCK_NB,
    ])) {
      return flock($this->handle, $operation);
    }
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function stream_read($count) {
    return fread($this->handle, $count);
  }

  /**
   * {@inheritdoc}
   */
  public function stream_write($data) {
    return fwrite($this->handle, $data);
  }

  /**
   * {@inheritdoc}
   */
  public function stream_eof() {
    return feof($this->handle);
  }

  /**
   * {@inheritdoc}
   */
  public function stream_seek($offset, $whence = SEEK_SET) {

    // fseek returns 0 on success and -1 on a failure.
    // stream_seek   1 on success and  0 on a failure.
    return !fseek($this->handle, $offset, $whence);
  }

  /**
   * {@inheritdoc}
   */
  public function stream_flush() {
    return fflush($this->handle);
  }

  /**
   * {@inheritdoc}
   */
  public function stream_tell() {
    return ftell($this->handle);
  }

  /**
   * {@inheritdoc}
   */
  public function stream_stat() {
    return fstat($this->handle);
  }

  /**
   * {@inheritdoc}
   */
  public function stream_close() {
    return fclose($this->handle);
  }

  /**
   * {@inheritdoc}
   */
  public function stream_cast($cast_as) {
    return $this->handle ? $this->handle : FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function stream_metadata($uri, $option, $value) {
    $target = $this
      ->getLocalPath($uri);
    $return = FALSE;
    switch ($option) {
      case STREAM_META_TOUCH:
        if (!empty($value)) {
          $return = touch($target, $value[0], $value[1]);
        }
        else {
          $return = touch($target);
        }
        break;
      case STREAM_META_OWNER_NAME:
      case STREAM_META_OWNER:
        $return = chown($target, $value);
        break;
      case STREAM_META_GROUP_NAME:
      case STREAM_META_GROUP:
        $return = chgrp($target, $value);
        break;
      case STREAM_META_ACCESS:
        $return = chmod($target, $value);
        break;
    }
    if ($return) {

      // For convenience clear the file status cache of the underlying file,
      // since metadata operations are often followed by file status checks.
      clearstatcache(TRUE, $target);
    }
    return $return;
  }

  /**
   * {@inheritdoc}
   *
   * Since Windows systems do not allow it and it is not needed for most use
   * cases anyway, this method is not supported on local files and will trigger
   * an error and return false. If needed, custom subclasses can provide
   * OS-specific implementations for advanced use cases.
   */
  public function stream_set_option($option, $arg1, $arg2) {
    trigger_error('stream_set_option() not supported for local file based stream wrappers', E_USER_WARNING);
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function stream_truncate($new_size) {
    return ftruncate($this->handle, $new_size);
  }

  /**
   * {@inheritdoc}
   */
  public function unlink($uri) {
    $this->uri = $uri;
    return $this
      ->getFileSystem()
      ->unlink($this
      ->getLocalPath());
  }

  /**
   * {@inheritdoc}
   */
  public function rename($from_uri, $to_uri) {
    return rename($this
      ->getLocalPath($from_uri), $this
      ->getLocalPath($to_uri));
  }

  /**
   * {@inheritdoc}
   */
  public function dirname($uri = NULL) {
    list($scheme) = explode('://', $uri, 2);
    $target = $this
      ->getTarget($uri);
    $dirname = dirname($target);
    if ($dirname == '.') {
      $dirname = '';
    }
    return $scheme . '://' . $dirname;
  }

  /**
   * {@inheritdoc}
   */
  public function mkdir($uri, $mode, $options) {
    $this->uri = $uri;
    $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
    if ($recursive) {

      // $this->getLocalPath() fails if $uri has multiple levels of directories
      // that do not yet exist.
      $localpath = $this
        ->getDirectoryPath() . '/' . $this
        ->getTarget($uri);
    }
    else {
      $localpath = $this
        ->getLocalPath($uri);
    }

    /** @var \Drupal\Core\File\FileSystemInterface $file_system */
    $file_system = \Drupal::service('file_system');
    if ($options & STREAM_REPORT_ERRORS) {
      return $file_system
        ->mkdir($localpath, $mode, $recursive);
    }
    else {
      return @$file_system
        ->mkdir($localpath, $mode, $recursive);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function rmdir($uri, $options) {
    $this->uri = $uri;

    /** @var \Drupal\Core\File\FileSystemInterface $file_system */
    $file_system = \Drupal::service('file_system');
    if ($options & STREAM_REPORT_ERRORS) {
      return $file_system
        ->rmdir($this
        ->getLocalPath());
    }
    else {
      return @$file_system
        ->rmdir($this
        ->getLocalPath());
    }
  }

  /**
   * {@inheritdoc}
   */
  public function url_stat($uri, $flags) {
    $this->uri = $uri;
    $path = $this
      ->getLocalPath();

    // Suppress warnings if requested or if the file or directory does not
    // exist. This is consistent with PHP's plain filesystem stream wrapper.
    if ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) {
      return @stat($path);
    }
    else {
      return stat($path);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function dir_opendir($uri, $options) {
    $this->uri = $uri;
    $this->handle = opendir($this
      ->getLocalPath());
    return (bool) $this->handle;
  }

  /**
   * {@inheritdoc}
   */
  public function dir_readdir() {
    return readdir($this->handle);
  }

  /**
   * {@inheritdoc}
   */
  public function dir_rewinddir() {
    rewinddir($this->handle);

    // We do not really have a way to signal a failure as rewinddir() does not
    // have a return value and there is no way to read a directory handler
    // without advancing to the next file.
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function dir_closedir() {
    closedir($this->handle);

    // We do not really have a way to signal a failure as closedir() does not
    // have a return value.
    return TRUE;
  }

  /**
   * Returns file system service.
   *
   * @return \Drupal\Core\File\FileSystemInterface
   *   The file system service.
   */
  private function getFileSystem() {
    return \Drupal::service('file_system');
  }

}

Members

Namesort descending Modifiers Type Description Overrides
LocalStream::$context public property Stream context resource.
LocalStream::$handle public property A generic resource handle.
LocalStream::$uri protected property Instance URI (stream).
LocalStream::dirname public function Gets the name of the directory from a given path. Overrides StreamWrapperInterface::dirname
LocalStream::dir_closedir public function Close directory handle. Overrides PhpStreamWrapperInterface::dir_closedir
LocalStream::dir_opendir public function Open directory handle. Overrides PhpStreamWrapperInterface::dir_opendir
LocalStream::dir_readdir public function Read entry from directory handle. Overrides PhpStreamWrapperInterface::dir_readdir
LocalStream::dir_rewinddir public function Rewind directory handle. Overrides PhpStreamWrapperInterface::dir_rewinddir
LocalStream::getDirectoryPath abstract public function Gets the path that the wrapper is responsible for. 6
LocalStream::getFileSystem private function Returns file system service.
LocalStream::getLocalPath protected function Returns the canonical absolute path of the URI, if possible.
LocalStream::getTarget protected function Returns the local writable target of the resource within the stream.
LocalStream::getType public static function Returns the type of stream wrapper. Overrides StreamWrapperInterface::getType 5
LocalStream::getUri public function Returns the stream resource URI. Overrides StreamWrapperInterface::getUri
LocalStream::mkdir public function Create a directory. Overrides PhpStreamWrapperInterface::mkdir 1
LocalStream::realpath public function Returns canonical, absolute path of the resource. Overrides StreamWrapperInterface::realpath 1
LocalStream::rename public function Renames a file or directory. Overrides PhpStreamWrapperInterface::rename 1
LocalStream::rmdir public function Removes a directory. Overrides PhpStreamWrapperInterface::rmdir 1
LocalStream::setUri public function Sets the absolute stream resource URI. Overrides StreamWrapperInterface::setUri
LocalStream::stream_cast public function Retrieve the underlying stream resource. Overrides PhpStreamWrapperInterface::stream_cast
LocalStream::stream_close public function Closes stream. Overrides PhpStreamWrapperInterface::stream_close
LocalStream::stream_eof public function Tests for end-of-file on a file pointer. Overrides PhpStreamWrapperInterface::stream_eof
LocalStream::stream_flush public function Flushes the output. Overrides PhpStreamWrapperInterface::stream_flush 1
LocalStream::stream_lock public function Advisory file locking. Overrides PhpStreamWrapperInterface::stream_lock 1
LocalStream::stream_metadata public function Sets metadata on the stream. Overrides PhpStreamWrapperInterface::stream_metadata 1
LocalStream::stream_open public function Opens file or URL. Overrides PhpStreamWrapperInterface::stream_open 1
LocalStream::stream_read public function Read from stream. Overrides PhpStreamWrapperInterface::stream_read
LocalStream::stream_seek public function Seeks to specific location in a stream. Overrides PhpStreamWrapperInterface::stream_seek
LocalStream::stream_set_option public function Since Windows systems do not allow it and it is not needed for most use cases anyway, this method is not supported on local files and will trigger an error and return false. If needed, custom subclasses can provide OS-specific implementations for… Overrides PhpStreamWrapperInterface::stream_set_option
LocalStream::stream_stat public function Retrieve information about a file resource. Overrides PhpStreamWrapperInterface::stream_stat
LocalStream::stream_tell public function Retrieve the current position of a stream. Overrides PhpStreamWrapperInterface::stream_tell
LocalStream::stream_truncate public function Truncate stream. Overrides PhpStreamWrapperInterface::stream_truncate 1
LocalStream::stream_write public function Write to stream. Overrides PhpStreamWrapperInterface::stream_write 1
LocalStream::unlink public function Delete a file. Overrides PhpStreamWrapperInterface::unlink 1
LocalStream::url_stat public function Retrieve information about a file. Overrides PhpStreamWrapperInterface::url_stat
StreamWrapperInterface::ALL constant A filter that matches all wrappers.
StreamWrapperInterface::getDescription public function Returns the description of the stream wrapper for use in the UI. 6
StreamWrapperInterface::getExternalUrl public function Returns a web accessible URL for the resource. 6
StreamWrapperInterface::getName public function Returns the name of the stream wrapper for use in the UI. 6
StreamWrapperInterface::HIDDEN constant Defines the stream wrapper bit flag for a hidden file.
StreamWrapperInterface::LOCAL constant Refers to a local file system location.
StreamWrapperInterface::LOCAL_HIDDEN constant Hidden, readable and writable using local files.
StreamWrapperInterface::LOCAL_NORMAL constant Visible, readable and writable using local files.
StreamWrapperInterface::NORMAL constant This is the default 'type' flag. This does not include StreamWrapperInterface::LOCAL, because PHP grants a greater trust level to local files (for example, they can be used in an "include" statement, regardless of the…
StreamWrapperInterface::READ constant Wrapper is readable (almost always true).
StreamWrapperInterface::READ_VISIBLE constant Visible and read-only.
StreamWrapperInterface::VISIBLE constant Exposed in the UI and potentially web accessible.
StreamWrapperInterface::WRITE constant Wrapper is writable.
StreamWrapperInterface::WRITE_VISIBLE constant Visible, readable and writable.