You are here

CachingStream.php in Zircon Profile 8

Same filename and directory in other branches
  1. 8.0 vendor/guzzlehttp/psr7/src/CachingStream.php

Namespace

GuzzleHttp\Psr7

File

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

namespace GuzzleHttp\Psr7;

use Psr\Http\Message\StreamInterface;

/**
 * Stream decorator that can cache previously read bytes from a sequentially
 * read stream.
 */
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();
  }

}

Classes

Namesort descending Description
CachingStream Stream decorator that can cache previously read bytes from a sequentially read stream.