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