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