Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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