Passed
Push — master ( 7e9bb0...293623 )
by Aleksei
02:04
created

GeneratorStream::__toString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace roxblnfk\SmartStream\Stream;
6
7
use Generator;
8
use Psr\Http\Message\StreamInterface;
9
10
final class GeneratorStream implements StreamInterface
11
{
12
    public const READ_MODE_AS_IS = 1;
13
    public const READ_MODE_FIRST_YIELD = 2;
14
15
    private int $readMode = self::READ_MODE_AS_IS;
16
17
    private ?Generator $stream;
18
19
    private bool $readable = true;
20
21
    private ?int $size = null;
22
23
    private int $caret = 0;
24
25
    private bool $started = false;
26
27 27
    public function __construct(Generator $body)
28
    {
29 27
        $this->stream = $body;
30 27
    }
31
32 2
    public function __toString(): string
33
    {
34
        try {
35 2
            return $this->getContents();
36 1
        } catch (\Exception $e) {
37 1
            return '';
38
        }
39
    }
40
41 1
    public function close(): void
42
    {
43 1
        if ($this->stream !== null) {
44 1
            $this->detach();
45
        }
46 1
    }
47
48 8
    public function detach()
49
    {
50 8
        if ($this->stream === null) {
51 1
            return null;
52
        }
53 8
        $this->stream;
54 8
        $this->stream = null;
55 8
        $this->size = null;
56 8
        $this->caret = 0;
57 8
        $this->started = false;
58 8
        $this->readable = false;
59 8
        return null;
60
    }
61
62 5
    public function getSize(): ?int
63
    {
64 5
        if (null !== $this->size) {
65 1
            return $this->size;
66
        }
67
68 4
        if ($this->stream === null) {
69 3
            return null;
70
        }
71
72 1
        return null;
73
    }
74
75 4
    public function tell(): int
76
    {
77 4
        return $this->caret;
78
    }
79
80 13
    public function eof(): bool
81
    {
82 13
        return $this->stream === null || !$this->stream->valid();
83
    }
84
85 3
    public function isSeekable(): bool
86
    {
87 3
        return false;
88
    }
89
90 1
    public function seek($offset, $whence = \SEEK_SET): void
91
    {
92 1
        throw new \RuntimeException('Stream is not seekable');
93
    }
94
95 1
    public function rewind(): void
96
    {
97 1
        if ($this->stream !== null) {
98 1
            $this->stream->rewind();
99
        }
100 1
        $this->caret = 0;
101 1
        $this->started = false;
102 1
    }
103
104 1
    public function isWritable(): bool
105
    {
106 1
        return false;
107
    }
108
109 1
    public function write($string): int
110
    {
111 1
        throw new \RuntimeException('Cannot write to a non-writable stream');
112
    }
113
114 4
    public function isReadable(): bool
115
    {
116 4
        return $this->readable;
117
    }
118
119 9
    public function read($length): string
120
    {
121 9
        if (!$this->readable) {
122 1
            throw new \RuntimeException('Cannot read from non-readable stream');
123
        }
124 8
        if ($this->eof()) {
125 1
            throw new \RuntimeException('Cannot read from ended stream');
126
        }
127 8
        if (!$this->started) {
128 8
            $this->started = true;
129 8
            $read = (string)$this->stream->current();
130 8
            $this->caret += strlen($read);
131 8
            return $read;
132
        }
133 7
        if ($this->readMode === self::READ_MODE_FIRST_YIELD) {
134
            $content = '';
135
            while (!$this->eof()) {
136
                $content .= $this->stream->send(null);
137
            }
138
            return $content;
139
        }
140 7
        $read = (string)$this->stream->send(null);
141 7
        $this->caret += strlen($read);
142 7
        if ($this->eof()) {
143 6
            $this->size = $this->caret;
144
        }
145 7
        return $read;
146
    }
147
148 8
    public function getContents(): string
149
    {
150 8
        if ($this->stream === null) {
151 2
            throw new \RuntimeException('Unable to read stream contents');
152
        }
153 6
        $content = '';
154 6
        while (!$this->eof()) {
155 6
            $content .= $this->read(PHP_INT_MAX);
156
        }
157 6
        return $content;
158
    }
159
160 4
    public function getMetadata($key = null)
161
    {
162 4
        if ($this->stream === null) {
163 2
            return $key ? null : [];
164
        }
165
166
        $meta = [
167 2
            'seekable' => $this->isSeekable(),
168 2
            'eof' => $this->eof(),
169
        ];
170
171 2
        if (null === $key) {
172 1
            return $meta;
173
        }
174
175 1
        return $meta[$key] ?? null;
176
    }
177
}
178