You are here

class AppendStream in Zircon Profile 8

Same name and namespace in other branches
  1. 8.0 vendor/guzzlehttp/psr7/src/AppendStream.php \GuzzleHttp\Psr7\AppendStream

Reads from multiple streams, one after the other.

This is a read-only stream decorator.

Hierarchy

Expanded class hierarchy of AppendStream

1 file declares its use of AppendStream
AppendStreamTest.php in vendor/guzzlehttp/psr7/tests/AppendStreamTest.php

File

vendor/guzzlehttp/psr7/src/AppendStream.php, line 11

Namespace

GuzzleHttp\Psr7
View source
class AppendStream implements StreamInterface {

  /** @var StreamInterface[] Streams being decorated */
  private $streams = [];
  private $seekable = true;
  private $current = 0;
  private $pos = 0;
  private $detached = false;

  /**
   * @param StreamInterface[] $streams Streams to decorate. Each stream must
   *                                   be readable.
   */
  public function __construct(array $streams = []) {
    foreach ($streams as $stream) {
      $this
        ->addStream($stream);
    }
  }
  public function __toString() {
    try {
      $this
        ->rewind();
      return $this
        ->getContents();
    } catch (\Exception $e) {
      return '';
    }
  }

  /**
   * Add a stream to the AppendStream
   *
   * @param StreamInterface $stream Stream to append. Must be readable.
   *
   * @throws \InvalidArgumentException if the stream is not readable
   */
  public function addStream(StreamInterface $stream) {
    if (!$stream
      ->isReadable()) {
      throw new \InvalidArgumentException('Each stream must be readable');
    }

    // The stream is only seekable if all streams are seekable
    if (!$stream
      ->isSeekable()) {
      $this->seekable = false;
    }
    $this->streams[] = $stream;
  }
  public function getContents() {
    return copy_to_string($this);
  }

  /**
   * Closes each attached stream.
   *
   * {@inheritdoc}
   */
  public function close() {
    $this->pos = $this->current = 0;
    foreach ($this->streams as $stream) {
      $stream
        ->close();
    }
    $this->streams = [];
  }

  /**
   * Detaches each attached stream
   *
   * {@inheritdoc}
   */
  public function detach() {
    $this
      ->close();
    $this->detached = true;
  }
  public function tell() {
    return $this->pos;
  }

  /**
   * Tries to calculate the size by adding the size of each stream.
   *
   * If any of the streams do not return a valid number, then the size of the
   * append stream cannot be determined and null is returned.
   *
   * {@inheritdoc}
   */
  public function getSize() {
    $size = 0;
    foreach ($this->streams as $stream) {
      $s = $stream
        ->getSize();
      if ($s === null) {
        return null;
      }
      $size += $s;
    }
    return $size;
  }
  public function eof() {
    return !$this->streams || $this->current >= count($this->streams) - 1 && $this->streams[$this->current]
      ->eof();
  }
  public function rewind() {
    $this
      ->seek(0);
  }

  /**
   * Attempts to seek to the given position. Only supports SEEK_SET.
   *
   * {@inheritdoc}
   */
  public function seek($offset, $whence = SEEK_SET) {
    if (!$this->seekable) {
      throw new \RuntimeException('This AppendStream is not seekable');
    }
    elseif ($whence !== SEEK_SET) {
      throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
    }
    $this->pos = $this->current = 0;

    // Rewind each stream
    foreach ($this->streams as $i => $stream) {
      try {
        $stream
          ->rewind();
      } catch (\Exception $e) {
        throw new \RuntimeException('Unable to seek stream ' . $i . ' of the AppendStream', 0, $e);
      }
    }

    // Seek to the actual position by reading from each stream
    while ($this->pos < $offset && !$this
      ->eof()) {
      $result = $this
        ->read(min(8096, $offset - $this->pos));
      if ($result === '') {
        break;
      }
    }
  }

  /**
   * Reads from all of the appended streams until the length is met or EOF.
   *
   * {@inheritdoc}
   */
  public function read($length) {
    $buffer = '';
    $total = count($this->streams) - 1;
    $remaining = $length;
    $progressToNext = false;
    while ($remaining > 0) {

      // Progress to the next stream if needed.
      if ($progressToNext || $this->streams[$this->current]
        ->eof()) {
        $progressToNext = false;
        if ($this->current === $total) {
          break;
        }
        $this->current++;
      }
      $result = $this->streams[$this->current]
        ->read($remaining);

      // Using a loose comparison here to match on '', false, and null
      if ($result == null) {
        $progressToNext = true;
        continue;
      }
      $buffer .= $result;
      $remaining = $length - strlen($buffer);
    }
    $this->pos += strlen($buffer);
    return $buffer;
  }
  public function isReadable() {
    return true;
  }
  public function isWritable() {
    return false;
  }
  public function isSeekable() {
    return $this->seekable;
  }
  public function write($string) {
    throw new \RuntimeException('Cannot write to an AppendStream');
  }
  public function getMetadata($key = null) {
    return $key ? null : [];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
AppendStream::$current private property
AppendStream::$detached private property
AppendStream::$pos private property
AppendStream::$seekable private property
AppendStream::$streams private property @var StreamInterface[] Streams being decorated
AppendStream::addStream public function Add a stream to the AppendStream
AppendStream::close public function Closes each attached stream. Overrides StreamInterface::close
AppendStream::detach public function Detaches each attached stream Overrides StreamInterface::detach
AppendStream::eof public function Returns true if the stream is at the end of the stream. Overrides StreamInterface::eof
AppendStream::getContents public function Returns the remaining contents in a string Overrides StreamInterface::getContents
AppendStream::getMetadata public function Get stream metadata as an associative array or retrieve a specific key. Overrides StreamInterface::getMetadata
AppendStream::getSize public function Tries to calculate the size by adding the size of each stream. Overrides StreamInterface::getSize
AppendStream::isReadable public function Returns whether or not the stream is readable. Overrides StreamInterface::isReadable
AppendStream::isSeekable public function Returns whether or not the stream is seekable. Overrides StreamInterface::isSeekable
AppendStream::isWritable public function Returns whether or not the stream is writable. Overrides StreamInterface::isWritable
AppendStream::read public function Reads from all of the appended streams until the length is met or EOF. Overrides StreamInterface::read
AppendStream::rewind public function Seek to the beginning of the stream. Overrides StreamInterface::rewind
AppendStream::seek public function Attempts to seek to the given position. Only supports SEEK_SET. Overrides StreamInterface::seek
AppendStream::tell public function Returns the current position of the file read/write pointer Overrides StreamInterface::tell
AppendStream::write public function Write data to the stream. Overrides StreamInterface::write
AppendStream::__construct public function
AppendStream::__toString public function Reads all data from the stream into a string, from the beginning to end. Overrides StreamInterface::__toString