Passed
Push — master ( 3d26ac...b47a92 )
by Aleksei
05:49
created

GeneratorStream::write()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

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