source: vendor/guzzlehttp/psr7/src/Stream.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: 7.2 KB
RevLine 
[e3d4e0a]1<?php
2
3declare(strict_types=1);
4
5namespace GuzzleHttp\Psr7;
6
7use Psr\Http\Message\StreamInterface;
8
9/**
10 * PHP stream implementation.
11 */
12class Stream implements StreamInterface
13{
14 /**
15 * @see https://www.php.net/manual/en/function.fopen.php
16 * @see https://www.php.net/manual/en/function.gzopen.php
17 */
18 private const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';
19 private const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/';
20
21 /** @var resource */
22 private $stream;
23 /** @var int|null */
24 private $size;
25 /** @var bool */
26 private $seekable;
27 /** @var bool */
28 private $readable;
29 /** @var bool */
30 private $writable;
31 /** @var string|null */
32 private $uri;
33 /** @var mixed[] */
34 private $customMetadata;
35
36 /**
37 * This constructor accepts an associative array of options.
38 *
39 * - size: (int) If a read stream would otherwise have an indeterminate
40 * size, but the size is known due to foreknowledge, then you can
41 * provide that size, in bytes.
42 * - metadata: (array) Any additional metadata to return when the metadata
43 * of the stream is accessed.
44 *
45 * @param resource $stream Stream resource to wrap.
46 * @param array{size?: int, metadata?: array} $options Associative array of options.
47 *
48 * @throws \InvalidArgumentException if the stream is not a stream resource
49 */
50 public function __construct($stream, array $options = [])
51 {
52 if (!is_resource($stream)) {
53 throw new \InvalidArgumentException('Stream must be a resource');
54 }
55
56 if (isset($options['size'])) {
57 $this->size = $options['size'];
58 }
59
60 $this->customMetadata = $options['metadata'] ?? [];
61 $this->stream = $stream;
62 $meta = stream_get_meta_data($this->stream);
63 $this->seekable = $meta['seekable'];
64 $this->readable = (bool) preg_match(self::READABLE_MODES, $meta['mode']);
65 $this->writable = (bool) preg_match(self::WRITABLE_MODES, $meta['mode']);
66 $this->uri = $this->getMetadata('uri');
67 }
68
69 /**
70 * Closes the stream when the destructed
71 */
72 public function __destruct()
73 {
74 $this->close();
75 }
76
77 public function __toString(): string
78 {
79 try {
80 if ($this->isSeekable()) {
81 $this->seek(0);
82 }
83
84 return $this->getContents();
85 } catch (\Throwable $e) {
86 if (\PHP_VERSION_ID >= 70400) {
87 throw $e;
88 }
89 trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
90
91 return '';
92 }
93 }
94
95 public function getContents(): string
96 {
97 if (!isset($this->stream)) {
98 throw new \RuntimeException('Stream is detached');
99 }
100
101 if (!$this->readable) {
102 throw new \RuntimeException('Cannot read from non-readable stream');
103 }
104
105 return Utils::tryGetContents($this->stream);
106 }
107
108 public function close(): void
109 {
110 if (isset($this->stream)) {
111 if (is_resource($this->stream)) {
112 fclose($this->stream);
113 }
114 $this->detach();
115 }
116 }
117
118 public function detach()
119 {
120 if (!isset($this->stream)) {
121 return null;
122 }
123
124 $result = $this->stream;
125 unset($this->stream);
126 $this->size = $this->uri = null;
127 $this->readable = $this->writable = $this->seekable = false;
128
129 return $result;
130 }
131
132 public function getSize(): ?int
133 {
134 if ($this->size !== null) {
135 return $this->size;
136 }
137
138 if (!isset($this->stream)) {
139 return null;
140 }
141
142 // Clear the stat cache if the stream has a URI
143 if ($this->uri) {
144 clearstatcache(true, $this->uri);
145 }
146
147 $stats = fstat($this->stream);
148 if (is_array($stats) && isset($stats['size'])) {
149 $this->size = $stats['size'];
150
151 return $this->size;
152 }
153
154 return null;
155 }
156
157 public function isReadable(): bool
158 {
159 return $this->readable;
160 }
161
162 public function isWritable(): bool
163 {
164 return $this->writable;
165 }
166
167 public function isSeekable(): bool
168 {
169 return $this->seekable;
170 }
171
172 public function eof(): bool
173 {
174 if (!isset($this->stream)) {
175 throw new \RuntimeException('Stream is detached');
176 }
177
178 return feof($this->stream);
179 }
180
181 public function tell(): int
182 {
183 if (!isset($this->stream)) {
184 throw new \RuntimeException('Stream is detached');
185 }
186
187 $result = ftell($this->stream);
188
189 if ($result === false) {
190 throw new \RuntimeException('Unable to determine stream position');
191 }
192
193 return $result;
194 }
195
196 public function rewind(): void
197 {
198 $this->seek(0);
199 }
200
201 public function seek($offset, $whence = SEEK_SET): void
202 {
203 $whence = (int) $whence;
204
205 if (!isset($this->stream)) {
206 throw new \RuntimeException('Stream is detached');
207 }
208 if (!$this->seekable) {
209 throw new \RuntimeException('Stream is not seekable');
210 }
211 if (fseek($this->stream, $offset, $whence) === -1) {
212 throw new \RuntimeException('Unable to seek to stream position '
213 .$offset.' with whence '.var_export($whence, true));
214 }
215 }
216
217 public function read($length): string
218 {
219 if (!isset($this->stream)) {
220 throw new \RuntimeException('Stream is detached');
221 }
222 if (!$this->readable) {
223 throw new \RuntimeException('Cannot read from non-readable stream');
224 }
225 if ($length < 0) {
226 throw new \RuntimeException('Length parameter cannot be negative');
227 }
228
229 if (0 === $length) {
230 return '';
231 }
232
233 try {
234 $string = fread($this->stream, $length);
235 } catch (\Exception $e) {
236 throw new \RuntimeException('Unable to read from stream', 0, $e);
237 }
238
239 if (false === $string) {
240 throw new \RuntimeException('Unable to read from stream');
241 }
242
243 return $string;
244 }
245
246 public function write($string): int
247 {
248 if (!isset($this->stream)) {
249 throw new \RuntimeException('Stream is detached');
250 }
251 if (!$this->writable) {
252 throw new \RuntimeException('Cannot write to a non-writable stream');
253 }
254
255 // We can't know the size after writing anything
256 $this->size = null;
257 $result = fwrite($this->stream, $string);
258
259 if ($result === false) {
260 throw new \RuntimeException('Unable to write to stream');
261 }
262
263 return $result;
264 }
265
266 /**
267 * @return mixed
268 */
269 public function getMetadata($key = null)
270 {
271 if (!isset($this->stream)) {
272 return $key ? null : [];
273 } elseif (!$key) {
274 return $this->customMetadata + stream_get_meta_data($this->stream);
275 } elseif (isset($this->customMetadata[$key])) {
276 return $this->customMetadata[$key];
277 }
278
279 $meta = stream_get_meta_data($this->stream);
280
281 return $meta[$key] ?? null;
282 }
283}
Note: See TracBrowser for help on using the repository browser.