Passed
Pull Request — master (#75)
by
unknown
11:38
created

DataStream::eof()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 3
nc 3
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Stream;
6
7
use App\Stream\Data\Converter;
8
use Psr\Http\Message\StreamInterface;
9
10
final class DataStream implements StreamInterface
11
{
12
    /** @var mixed */
13
    private $data;
14
    private bool $readable = false;
15
    private ?int $size = null;
16
    private bool $rendered = false;
17
    private string $buffer = '';
18
    private int $caret = 0;
19
20
    /** @var string|null Class name */
21
    private ?string $converter = null;
22
    private array $converterParams = [];
23
24
    public function __construct($body)
25
    {
26
        $this->data = $body;
27
    }
28
    public function __toString(): string
29
    {
30
        try {
31
            if ($this->isSeekable()) {
32
                $this->seek(0);
33
            }
34
            return $this->getContents();
35
        } catch (\Exception $e) {
36
            return '';
37
        }
38
    }
39
    public function close(): void
40
    {
41
        if (isset($this->data)) {
42
            $this->detach();
43
        }
44
    }
45
    public function detach()
46
    {
47
        $result = $this->data;
48
        unset($this->data);
49
        $this->size = null;
50
        $this->caret = 0;
51
        $this->buffer = '';
52
        $this->rendered = false;
53
        $this->readable = false;
54
        return $result;
55
    }
56
    public function getSize(): ?int
57
    {
58
        return $this->size;
59
    }
60
    public function tell(): int
61
    {
62
        return $this->caret;
63
    }
64
    public function eof(): bool
65
    {
66
        return $this->data === null || $this->rendered && $this->caret >= $this->size;
67
    }
68
    public function isSeekable(): bool
69
    {
70
        return $this->rendered;
71
    }
72
    public function seek($offset, $whence = \SEEK_SET): void
73
    {
74
        if (!$this->isSeekable()) {
75
            throw new \RuntimeException('Stream is not seekable.');
76
        }
77
        switch ($whence) {
78
            case SEEK_SET:
79
                $position = $whence;
80
                break;
81
            case SEEK_CUR:
82
                $position = $this->caret + $whence;
83
                break;
84
            case SEEK_END:
85
                $position = $this->size + $whence;
86
                break;
87
            default:
88
                throw new \InvalidArgumentException('Unsupported whence value.');
89
        }
90
        if ($position > $this->size || $position < 0) {
91
            throw new \RuntimeException('Impossible offset');
92
        }
93
        $this->caret = $position;
94
    }
95
    public function rewind(): void
96
    {
97
        $this->caret = 0;
98
    }
99
    public function isWritable(): bool
100
    {
101
        return false;
102
    }
103
    public function write($string): int
104
    {
105
        if (!$this->isWritable()) {
106
            throw new \RuntimeException('Cannot write to a non-writable stream.');
107
        }
108
        return 0;
109
    }
110
    public function isReadable(): bool
111
    {
112
        return $this->readable;
113
    }
114
    public function read($length): string
115
    {
116
        if (!$this->isReadable()) {
117
            throw new \RuntimeException('Stream should be rendered.');
118
        }
119
        if ($this->eof()) {
120
            throw new \RuntimeException('Cannot read from ended stream.');
121
        }
122
        $result = substr($this->buffer, $this->caret, $length);
123
        $this->caret += strlen($result);
124
        return $result;
125
    }
126
    public function getContents(): string
127
    {
128
        if (!isset($this->data)) {
129
            throw new \RuntimeException('Unable to read stream contents.');
130
        }
131
        $content = '';
132
        while (!$this->eof()) {
133
            $content .= $this->read(PHP_INT_MAX);
134
        }
135
        return $content;
136
    }
137
    public function getMetadata($key = null)
138
    {
139
        if (!isset($this->data)) {
140
            return $key ? null : [];
141
        }
142
143
        $meta = [
144
            'seekable' => $this->isSeekable(),
145
            'eof' => $this->eof(),
146
        ];
147
148
        if (null === $key) {
149
            return $meta;
150
        }
151
152
        return $meta[$key] ?? null;
153
    }
154
155
    public function render(Converter $converter): void
156
    {
157
        $this->buffer = $converter->convert($this->data, $this->converterParams);
158
        $this->readable = true;
159
        $this->rendered = true;
160
        $this->caret = 0;
161
        $this->size = strlen($this->buffer);
162
    }
163
    public function setConverter(?string $converter, array $params = []): self
164
    {
165
        $this->converter = $converter;
166
        $this->converterParams = $params;
167
        return $this;
168
    }
169
    public function getConverter(): ?string
170
    {
171
        return $this->converter;
172
    }
173
}
174