Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. CachingStream.php

class CachingStream

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

Hierarchy

  • class \GuzzleHttp\Psr7\CachingStream implements \Psr\Http\Message\StreamInterface uses \GuzzleHttp\Psr7\StreamDecoratorTrait

Expanded class hierarchy of CachingStream

File

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

Namespace

GuzzleHttp\Psr7
View source
final 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;
    
    /**
     * @var StreamInterface
     */
    private $stream;
    
    /**
     * We will treat the buffer object as the body of the stream
     *
     * @param StreamInterface $stream Stream to cache. The cursor is assumed to be at the beginning of the stream.
     * @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(Utils::tryFopen('php://temp', 'r+'));
    }
    public function getSize() : ?int {
        $remoteSize = $this->remoteStream
            ->getSize();
        if (null === $remoteSize) {
            return null;
        }
        return max($this->stream
            ->getSize(), $remoteSize);
    }
    public function rewind() : void {
        $this->seek(0);
    }
    public function seek($offset, $whence = SEEK_SET) : void {
        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();
            }
            $byte = $size + $offset;
        }
        else {
            throw new \InvalidArgumentException('Invalid whence');
        }
        $diff = $byte - $this->stream
            ->getSize();
        if ($diff > 0) {
            // Read the remoteStream until we have read in at least the amount
            // of bytes requested, or we reach the end of the file.
            while ($diff > 0 && !$this->remoteStream
                ->eof()) {
                $this->read($diff);
                $diff = $byte - $this->stream
                    ->getSize();
            }
        }
        else {
            // We can just do a normal seek since we've already seen this byte.
            $this->stream
                ->seek($byte);
        }
    }
    public function read($length) : string {
        // 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) : int {
        // 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() : bool {
        return $this->stream
            ->eof() && $this->remoteStream
            ->eof();
    }
    
    /**
     * Close both the remote stream and buffer stream
     */
    public function close() : void {
        $this->remoteStream
            ->close();
        $this->stream
            ->close();
    }
    private function cacheEntireStream() : int {
        $target = new FnStream([
            'write' => 'strlen',
        ]);
        Utils::copyToStream($this, $target);
        return $this->tell();
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title 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::$stream private property
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

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal