Response::getBody()   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
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 1
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Conia\Chuck;
6
7
use Conia\Chuck\Exception\HttpNotFound;
8
use Conia\Chuck\Exception\RuntimeException;
9
use Conia\Chuck\Factory;
10
use Conia\Chuck\Http\WrapsMessage;
11
use finfo;
12
use Psr\Http\Message\ResponseInterface as PsrResponse;
13
use Psr\Http\Message\StreamInterface as PsrStream;
14
use Traversable;
15
16
/** @psalm-api */
17
class Response implements ResponseWrapper
18
{
19
    use WrapsMessage;
20
21 66
    public function __construct(
22
        protected PsrResponse $psr,
23
        protected readonly Factory|null $factory = null,
24
    ) {
25 66
    }
26
27 51
    public static function fromFactory(Factory $factory): self
28
    {
29 51
        return new self($factory->response(), $factory);
30
    }
31
32 31
    public function psr(): PsrResponse
33
    {
34 31
        return $this->psr;
35
    }
36
37 1
    public function setPsr(PsrResponse $psr): static
38
    {
39 1
        $this->psr = $psr;
40
41 1
        return $this;
42
    }
43
44 10
    public function status(int $statusCode, ?string $reasonPhrase = null): static
45
    {
46 10
        if (empty($reasonPhrase)) {
47 9
            $this->psr = $this->psr->withStatus($statusCode);
48
        } else {
49 1
            $this->psr = $this->psr->withStatus($statusCode, $reasonPhrase);
50
        }
51
52 10
        return $this;
53
    }
54
55 5
    public function getStatusCode(): int
56
    {
57 5
        return $this->psr->getStatusCode();
58
    }
59
60 3
    public function getReasonPhrase(): string
61
    {
62 3
        return $this->psr->getReasonPhrase();
63
    }
64
65 1
    public function protocolVersion(string $protocol): static
66
    {
67 1
        $this->psr = $this->psr->withProtocolVersion($protocol);
68
69 1
        return $this;
70
    }
71
72 17
    public function header(string $name, string $value): static
73
    {
74 17
        $this->psr = $this->psr->withAddedHeader($name, $value);
75
76 17
        return $this;
77
    }
78
79 1
    public function removeHeader(string $name): static
80
    {
81 1
        $this->psr = $this->psr->withoutHeader($name);
82
83 1
        return $this;
84
    }
85
86 3
    public function headers(): array
87
    {
88 3
        return $this->psr->getHeaders();
89
    }
90
91 14
    public function getHeader(string $name): array
92
    {
93 14
        return $this->psr->getHeader($name);
94
    }
95
96 3
    public function hasHeader(string $name): bool
97
    {
98 3
        return $this->psr->hasHeader($name);
99
    }
100
101 13
    public function body(PsrStream|string $body): static
102
    {
103 13
        if ($body instanceof PsrStream) {
104 1
            $this->psr = $this->psr->withBody($body);
105
106 1
            return $this;
107
        }
108
109 12
        if ($this->factory) {
110 11
            $this->psr = $this->psr->withBody($this->factory->stream($body));
111
112 11
            return $this;
113
        }
114
115 1
        throw new RuntimeException('No factory instance set in response object');
116
    }
117
118 19
    public function getBody(): PsrStream
119
    {
120 19
        return $this->psr->getBody();
121
    }
122
123 3
    public function write(string $content): static
124
    {
125 3
        $this->psr->getBody()->write($content);
126
127 3
        return $this;
128
    }
129
130 2
    public function redirect(string $url, int $code = 302): static
131
    {
132 2
        $this->header('Location', $url);
133 2
        $this->status($code);
134
135 2
        return $this;
136
    }
137
138
    /**
139
     * @param null|PsrStream|resource|string $body
140
     */
141 34
    public function withContentType(
142
        string $contentType,
143
        mixed $body = null,
144
        int $code = 200,
145
        string $reasonPhrase = ''
146
    ): static {
147 34
        $this->psr = $this->psr
148 34
            ->withStatus($code, $reasonPhrase)
149 34
            ->withAddedHeader('Content-Type', $contentType);
150
151 34
        if ($body) {
152 33
            assert(isset($this->factory));
153 33
            $this->psr = $this->psr->withBody($this->factory->stream($body));
0 ignored issues
show
Bug introduced by
The method stream() 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

153
            $this->psr = $this->psr->withBody($this->factory->/** @scrutinizer ignore-call */ stream($body));

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...
154
        }
155
156 33
        return $this;
157
    }
158
159
    /**
160
     * @param null|PsrStream|resource|string $body
161
     */
162 15
    public function html(mixed $body = null, int $code = 200, string $reasonPhrase = ''): static
163
    {
164 15
        return $this->withContentType('text/html', $body, $code, $reasonPhrase);
165
    }
166
167
    /**
168
     * @param null|PsrStream|resource|string $body
169
     */
170 12
    public function text(mixed $body = null, int $code = 200, string $reasonPhrase = ''): static
171
    {
172 12
        return $this->withContentType('text/plain', $body, $code, $reasonPhrase);
173
    }
174
175 7
    public function json(
176
        mixed $data,
177
        int $code = 200,
178
        string $reasonPhrase = '',
179
        int $flags = JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR,
180
    ): static {
181 7
        if ($data instanceof Traversable) {
182 1
            $body = json_encode(iterator_to_array($data), $flags);
183
        } else {
184 6
            $body = json_encode($data, $flags);
185
        }
186
187 7
        return $this->withContentType('application/json', $body, $code, $reasonPhrase);
188
    }
189
190 6
    public function file(
191
        string $file,
192
        bool $throwNotFound = true,
193
        int $code = 200,
194
        string $reasonPhrase = '',
195
    ): static {
196 6
        $this->validateFile($file, $throwNotFound);
197
198 4
        $finfo = new finfo(FILEINFO_MIME_TYPE);
199 4
        $contentType = $finfo->file($file);
200 4
        $finfo = new finfo(FILEINFO_MIME_ENCODING);
201 4
        $encoding = $finfo->file($file);
202 4
        assert(isset($this->factory));
203 4
        $stream = $this->factory->streamFromFile($file, 'rb');
204
205 4
        $this->psr = $this->psr
206 4
            ->withStatus($code, $reasonPhrase)
207 4
            ->withAddedHeader('Content-Type', $contentType)
208 4
            ->withAddedHeader('Content-Transfer-Encoding', $encoding)
209 4
            ->withBody($stream);
210
211 4
        $size = $stream->getSize();
212
213 4
        if (!is_null($size)) {
214 4
            $this->psr = $this->psr->withAddedHeader('Content-Length', (string)$size);
215
        }
216
217 4
        return $this;
218
    }
219
220 3
    public function download(
221
        string $file,
222
        string $newName = '',
223
        bool $throwNotFound = true,
224
        int $code = 200,
225
        string $reasonPhrase = '',
226
    ): static {
227 3
        $response = $this->file($file, $throwNotFound, $code, $reasonPhrase);
228 3
        $response->header(
229 3
            'Content-Disposition',
230 3
            'attachment; filename="' . ($newName ?: basename($file)) . '"'
231 3
        );
232
233 3
        return $response;
234
    }
235
236 1
    public function sendfile(
237
        string $file,
238
        bool $throwNotFound = true,
239
        int $code = 200,
240
        string $reasonPhrase = '',
241
    ): static {
242 1
        $this->validateFile($file, $throwNotFound);
243 1
        $server = strtolower($_SERVER['SERVER_SOFTWARE'] ?? '');
244 1
        $this->psr = $this->psr->withStatus($code, $reasonPhrase);
245
246 1
        if (strpos($server, 'nginx') !== false) {
247 1
            $this->psr = $this->psr->withAddedHeader('X-Accel-Redirect', $file);
248
        } else {
249 1
            $this->psr = $this->psr->withAddedHeader('X-Sendfile', $file);
250
        }
251
252 1
        return $this;
253
    }
254
255 7
    protected function validateFile(string $file, bool $throwNotFound): void
256
    {
257 7
        if (!is_file($file)) {
258 2
            if ($throwNotFound) {
259 1
                throw new HttpNotFound();
260
            }
261
262 1
            throw new RuntimeException('File not found');
263
        }
264
    }
265
}
266