Passed
Push — master ( b292e6...c5f4fa )
by butschster
07:03
created

GeneratorStream::read()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7.0099

Importance

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