GeneratorStream::write()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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