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