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}