You are here

class CachingStream in Zircon Profile 8

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

Stream decorator that can cache previously read bytes from a sequentially read stream.

Hierarchy

Expanded class hierarchy of CachingStream

1 file declares its use of CachingStream
CachingStreamTest.php in vendor/guzzlehttp/psr7/tests/CachingStreamTest.php

File

vendor/guzzlehttp/psr7/src/CachingStream.php, line 10

Namespace

GuzzleHttp\Psr7
View source
class CachingStream implements StreamInterface {
  use StreamDecoratorTrait;

  /** @var StreamInterface Stream being wrapped */
  private $remoteStream;

  /** @var int Number of bytes to skip reading due to a write on the buffer */
  private $skipReadBytes = 0;

  /**
   * We will treat the buffer object as the body of the stream
   *
   * @param StreamInterface $stream Stream to cache
   * @param StreamInterface $target Optionally specify where data is cached
   */
  public function __construct(StreamInterface $stream, StreamInterface $target = null) {
    $this->remoteStream = $stream;
    $this->stream = $target ?: new Stream(fopen('php://temp', 'r+'));
  }
  public function getSize() {
    return max($this->stream
      ->getSize(), $this->remoteStream
      ->getSize());
  }
  public function rewind() {
    $this
      ->seek(0);
  }
  public function seek($offset, $whence = SEEK_SET) {
    if ($whence == SEEK_SET) {
      $byte = $offset;
    }
    elseif ($whence == SEEK_CUR) {
      $byte = $offset + $this
        ->tell();
    }
    elseif ($whence == SEEK_END) {
      $size = $this->remoteStream
        ->getSize();
      if ($size === null) {
        $size = $this
          ->cacheEntireStream();
      }

      // Because 0 is the first byte, we seek to size - 1.
      $byte = $size - 1 - $offset;
    }
    else {
      throw new \InvalidArgumentException('Invalid whence');
    }
    $diff = $byte - $this->stream
      ->getSize();
    if ($diff > 0) {

      // If the seek byte is greater the number of read bytes, then read
      // the difference of bytes to cache the bytes and inherently seek.
      $this
        ->read($diff);
    }
    else {

      // We can just do a normal seek since we've already seen this byte.
      $this->stream
        ->seek($byte);
    }
  }
  public function read($length) {

    // Perform a regular read on any previously read data from the buffer
    $data = $this->stream
      ->read($length);
    $remaining = $length - strlen($data);

    // More data was requested so read from the remote stream
    if ($remaining) {

      // If data was written to the buffer in a position that would have
      // been filled from the remote stream, then we must skip bytes on
      // the remote stream to emulate overwriting bytes from that
      // position. This mimics the behavior of other PHP stream wrappers.
      $remoteData = $this->remoteStream
        ->read($remaining + $this->skipReadBytes);
      if ($this->skipReadBytes) {
        $len = strlen($remoteData);
        $remoteData = substr($remoteData, $this->skipReadBytes);
        $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
      }
      $data .= $remoteData;
      $this->stream
        ->write($remoteData);
    }
    return $data;
  }
  public function write($string) {

    // When appending to the end of the currently read stream, you'll want
    // to skip bytes from being read from the remote stream to emulate
    // other stream wrappers. Basically replacing bytes of data of a fixed
    // length.
    $overflow = strlen($string) + $this
      ->tell() - $this->remoteStream
      ->tell();
    if ($overflow > 0) {
      $this->skipReadBytes += $overflow;
    }
    return $this->stream
      ->write($string);
  }
  public function eof() {
    return $this->stream
      ->eof() && $this->remoteStream
      ->eof();
  }

  /**
   * Close both the remote stream and buffer stream
   */
  public function close() {
    $this->remoteStream
      ->close() && $this->stream
      ->close();
  }
  private function cacheEntireStream() {
    $target = new FnStream([
      'write' => 'strlen',
    ]);
    copy_to_stream($this, $target);
    return $this
      ->tell();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CachingStream::$remoteStream private property @var StreamInterface Stream being wrapped
CachingStream::$skipReadBytes private property @var int Number of bytes to skip reading due to a write on the buffer
CachingStream::cacheEntireStream private function
CachingStream::close public function Close both the remote stream and buffer stream Overrides StreamDecoratorTrait::close
CachingStream::eof public function Returns true if the stream is at the end of the stream. Overrides StreamDecoratorTrait::eof
CachingStream::getSize public function Get the size of the stream if known. Overrides StreamDecoratorTrait::getSize
CachingStream::read public function Read data from the stream. Overrides StreamDecoratorTrait::read
CachingStream::rewind public function Seek to the beginning of the stream. Overrides StreamDecoratorTrait::rewind
CachingStream::seek public function Seek to a position in the stream. Overrides StreamDecoratorTrait::seek
CachingStream::write public function Write data to the stream. Overrides StreamDecoratorTrait::write
CachingStream::__construct public function We will treat the buffer object as the body of the stream Overrides StreamDecoratorTrait::__construct
StreamDecoratorTrait::createStream protected function Implement in subclasses to dynamically create streams when requested. 2
StreamDecoratorTrait::detach public function
StreamDecoratorTrait::getContents public function
StreamDecoratorTrait::getMetadata public function
StreamDecoratorTrait::isReadable public function
StreamDecoratorTrait::isSeekable public function 1
StreamDecoratorTrait::isWritable public function 1
StreamDecoratorTrait::tell public function 1
StreamDecoratorTrait::__call public function Allow decorators to implement custom methods
StreamDecoratorTrait::__get public function Magic method used to create a new stream if streams are not added in the constructor of a decorator (e.g., LazyOpenStream).
StreamDecoratorTrait::__toString public function