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