Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

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