Security update for Core, with self-updated composer
[yaffs-website] / vendor / zendframework / zend-diactoros / src / Stream.php
1 <?php
2 /**
3  * @see       https://github.com/zendframework/zend-diactoros for the canonical source repository
4  * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
5  * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
6  */
7
8 namespace Zend\Diactoros;
9
10 use InvalidArgumentException;
11 use RuntimeException;
12 use Psr\Http\Message\StreamInterface;
13
14 /**
15  * Implementation of PSR HTTP streams
16  */
17 class Stream implements StreamInterface
18 {
19     /**
20      * @var resource|null
21      */
22     protected $resource;
23
24     /**
25      * @var string|resource
26      */
27     protected $stream;
28
29     /**
30      * @param string|resource $stream
31      * @param string $mode Mode with which to open stream
32      * @throws InvalidArgumentException
33      */
34     public function __construct($stream, $mode = 'r')
35     {
36         $this->setStream($stream, $mode);
37     }
38
39     /**
40      * {@inheritdoc}
41      */
42     public function __toString()
43     {
44         if (! $this->isReadable()) {
45             return '';
46         }
47
48         try {
49             if ($this->isSeekable()) {
50                 $this->rewind();
51             }
52
53             return $this->getContents();
54         } catch (RuntimeException $e) {
55             return '';
56         }
57     }
58
59     /**
60      * {@inheritdoc}
61      */
62     public function close()
63     {
64         if (! $this->resource) {
65             return;
66         }
67
68         $resource = $this->detach();
69         fclose($resource);
70     }
71
72     /**
73      * {@inheritdoc}
74      */
75     public function detach()
76     {
77         $resource = $this->resource;
78         $this->resource = null;
79         return $resource;
80     }
81
82     /**
83      * Attach a new stream/resource to the instance.
84      *
85      * @param string|resource $resource
86      * @param string $mode
87      * @throws InvalidArgumentException for stream identifier that cannot be
88      *     cast to a resource
89      * @throws InvalidArgumentException for non-resource stream
90      */
91     public function attach($resource, $mode = 'r')
92     {
93         $this->setStream($resource, $mode);
94     }
95
96     /**
97      * {@inheritdoc}
98      */
99     public function getSize()
100     {
101         if (null === $this->resource) {
102             return null;
103         }
104
105         $stats = fstat($this->resource);
106         if ($stats !== false) {
107             return $stats['size'];
108         }
109
110         return null;
111     }
112
113     /**
114      * {@inheritdoc}
115      */
116     public function tell()
117     {
118         if (! $this->resource) {
119             throw new RuntimeException('No resource available; cannot tell position');
120         }
121
122         $result = ftell($this->resource);
123         if (! is_int($result)) {
124             throw new RuntimeException('Error occurred during tell operation');
125         }
126
127         return $result;
128     }
129
130     /**
131      * {@inheritdoc}
132      */
133     public function eof()
134     {
135         if (! $this->resource) {
136             return true;
137         }
138
139         return feof($this->resource);
140     }
141
142     /**
143      * {@inheritdoc}
144      */
145     public function isSeekable()
146     {
147         if (! $this->resource) {
148             return false;
149         }
150
151         $meta = stream_get_meta_data($this->resource);
152         return $meta['seekable'];
153     }
154
155     /**
156      * {@inheritdoc}
157      */
158     public function seek($offset, $whence = SEEK_SET)
159     {
160         if (! $this->resource) {
161             throw new RuntimeException('No resource available; cannot seek position');
162         }
163
164         if (! $this->isSeekable()) {
165             throw new RuntimeException('Stream is not seekable');
166         }
167
168         $result = fseek($this->resource, $offset, $whence);
169
170         if (0 !== $result) {
171             throw new RuntimeException('Error seeking within stream');
172         }
173
174         return true;
175     }
176
177     /**
178      * {@inheritdoc}
179      */
180     public function rewind()
181     {
182         return $this->seek(0);
183     }
184
185     /**
186      * {@inheritdoc}
187      */
188     public function isWritable()
189     {
190         if (! $this->resource) {
191             return false;
192         }
193
194         $meta = stream_get_meta_data($this->resource);
195         $mode = $meta['mode'];
196
197         return (
198             strstr($mode, 'x')
199             || strstr($mode, 'w')
200             || strstr($mode, 'c')
201             || strstr($mode, 'a')
202             || strstr($mode, '+')
203         );
204     }
205
206     /**
207      * {@inheritdoc}
208      */
209     public function write($string)
210     {
211         if (! $this->resource) {
212             throw new RuntimeException('No resource available; cannot write');
213         }
214
215         if (! $this->isWritable()) {
216             throw new RuntimeException('Stream is not writable');
217         }
218
219         $result = fwrite($this->resource, $string);
220
221         if (false === $result) {
222             throw new RuntimeException('Error writing to stream');
223         }
224         return $result;
225     }
226
227     /**
228      * {@inheritdoc}
229      */
230     public function isReadable()
231     {
232         if (! $this->resource) {
233             return false;
234         }
235
236         $meta = stream_get_meta_data($this->resource);
237         $mode = $meta['mode'];
238
239         return (strstr($mode, 'r') || strstr($mode, '+'));
240     }
241
242     /**
243      * {@inheritdoc}
244      */
245     public function read($length)
246     {
247         if (! $this->resource) {
248             throw new RuntimeException('No resource available; cannot read');
249         }
250
251         if (! $this->isReadable()) {
252             throw new RuntimeException('Stream is not readable');
253         }
254
255         $result = fread($this->resource, $length);
256
257         if (false === $result) {
258             throw new RuntimeException('Error reading stream');
259         }
260
261         return $result;
262     }
263
264     /**
265      * {@inheritdoc}
266      */
267     public function getContents()
268     {
269         if (! $this->isReadable()) {
270             throw new RuntimeException('Stream is not readable');
271         }
272
273         $result = stream_get_contents($this->resource);
274         if (false === $result) {
275             throw new RuntimeException('Error reading from stream');
276         }
277         return $result;
278     }
279
280     /**
281      * {@inheritdoc}
282      */
283     public function getMetadata($key = null)
284     {
285         if (null === $key) {
286             return stream_get_meta_data($this->resource);
287         }
288
289         $metadata = stream_get_meta_data($this->resource);
290         if (! array_key_exists($key, $metadata)) {
291             return null;
292         }
293
294         return $metadata[$key];
295     }
296
297     /**
298      * Set the internal stream resource.
299      *
300      * @param string|resource $stream String stream target or stream resource.
301      * @param string $mode Resource mode for stream target.
302      * @throws InvalidArgumentException for invalid streams or resources.
303      */
304     private function setStream($stream, $mode = 'r')
305     {
306         $error    = null;
307         $resource = $stream;
308
309         if (is_string($stream)) {
310             set_error_handler(function ($e) use (&$error) {
311                 $error = $e;
312             }, E_WARNING);
313             $resource = fopen($stream, $mode);
314             restore_error_handler();
315         }
316
317         if ($error) {
318             throw new InvalidArgumentException('Invalid stream reference provided');
319         }
320
321         if (! is_resource($resource) || 'stream' !== get_resource_type($resource)) {
322             throw new InvalidArgumentException(
323                 'Invalid stream provided; must be a string stream identifier or stream resource'
324             );
325         }
326
327         if ($stream !== $resource) {
328             $this->stream = $stream;
329         }
330
331         $this->resource = $resource;
332     }
333 }