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

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 129ea1e6d783
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 * Reads from multiple streams, one after the other.
8 *
9 * This is a read-only stream decorator.
10 */
11 class AppendStream implements StreamInterface
12 {
13 /** @var StreamInterface[] Streams being decorated */
14 private $streams = [];
15
16 private $seekable = true;
17 private $current = 0;
18 private $pos = 0;
19 private $detached = false;
20
21 /**
22 * @param StreamInterface[] $streams Streams to decorate. Each stream must
23 * be readable.
24 */
25 public function __construct(array $streams = [])
26 {
27 foreach ($streams as $stream) {
28 $this->addStream($stream);
29 }
30 }
31
32 public function __toString()
33 {
34 try {
35 $this->rewind();
36 return $this->getContents();
37 } catch (\Exception $e) {
38 return '';
39 }
40 }
41
42 /**
43 * Add a stream to the AppendStream
44 *
45 * @param StreamInterface $stream Stream to append. Must be readable.
46 *
47 * @throws \InvalidArgumentException if the stream is not readable
48 */
49 public function addStream(StreamInterface $stream)
50 {
51 if (!$stream->isReadable()) {
52 throw new \InvalidArgumentException('Each stream must be readable');
53 }
54
55 // The stream is only seekable if all streams are seekable
56 if (!$stream->isSeekable()) {
57 $this->seekable = false;
58 }
59
60 $this->streams[] = $stream;
61 }
62
63 public function getContents()
64 {
65 return copy_to_string($this);
66 }
67
68 /**
69 * Closes each attached stream.
70 *
71 * {@inheritdoc}
72 */
73 public function close()
74 {
75 $this->pos = $this->current = 0;
76
77 foreach ($this->streams as $stream) {
78 $stream->close();
79 }
80
81 $this->streams = [];
82 }
83
84 /**
85 * Detaches each attached stream
86 *
87 * {@inheritdoc}
88 */
89 public function detach()
90 {
91 $this->close();
92 $this->detached = true;
93 }
94
95 public function tell()
96 {
97 return $this->pos;
98 }
99
100 /**
101 * Tries to calculate the size by adding the size of each stream.
102 *
103 * If any of the streams do not return a valid number, then the size of the
104 * append stream cannot be determined and null is returned.
105 *
106 * {@inheritdoc}
107 */
108 public function getSize()
109 {
110 $size = 0;
111
112 foreach ($this->streams as $stream) {
113 $s = $stream->getSize();
114 if ($s === null) {
115 return null;
116 }
117 $size += $s;
118 }
119
120 return $size;
121 }
122
123 public function eof()
124 {
125 return !$this->streams ||
126 ($this->current >= count($this->streams) - 1 &&
127 $this->streams[$this->current]->eof());
128 }
129
130 public function rewind()
131 {
132 $this->seek(0);
133 }
134
135 /**
136 * Attempts to seek to the given position. Only supports SEEK_SET.
137 *
138 * {@inheritdoc}
139 */
140 public function seek($offset, $whence = SEEK_SET)
141 {
142 if (!$this->seekable) {
143 throw new \RuntimeException('This AppendStream is not seekable');
144 } elseif ($whence !== SEEK_SET) {
145 throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
146 }
147
148 $this->pos = $this->current = 0;
149
150 // Rewind each stream
151 foreach ($this->streams as $i => $stream) {
152 try {
153 $stream->rewind();
154 } catch (\Exception $e) {
155 throw new \RuntimeException('Unable to seek stream '
156 . $i . ' of the AppendStream', 0, $e);
157 }
158 }
159
160 // Seek to the actual position by reading from each stream
161 while ($this->pos < $offset && !$this->eof()) {
162 $result = $this->read(min(8096, $offset - $this->pos));
163 if ($result === '') {
164 break;
165 }
166 }
167 }
168
169 /**
170 * Reads from all of the appended streams until the length is met or EOF.
171 *
172 * {@inheritdoc}
173 */
174 public function read($length)
175 {
176 $buffer = '';
177 $total = count($this->streams) - 1;
178 $remaining = $length;
179 $progressToNext = false;
180
181 while ($remaining > 0) {
182
183 // Progress to the next stream if needed.
184 if ($progressToNext || $this->streams[$this->current]->eof()) {
185 $progressToNext = false;
186 if ($this->current === $total) {
187 break;
188 }
189 $this->current++;
190 }
191
192 $result = $this->streams[$this->current]->read($remaining);
193
194 // Using a loose comparison here to match on '', false, and null
195 if ($result == null) {
196 $progressToNext = true;
197 continue;
198 }
199
200 $buffer .= $result;
201 $remaining = $length - strlen($buffer);
202 }
203
204 $this->pos += strlen($buffer);
205
206 return $buffer;
207 }
208
209 public function isReadable()
210 {
211 return true;
212 }
213
214 public function isWritable()
215 {
216 return false;
217 }
218
219 public function isSeekable()
220 {
221 return $this->seekable;
222 }
223
224 public function write($string)
225 {
226 throw new \RuntimeException('Cannot write to an AppendStream');
227 }
228
229 public function getMetadata($key = null)
230 {
231 return $key ? null : [];
232 }
233 }