source: vendor/guzzlehttp/psr7/src/AppendStream.php

Last change on this file was e3d4e0a, checked in by Vlado 222039 <vlado.popovski@…>, 7 days ago

Upload project files

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