You are here

AppendStream.php in Lockr 7.3

Namespace

GuzzleHttp\Psr7

File

vendor/guzzlehttp/psr7/src/AppendStream.php
View source
<?php

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;

/**
 * Reads from multiple streams, one after the other.
 *
 * This is a read-only stream decorator.
 */
class AppendStream implements StreamInterface {

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

  /**
   * @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;
    $this->seekable = true;
    foreach ($this->streams as $stream) {
      $stream
        ->close();
    }
    $this->streams = [];
  }

  /**
   * Detaches each attached stream.
   *
   * Returns null as it's not clear which underlying stream resource to return.
   *
   * {@inheritdoc}
   */
  public function detach() {
    $this->pos = $this->current = 0;
    $this->seekable = true;
    foreach ($this->streams as $stream) {
      $stream
        ->detach();
    }
    $this->streams = [];
  }
  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 : [];
  }

}

Classes

Namesort descending Description
AppendStream Reads from multiple streams, one after the other.