comparison vendor/guzzlehttp/psr7/src/CachingStream.php @ 0:4c8ae668cc8c

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