Passed
Push — develop ( 894dbc...ea363f )
by nguereza
01:45
created

Response::setCommonHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 10
rs 10
1
<?php
2
3
/**
4
 * Platine HTTP
5
 *
6
 * Platine HTTP Message is the implementation of PSR 7
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine HTTP
11
 * Copyright (c) 2019 Dion Chaika
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file Response.php
34
 *
35
 *  The Response class is a representation of an outgoing, server-side response.
36
 *
37
 *  @package    Platine\Http
38
 *  @author Platine Developers Team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   https://www.platine-php.com
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Http;
49
50
use InvalidArgumentException;
51
52
class Response extends Message implements ResponseInterface
53
{
54
    /**
55
     * The reason phrases.
56
     */
57
    protected const REASON_PHRASES = [
58
        //
59
        // Informational
60
        //
61
        100 => 'Continue',
62
        101 => 'Switching Protocols',
63
        102 => 'Processing',
64
        103 => 'Early Hints',
65
        //
66
        // Successful
67
        //
68
        200 => 'OK',
69
        201 => 'Created',
70
        202 => 'Accepted',
71
        203 => 'Non-Authoritative Information',
72
        204 => 'No Content',
73
        205 => 'Reset Content',
74
        206 => 'Partial Content',
75
        207 => 'Multi-Status',
76
        208 => 'Already Reported',
77
        226 => 'IM Used',
78
        //
79
        // Redirection
80
        //
81
        300 => 'Multiple Choices',
82
        301 => 'Moved Permanently',
83
        302 => 'Found',
84
        303 => 'See Other',
85
        304 => 'Not Modified',
86
        305 => 'Use Proxy',
87
        307 => 'Temporary Redirect',
88
        308 => 'Permanent Redirect',
89
        //
90
        // Client Error
91
        //
92
        400 => 'Bad Request',
93
        401 => 'Unauthorized',
94
        402 => 'Payment Required',
95
        403 => 'Forbidden',
96
        404 => 'Not Found',
97
        405 => 'Method Not Allowed',
98
        406 => 'Not Acceptable',
99
        407 => 'Proxy Authentication Required',
100
        408 => 'Request Timeout',
101
        409 => 'Conflict',
102
        410 => 'Gone',
103
        411 => 'Length Required',
104
        412 => 'Precondition Failed',
105
        413 => 'Payload Too Large',
106
        414 => 'URI Too Long',
107
        415 => 'Unsupported Media Type',
108
        416 => 'Range Not Satisfiable',
109
        417 => 'Expectation Failed',
110
        421 => 'Misdirected Request',
111
        422 => 'Unprocessable Entity',
112
        423 => 'Locked',
113
        424 => 'Failed Dependency',
114
        425 => 'Too Early',
115
        426 => 'Upgrade Required',
116
        428 => 'Precondition Required',
117
        429 => 'Too Many Requests',
118
        431 => 'Request Header Fields Too Large',
119
        451 => 'Unavailable For Legal Reasons',
120
        //
121
        // Server Error
122
        //
123
        500 => 'Internal Server Error',
124
        501 => 'Not Implemented',
125
        502 => 'Bad Gateway',
126
        503 => 'Service Unavailable',
127
        504 => 'Gateway Timeout',
128
        505 => 'HTTP Version Not Supported',
129
        506 => 'Variant Also Negotiates',
130
        507 => 'Insufficient Storage',
131
        508 => 'Loop Detected',
132
        510 => 'Not Extended',
133
        511 => 'Network Authentication Required'
134
    ];
135
136
    /**
137
     * The Response HTTP status code
138
     * @var int
139
     */
140
    protected int $statusCode = 200;
141
142
    /**
143
     * The Reason phrase
144
     * @var string
145
     */
146
    protected string $reasonPhrase = 'OK';
147
148
    /**
149
     * Create new Response instance
150
     * @param int $statusCode
151
     * @param string      $reasonPhrase
152
     */
153
    public function __construct(int $statusCode = 200, string $reasonPhrase = '')
154
    {
155
        $this->statusCode = $this->filterStatusCode($statusCode);
156
        if ($reasonPhrase === '') {
157
            $reasonPhrase = isset(static::REASON_PHRASES[$this->statusCode])
158
                    ? static::REASON_PHRASES[$this->statusCode]
159
                    : '';
160
        }
161
        $this->reasonPhrase = $reasonPhrase;
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function getStatusCode(): int
168
    {
169
        return $this->statusCode;
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function withStatus(int $code, string $reasonPhrase = ''): self
176
    {
177
        $that = clone $this;
178
        $that->statusCode = $this->filterStatusCode($code);
179
180
        if ($reasonPhrase === '') {
181
            $reasonPhrase = isset(static::REASON_PHRASES[$that->statusCode])
182
                    ? static::REASON_PHRASES[$that->statusCode]
183
                    : '';
184
        }
185
        $that->reasonPhrase = $reasonPhrase;
186
187
        return $that;
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193
    public function getReasonPhrase(): string
194
    {
195
        return $this->reasonPhrase;
196
    }
197
198
    /**
199
     * The string representation of this response
200
     * @return string
201
     */
202
    public function __toString(): string
203
    {
204
        $response = sprintf(
205
            "HTTP/%s %d %s\r\n",
206
            $this->getProtocolVersion(),
207
            $this->getStatusCode(),
208
            $this->getReasonPhrase()
209
        );
210
211
        foreach (array_keys($this->headers) as $header) {
212
            if (strtolower($header) === 'set-cookie') {
213
                foreach ($this->getHeader('Set-Cookie') as $setCookie) {
214
                    $response .= sprintf(
215
                        "%s: %s\r\n",
216
                        $header,
217
                        $setCookie
218
                    );
219
                }
220
            } else {
221
                $response .= sprintf(
222
                    "%s: %s\r\n",
223
                    $header,
224
                    $this->getHeaderLine($header)
225
                );
226
            }
227
        }
228
        return sprintf(
229
            "%s\r\n%s",
230
            $response,
231
            $this->getBody()
232
        );
233
    }
234
235
    /**
236
     * Filter status code
237
     * @param  int    $code
238
     * @return int
239
     */
240
    protected function filterStatusCode(int $code): int
241
    {
242
        if ($code === 306) {
243
            throw new InvalidArgumentException(
244
                'Invalid status code! Status code 306 is unused.'
245
            );
246
        }
247
248
        if ($code < 100 || $code > 599) {
249
            throw new InvalidArgumentException(
250
                'Invalid status code! Status code must be between 100 and 599.'
251
            );
252
        }
253
254
        return $code;
255
    }
256
}
257