annotate vendor/guzzlehttp/psr7/src/CachingStream.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2 namespace GuzzleHttp\Psr7;
Chris@0 3
Chris@0 4 use Psr\Http\Message\StreamInterface;
Chris@0 5
Chris@0 6 /**
Chris@0 7 * Stream decorator that can cache previously read bytes from a sequentially
Chris@0 8 * read stream.
Chris@0 9 */
Chris@0 10 class CachingStream implements StreamInterface
Chris@0 11 {
Chris@0 12 use StreamDecoratorTrait;
Chris@0 13
Chris@0 14 /** @var StreamInterface Stream being wrapped */
Chris@0 15 private $remoteStream;
Chris@0 16
Chris@0 17 /** @var int Number of bytes to skip reading due to a write on the buffer */
Chris@0 18 private $skipReadBytes = 0;
Chris@0 19
Chris@0 20 /**
Chris@0 21 * We will treat the buffer object as the body of the stream
Chris@0 22 *
Chris@0 23 * @param StreamInterface $stream Stream to cache
Chris@0 24 * @param StreamInterface $target Optionally specify where data is cached
Chris@0 25 */
Chris@0 26 public function __construct(
Chris@0 27 StreamInterface $stream,
Chris@0 28 StreamInterface $target = null
Chris@0 29 ) {
Chris@0 30 $this->remoteStream = $stream;
Chris@0 31 $this->stream = $target ?: new Stream(fopen('php://temp', 'r+'));
Chris@0 32 }
Chris@0 33
Chris@0 34 public function getSize()
Chris@0 35 {
Chris@0 36 return max($this->stream->getSize(), $this->remoteStream->getSize());
Chris@0 37 }
Chris@0 38
Chris@0 39 public function rewind()
Chris@0 40 {
Chris@0 41 $this->seek(0);
Chris@0 42 }
Chris@0 43
Chris@0 44 public function seek($offset, $whence = SEEK_SET)
Chris@0 45 {
Chris@0 46 if ($whence == SEEK_SET) {
Chris@0 47 $byte = $offset;
Chris@0 48 } elseif ($whence == SEEK_CUR) {
Chris@0 49 $byte = $offset + $this->tell();
Chris@0 50 } elseif ($whence == SEEK_END) {
Chris@0 51 $size = $this->remoteStream->getSize();
Chris@0 52 if ($size === null) {
Chris@0 53 $size = $this->cacheEntireStream();
Chris@0 54 }
Chris@0 55 $byte = $size + $offset;
Chris@0 56 } else {
Chris@0 57 throw new \InvalidArgumentException('Invalid whence');
Chris@0 58 }
Chris@0 59
Chris@0 60 $diff = $byte - $this->stream->getSize();
Chris@0 61
Chris@0 62 if ($diff > 0) {
Chris@0 63 // Read the remoteStream until we have read in at least the amount
Chris@0 64 // of bytes requested, or we reach the end of the file.
Chris@0 65 while ($diff > 0 && !$this->remoteStream->eof()) {
Chris@0 66 $this->read($diff);
Chris@0 67 $diff = $byte - $this->stream->getSize();
Chris@0 68 }
Chris@0 69 } else {
Chris@0 70 // We can just do a normal seek since we've already seen this byte.
Chris@0 71 $this->stream->seek($byte);
Chris@0 72 }
Chris@0 73 }
Chris@0 74
Chris@0 75 public function read($length)
Chris@0 76 {
Chris@0 77 // Perform a regular read on any previously read data from the buffer
Chris@0 78 $data = $this->stream->read($length);
Chris@0 79 $remaining = $length - strlen($data);
Chris@0 80
Chris@0 81 // More data was requested so read from the remote stream
Chris@0 82 if ($remaining) {
Chris@0 83 // If data was written to the buffer in a position that would have
Chris@0 84 // been filled from the remote stream, then we must skip bytes on
Chris@0 85 // the remote stream to emulate overwriting bytes from that
Chris@0 86 // position. This mimics the behavior of other PHP stream wrappers.
Chris@0 87 $remoteData = $this->remoteStream->read(
Chris@0 88 $remaining + $this->skipReadBytes
Chris@0 89 );
Chris@0 90
Chris@0 91 if ($this->skipReadBytes) {
Chris@0 92 $len = strlen($remoteData);
Chris@0 93 $remoteData = substr($remoteData, $this->skipReadBytes);
Chris@0 94 $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
Chris@0 95 }
Chris@0 96
Chris@0 97 $data .= $remoteData;
Chris@0 98 $this->stream->write($remoteData);
Chris@0 99 }
Chris@0 100
Chris@0 101 return $data;
Chris@0 102 }
Chris@0 103
Chris@0 104 public function write($string)
Chris@0 105 {
Chris@0 106 // When appending to the end of the currently read stream, you'll want
Chris@0 107 // to skip bytes from being read from the remote stream to emulate
Chris@0 108 // other stream wrappers. Basically replacing bytes of data of a fixed
Chris@0 109 // length.
Chris@0 110 $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
Chris@0 111 if ($overflow > 0) {
Chris@0 112 $this->skipReadBytes += $overflow;
Chris@0 113 }
Chris@0 114
Chris@0 115 return $this->stream->write($string);
Chris@0 116 }
Chris@0 117
Chris@0 118 public function eof()
Chris@0 119 {
Chris@0 120 return $this->stream->eof() && $this->remoteStream->eof();
Chris@0 121 }
Chris@0 122
Chris@0 123 /**
Chris@0 124 * Close both the remote stream and buffer stream
Chris@0 125 */
Chris@0 126 public function close()
Chris@0 127 {
Chris@0 128 $this->remoteStream->close() && $this->stream->close();
Chris@0 129 }
Chris@0 130
Chris@0 131 private function cacheEntireStream()
Chris@0 132 {
Chris@0 133 $target = new FnStream(['write' => 'strlen']);
Chris@0 134 copy_to_stream($this, $target);
Chris@0 135
Chris@0 136 return $this->tell();
Chris@0 137 }
Chris@0 138 }