Passed
Push — master ( 648522...f8a212 )
by Aleksei
01:45
created

GeneratorStream::detach()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 9
c 2
b 0
f 0
nc 2
nop 0
dl 0
loc 12
ccs 10
cts 10
cp 1
crap 2
rs 9.9666
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 37
    public function __construct(Generator $body)
28
    {
29 37
        $this->stream = $body;
30 37
    }
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 6
    public function getSize(): ?int
63
    {
64 6
        return $this->size;
65
    }
66
67 6
    public function tell(): int
68
    {
69 6
        return $this->caret;
70
    }
71
72 22
    public function eof(): bool
73
    {
74 22
        return $this->stream === null || !$this->stream->valid();
75
    }
76
77 3
    public function isSeekable(): bool
78
    {
79 3
        return false;
80
    }
81
82 1
    public function seek($offset, $whence = \SEEK_SET): void
83
    {
84 1
        throw new \RuntimeException('Stream is not seekable');
85
    }
86
87 1
    public function rewind(): void
88
    {
89 1
        if ($this->stream !== null) {
90 1
            $this->stream->rewind();
91
        }
92 1
        $this->caret = 0;
93 1
        $this->started = false;
94 1
    }
95
96 1
    public function isWritable(): bool
97
    {
98 1
        return false;
99
    }
100
101 1
    public function write($string): int
102
    {
103 1
        throw new \RuntimeException('Cannot write to a non-writable stream');
104
    }
105
106 13
    public function isReadable(): bool
107
    {
108 13
        return $this->readable;
109
    }
110
111 17
    public function read($length): string
112
    {
113 17
        if (!$this->readable) {
114 1
            throw new \RuntimeException('Cannot read from non-readable stream');
115
        }
116 16
        if ($this->eof()) {
117 2
            throw new \RuntimeException('Cannot read from ended stream');
118
        }
119 16
        if (!$this->started) {
120 16
            $this->started = true;
121 16
            $read = (string)$this->stream->current();
122 16
            $this->caret += strlen($read);
123 16
            return $read;
124
        }
125 13
        if ($this->readMode === self::READ_MODE_FIRST_YIELD) {
126
            $content = '';
127
            while (!$this->eof()) {
128
                $content .= $this->stream->send(null);
129
            }
130
            return $content;
131
        }
132 13
        $read = (string)$this->stream->send(null);
133 13
        $this->caret += strlen($read);
134 13
        if ($this->eof()) {
135 12
            $this->size = $this->caret;
136
        }
137 13
        return $read;
138
    }
139
140 8
    public function getContents(): string
141
    {
142 8
        if ($this->stream === null) {
143 2
            throw new \RuntimeException('Unable to read stream contents');
144
        }
145 6
        $content = '';
146 6
        while (!$this->eof()) {
147 6
            $content .= $this->read(PHP_INT_MAX);
148
        }
149 6
        return $content;
150
    }
151
152 4
    public function getMetadata($key = null)
153
    {
154 4
        if ($this->stream === null) {
155 2
            return $key ? null : [];
156
        }
157
158
        $meta = [
159 2
            'seekable' => $this->isSeekable(),
160 2
            'eof' => $this->eof(),
161
        ];
162
163 2
        if (null === $key) {
164 1
            return $meta;
165
        }
166
167 1
        return $meta[$key] ?? null;
168
    }
169
}
170