Passed
Push — main ( 682960...085d3b )
by Thomas
02:23
created

Response::removeHeader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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

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