Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | 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
 * Reads from multiple streams, one after the other.
11
 *
12
 * This is a read-only stream decorator.
13
 */
14
final class AppendStream implements StreamInterface
15
{
16
    /** @var StreamInterface[] Streams being decorated */
17
    private $streams = [];
18
 
19
    /** @var bool */
20
    private $seekable = true;
21
 
22
    /** @var int */
23
    private $current = 0;
24
 
25
    /** @var int */
26
    private $pos = 0;
27
 
28
    /**
29
     * @param StreamInterface[] $streams Streams to decorate. Each stream must
30
     *                                   be readable.
31
     */
32
    public function __construct(array $streams = [])
33
    {
34
        foreach ($streams as $stream) {
35
            $this->addStream($stream);
36
        }
37
    }
38
 
39
    public function __toString(): string
40
    {
41
        try {
42
            $this->rewind();
1441 ariadna 43
 
1 efrain 44
            return $this->getContents();
45
        } catch (\Throwable $e) {
46
            if (\PHP_VERSION_ID >= 70400) {
47
                throw $e;
48
            }
49
            trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
1441 ariadna 50
 
1 efrain 51
            return '';
52
        }
53
    }
54
 
55
    /**
56
     * Add a stream to the AppendStream
57
     *
58
     * @param StreamInterface $stream Stream to append. Must be readable.
59
     *
60
     * @throws \InvalidArgumentException if the stream is not readable
61
     */
62
    public function addStream(StreamInterface $stream): void
63
    {
64
        if (!$stream->isReadable()) {
65
            throw new \InvalidArgumentException('Each stream must be readable');
66
        }
67
 
68
        // The stream is only seekable if all streams are seekable
69
        if (!$stream->isSeekable()) {
70
            $this->seekable = false;
71
        }
72
 
73
        $this->streams[] = $stream;
74
    }
75
 
76
    public function getContents(): string
77
    {
78
        return Utils::copyToString($this);
79
    }
80
 
81
    /**
82
     * Closes each attached stream.
83
     */
84
    public function close(): void
85
    {
86
        $this->pos = $this->current = 0;
87
        $this->seekable = true;
88
 
89
        foreach ($this->streams as $stream) {
90
            $stream->close();
91
        }
92
 
93
        $this->streams = [];
94
    }
95
 
96
    /**
97
     * Detaches each attached stream.
98
     *
99
     * Returns null as it's not clear which underlying stream resource to return.
100
     */
101
    public function detach()
102
    {
103
        $this->pos = $this->current = 0;
104
        $this->seekable = true;
105
 
106
        foreach ($this->streams as $stream) {
107
            $stream->detach();
108
        }
109
 
110
        $this->streams = [];
111
 
112
        return null;
113
    }
114
 
115
    public function tell(): int
116
    {
117
        return $this->pos;
118
    }
119
 
120
    /**
121
     * Tries to calculate the size by adding the size of each stream.
122
     *
123
     * If any of the streams do not return a valid number, then the size of the
124
     * append stream cannot be determined and null is returned.
125
     */
126
    public function getSize(): ?int
127
    {
128
        $size = 0;
129
 
130
        foreach ($this->streams as $stream) {
131
            $s = $stream->getSize();
132
            if ($s === null) {
133
                return null;
134
            }
135
            $size += $s;
136
        }
137
 
138
        return $size;
139
    }
140
 
141
    public function eof(): bool
142
    {
1441 ariadna 143
        return !$this->streams
144
            || ($this->current >= count($this->streams) - 1
145
             && $this->streams[$this->current]->eof());
1 efrain 146
    }
147
 
148
    public function rewind(): void
149
    {
150
        $this->seek(0);
151
    }
152
 
153
    /**
154
     * Attempts to seek to the given position. Only supports SEEK_SET.
155
     */
156
    public function seek($offset, $whence = SEEK_SET): void
157
    {
158
        if (!$this->seekable) {
159
            throw new \RuntimeException('This AppendStream is not seekable');
160
        } elseif ($whence !== SEEK_SET) {
161
            throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
162
        }
163
 
164
        $this->pos = $this->current = 0;
165
 
166
        // Rewind each stream
167
        foreach ($this->streams as $i => $stream) {
168
            try {
169
                $stream->rewind();
170
            } catch (\Exception $e) {
171
                throw new \RuntimeException('Unable to seek stream '
1441 ariadna 172
                    .$i.' of the AppendStream', 0, $e);
1 efrain 173
            }
174
        }
175
 
176
        // Seek to the actual position by reading from each stream
177
        while ($this->pos < $offset && !$this->eof()) {
178
            $result = $this->read(min(8096, $offset - $this->pos));
179
            if ($result === '') {
180
                break;
181
            }
182
        }
183
    }
184
 
185
    /**
186
     * Reads from all of the appended streams until the length is met or EOF.
187
     */
188
    public function read($length): string
189
    {
190
        $buffer = '';
191
        $total = count($this->streams) - 1;
192
        $remaining = $length;
193
        $progressToNext = false;
194
 
195
        while ($remaining > 0) {
196
            // Progress to the next stream if needed.
197
            if ($progressToNext || $this->streams[$this->current]->eof()) {
198
                $progressToNext = false;
199
                if ($this->current === $total) {
200
                    break;
201
                }
1441 ariadna 202
                ++$this->current;
1 efrain 203
            }
204
 
205
            $result = $this->streams[$this->current]->read($remaining);
206
 
207
            if ($result === '') {
208
                $progressToNext = true;
209
                continue;
210
            }
211
 
212
            $buffer .= $result;
213
            $remaining = $length - strlen($buffer);
214
        }
215
 
216
        $this->pos += strlen($buffer);
217
 
218
        return $buffer;
219
    }
220
 
221
    public function isReadable(): bool
222
    {
223
        return true;
224
    }
225
 
226
    public function isWritable(): bool
227
    {
228
        return false;
229
    }
230
 
231
    public function isSeekable(): bool
232
    {
233
        return $this->seekable;
234
    }
235
 
236
    public function write($string): int
237
    {
238
        throw new \RuntimeException('Cannot write to an AppendStream');
239
    }
240
 
241
    /**
242
     * @return mixed
243
     */
244
    public function getMetadata($key = null)
245
    {
246
        return $key ? null : [];
247
    }
248
}