Passed
Push — master ( 22bbb0...7e9bb0 )
by Aleksei
01:57
created

GeneratorStream::write()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

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