Passed
Push — master ( f8a212...1af0f9 )
by Aleksei
01:55
created

BucketStream::getConverter()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 1
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace roxblnfk\SmartStream\Stream;
6
7
use Psr\Http\Message\StreamInterface;
8
use roxblnfk\SmartStream\ConverterInterface;
9
use roxblnfk\SmartStream\ConverterMatcherInterface;
10
use roxblnfk\SmartStream\Data\DataBucket;
11
use roxblnfk\SmartStream\Exception\ConverterNotFoundException;
12
use roxblnfk\SmartStream\Matching\MatchingResult;
13
use RuntimeException;
14
15
final class BucketStream implements StreamInterface
16
{
17
    private ConverterMatcherInterface $converterMatcher;
18
    private ?DataBucket $bucket;
19
    private ?GeneratorStream $stream = null;
20
    private ?MatchingResult $matchedResult = null;
21
22 87
    public function __construct(ConverterMatcherInterface $converterMatcher, DataBucket $bucket)
23
    {
24 87
        $this->bucket = $bucket;
25 87
        $this->converterMatcher = $converterMatcher;
26
27 87
        if ($bucket->isConvertable()) {
28 86
            $result = $this->converterMatcher->match($this->bucket);
29
            // if should be converted but no converter was found
30 86
            if ($result === null && $bucket->hasFormat()) {
31
                throw new ConverterNotFoundException($bucket->getFormat());
32
            }
33 86
            $this->matchedResult = $result;
34
        }
35 87
    }
36 4
    public function __toString(): string
37
    {
38
        try {
39 4
            if ($this->isSeekable()) {
40
                $this->seek(0);
41
            }
42 4
            return $this->getContents();
43 3
        } catch (\Exception $e) {
44 3
            return '';
45
        }
46
    }
47 2
    public function close(): void
48
    {
49 2
        $this->detach();
50 2
    }
51 18
    public function detach()
52
    {
53 18
        $this->matchedResult = null;
54 18
        $this->stream = null;
55 18
        $this->bucket = null;
56 18
        return null;
57
    }
58 9
    public function getSize(): ?int
59
    {
60 9
        return $this->stream === null ? null : $this->stream->getSize();
61
    }
62 6
    public function tell(): int
63
    {
64 6
        return $this->stream === null ? 0 : $this->stream->tell();
65
    }
66 20
    public function eof(): bool
67
    {
68 20
        if ($this->bucket === null) {
69 6
            return true;
70
        }
71 14
        return $this->stream === null ? false : $this->stream->eof();
72
    }
73 10
    public function isSeekable(): bool
74
    {
75 10
        return false;
76
    }
77 2
    public function seek($offset, $whence = \SEEK_SET): void
78
    {
79 2
        throw new RuntimeException('Stream is not seekable.');
80
    }
81 2
    public function rewind(): void
82
    {
83 2
        if ($this->stream !== null) {
84
            $this->stream->rewind();
85
        }
86 2
    }
87 2
    public function isWritable(): bool
88
    {
89 2
        return false;
90
    }
91 2
    public function write($string): int
92
    {
93 2
        throw new RuntimeException('Cannot write to a non-writable stream.');
94
    }
95 23
    public function isReadable(): bool
96
    {
97 23
        if ($this->matchedResult === null) {
98 13
            return false;
99
        }
100 10
        $this->initConverter();
101 10
        return $this->stream === null ? false : $this->stream->isReadable();
102
    }
103 13
    public function read($length): string
104
    {
105 13
        if (!$this->isReadable()) {
106 5
            throw new RuntimeException('Stream should be rendered.');
107
        }
108
        # read generator stream
109 8
        return $this->stream->read($length);
0 ignored issues
show
Bug introduced by
The method read() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

109
        return $this->stream->/** @scrutinizer ignore-call */ read($length);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
110
    }
111 12
    public function getContents(): string
112
    {
113 12
        if ($this->bucket === null) {
114 4
            throw new \RuntimeException('Unable to read stream contents');
115
        }
116 8
        $content = '';
117 8
        while (!$this->eof()) {
118 8
            $content .= $this->read(PHP_INT_MAX);
119
        }
120 6
        return $content;
121
    }
122 8
    public function getMetadata($key = null)
123
    {
124 8
        if ($this->bucket === null) {
125 4
            return $key ? null : [];
126
        }
127
128
        $meta = [
129 4
            'seekable' => $this->isSeekable(),
130 4
            'eof' => $this->eof(),
131
        ];
132
133 4
        if (null === $key) {
134 2
            return $meta;
135
        }
136
137 2
        return $meta[$key] ?? null;
138
    }
139
140 5
    public function withBucket(DataBucket $bucket): self
141
    {
142 5
        return new static($this->converterMatcher, $bucket);
143
    }
144
145 2
    public function hasConverter(): bool
146
    {
147 2
        return $this->matchedResult !== null;
148
    }
149 2
    public function getConverter(): ?ConverterInterface
150
    {
151 2
        return $this->matchedResult === null ? null : $this->matchedResult->getConverter();
152
    }
153
154 8
    public function hasBucketFormat(): bool
155
    {
156 8
        return $this->bucket === null ? false : $this->bucket->hasFormat();
157
    }
158 9
    public function getBucketFormat(): ?string
159
    {
160 9
        return $this->bucket === null ? null : $this->bucket->getFormat();
161
    }
162
163
    public function hasMatchedFormat(): bool
164
    {
165
        return $this->matchedResult !== null;
166
    }
167
    public function getMatchedFormat(): ?string
168
    {
169
        return $this->matchedResult === null ? null : $this->matchedResult->getFormat();
170
    }
171
172 9
    public function isRenderStarted(): bool
173
    {
174 9
        return $this->stream !== null;
175
    }
176
177 20
    public function getBucket(): ?DataBucket
178
    {
179 20
        return $this->bucket;
180
    }
181 2
    public function getMatchedResult(): ?MatchingResult
182
    {
183 2
        return $this->matchedResult;
184
    }
185
186 10
    private function initConverter(): bool
187
    {
188 10
        if ($this->stream !== null) {
189 6
            return true;
190
        }
191 10
        if ($this->matchedResult === null || $this->bucket === null) {
192
            return false;
193
        }
194 10
        $this->stream = new GeneratorStream($this->matchedResult->getConverter()->convert($this->bucket));
195 10
        return true;
196
    }
197
}
198