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

BucketStream::hasBucketFormat()   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 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 1
nc 2
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
c 2
b 0
f 0
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 42
    public function __construct(ConverterMatcherInterface $converterMatcher, DataBucket $bucket)
23
    {
24 42
        $this->bucket = $bucket;
25 42
        $this->converterMatcher = $converterMatcher;
26
27 42
        if ($bucket->isConvertable()) {
28 41
            $result = $this->converterMatcher->match($this->bucket);
29
            // if should be converted but no converter was found
30 41
            if ($result === null && $bucket->hasFormat()) {
31
                throw new ConverterNotFoundException($bucket->getFormat());
32
            }
33 41
            $this->matchedResult = $result;
34
        }
35 42
    }
36 2
    public function __toString(): string
37
    {
38
        try {
39 2
            if ($this->isSeekable()) {
40
                $this->seek(0);
41
            }
42 2
            return $this->getContents();
43 1
        } catch (\Exception $e) {
44 1
            return '';
45
        }
46
    }
47 1
    public function close(): void
48
    {
49 1
        $this->detach();
50 1
    }
51 8
    public function detach()
52
    {
53 8
        $this->matchedResult = null;
54 8
        $this->stream = null;
55 8
        $this->bucket = null;
56 8
        return null;
57
    }
58 5
    public function getSize(): ?int
59
    {
60 5
        return $this->stream === null ? null : $this->stream->getSize();
61
    }
62 4
    public function tell(): int
63
    {
64 4
        return $this->stream === null ? 0 : $this->stream->tell();
65
    }
66 12
    public function eof(): bool
67
    {
68 12
        if ($this->bucket === null) {
69 3
            return true;
70
        }
71 9
        return $this->stream === null ? false : $this->stream->eof();
72
    }
73 5
    public function isSeekable(): bool
74
    {
75 5
        return false;
76
    }
77 1
    public function seek($offset, $whence = \SEEK_SET): void
78
    {
79 1
        throw new RuntimeException('Stream is not seekable.');
80
    }
81 1
    public function rewind(): void
82
    {
83 1
        if ($this->stream !== null) {
84
            $this->stream->rewind();
85
        }
86 1
    }
87 1
    public function isWritable(): bool
88
    {
89 1
        return false;
90
    }
91 1
    public function write($string): int
92
    {
93 1
        throw new RuntimeException('Cannot write to a non-writable stream.');
94
    }
95 13
    public function isReadable(): bool
96
    {
97 13
        if ($this->matchedResult === null) {
98 4
            return false;
99
        }
100 9
        $this->initConverter();
101 9
        return $this->stream === null ? false : $this->stream->isReadable();
102
    }
103 9
    public function read($length): string
104
    {
105 9
        if (!$this->isReadable()) {
106 1
            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 8
    public function getContents(): string
112
    {
113 8
        if ($this->bucket === null) {
114 2
            throw new \RuntimeException('Unable to read stream contents');
115
        }
116 6
        $content = '';
117 6
        while (!$this->eof()) {
118 6
            $content .= $this->read(PHP_INT_MAX);
119
        }
120 6
        return $content;
121
    }
122 4
    public function getMetadata($key = null)
123
    {
124 4
        if ($this->bucket === null) {
125 2
            return $key ? null : [];
126
        }
127
128
        $meta = [
129 2
            'seekable' => $this->isSeekable(),
130 2
            'eof' => $this->eof(),
131
        ];
132
133 2
        if (null === $key) {
134 1
            return $meta;
135
        }
136
137 1
        return $meta[$key] ?? null;
138
    }
139
140 4
    public function withBucket(DataBucket $bucket): self
141
    {
142 4
        return new static($this->converterMatcher, $bucket);
143
    }
144
145
    public function hasConverter(): bool
146
    {
147
        return $this->matchedResult !== null;
148
    }
149
    public function getConverter(): ?ConverterInterface
150
    {
151
        return $this->matchedResult === null ? null : $this->matchedResult->getConverter();
152
    }
153
154 4
    public function hasBucketFormat(): bool
155
    {
156 4
        return $this->bucket === null ? false : $this->bucket->hasFormat();
157
    }
158 5
    public function getBucketFormat(): ?string
159
    {
160 5
        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 5
    public function isRenderStarted(): bool
173
    {
174 5
        return $this->stream !== null;
175
    }
176
177 15
    public function getBucket(): ?DataBucket
178
    {
179 15
        return $this->bucket;
180
    }
181
    public function getMatchedResult(): ?MatchingResult
182
    {
183
        return $this->matchedResult;
184
    }
185
186 9
    private function initConverter(): bool
187
    {
188 9
        if ($this->stream !== null) {
189 6
            return true;
190
        }
191 9
        if ($this->matchedResult === null || $this->bucket === null) {
192
            return false;
193
        }
194 9
        $this->stream = new GeneratorStream($this->matchedResult->getConverter()->convert($this->bucket));
195 9
        return true;
196
    }
197
}
198