Completed
Push — master ( f12e08...4edb1e )
by Frederik
02:06
created

ConcatenatedStream::tell()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Stream;
5
6
use Genkgo\Mail\StreamInterface;
7
8
final class ConcatenatedStream implements StreamInterface
9
{
10
    /**
11
     * @var array|StreamInterface[]
12
     */
13
    private $streams = [];
14
    /**
15
     * @var int
16
     */
17
    private $position = 0;
18
    /**
19
     * @var int
20
     */
21
    private $index = 0;
22
23
    /**
24
     * ConcatenatedStream constructor.
25
     * @param iterable $streams
26
     */
27 28
    public function __construct(iterable $streams)
28
    {
29 28
        foreach ($streams as $stream) {
30 28
            $this->addStream($stream);
31
        }
32 28
    }
33
34
    /**
35
     * @param StreamInterface $stream
36
     */
37 28
    private function addStream(StreamInterface $stream): void
38
    {
39 28
        $this->streams[] = $stream;
40 28
    }
41
42
    /**
43
     * @return string
44
     */
45 22
    public function __toString(): string
46
    {
47 22
        $result = [];
48
49 22
        foreach ($this->streams as $stream) {
50 22
            $result[] = $stream->__toString();
51
        }
52
53 22
        return implode('', $result);
54
    }
55
56
    /**
57
     *
58
     */
59
    public function close(): void
60
    {
61
        foreach ($this->streams as $stream) {
62
            $stream->close();
63
        }
64
    }
65
66
    /**
67
     * @return mixed
68
     */
69
    public function detach()
70
    {
71
        return null;
72
    }
73
74
    /**
75
     * @return int|null
76
     */
77 1
    public function getSize(): ?int
78
    {
79 1
        $size = 0;
80 1
        foreach ($this->streams as $stream) {
81 1
            $streamSize = $stream->getSize();
82
83 1
            if ($streamSize === null) {
84
                return null;
85
            }
86
87 1
            $size += $streamSize;
88
        }
89
90 1
        return $size;
91
    }
92
93
    /**
94
     * @return int
95
     * @throws \RuntimeException
96
     */
97 1
    public function tell(): int
98
    {
99 1
        return $this->position;
100
    }
101
102
    /**
103
     * @return bool
104
     */
105 2
    public function eof(): bool
106
    {
107 2
        if (!isset($this->streams[$this->index])) {
108 2
            return true;
109
        }
110
111 2
        if (!$this->streams[$this->index]->eof()) {
112 2
            return false;
113
        }
114
115
        return isset($this->streams[$this->index + 1]);
116
    }
117
118
    /**
119
     * @return bool
120
     */
121
    public function isSeekable(): bool
122
    {
123
        foreach ($this->streams as $stream) {
124
            if ($stream->isSeekable() === false) {
125
                return false;
126
            }
127
        }
128
129
        return true;
130
    }
131
132
    /**
133
     * @param int $offset
134
     * @param int $whence
135
     * @return int
136
     */
137 1
    public function seek(int $offset, int $whence = SEEK_SET): int
138
    {
139 1
        $seek = 0;
140 1
        while (isset($this->streams[$this->index]) && $offset > $seek) {
141 1
            $streamSize = $this->streams[$this->index]->getSize();
142 1
            if ($this->streams[$this->index]->seek($offset - $seek) === -1) {
143
                return -1;
144
            }
145
146 1
            if ($streamSize > $offset - $seek) {
147 1
                $seek += $offset - $seek;
148
            } else {
149 1
                $seek += $streamSize;
150
            }
151
152 1
            if ($this->streams[$this->index]->eof()) {
153 1
                $this->index++;
154
            }
155
        }
156
157 1
        $this->position = $seek;
158 1
        return 0;
159
    }
160
161
    /**
162
     * @return bool
163
     */
164 1
    public function rewind(): bool
165
    {
166 1
        $this->position = 0;
167 1
        $this->index = 0;
168
169 1
        foreach ($this->streams as $stream) {
170 1
            $stream->rewind();
171
        }
172
173 1
        return true;
174
    }
175
176
    /**
177
     * @return bool
178
     */
179 1
    public function isWritable(): bool
180
    {
181 1
        return false;
182
    }
183
184
    /**
185
     * @param $string
186
     * @return int
187
     */
188 1
    public function write($string): int
189
    {
190 1
        throw new \RuntimeException('Cannot write to stream');
191
    }
192
193
    /**
194
     * @return bool
195
     */
196
    public function isReadable(): bool
197
    {
198
        return true;
199
    }
200
201
    /**
202
     * @param int $length
203
     * @return string
204
     */
205 4
    public function read(int $length): string
206
    {
207 4
        $result = '';
208 4
        while (strlen($result) < $length && isset($this->streams[$this->index])) {
209 4
            $result .= $this->streams[$this->index]->read($length);
210 4
            if ($this->streams[$this->index]->eof()) {
211 2
                $this->index++;
212
            }
213
        }
214
215 4
        $this->position += strlen($result);
216 4
        return $result;
217
    }
218
219
    /**
220
     * @return string
221
     */
222 3
    public function getContents(): string
223
    {
224 3
        $result = '';
225
226 3
        while (isset($this->streams[$this->index])) {
227 3
            $result .= $this->streams[$this->index]->getContents();
228 3
            $this->index++;
229
        }
230
231 3
        return $result;
232
    }
233
234
    /**
235
     * @param array $keys
236
     * @return array
237
     */
238
    public function getMetadata(array $keys = []): array
239
    {
240
        return [];
241
    }
242
}