Message::withProtocolVersion()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Linna Http Message.
5
 *
6
 * @author Sebastian Rapetti <[email protected]>
7
 * @copyright (c) 2019, Sebastian Rapetti
8
 * @license http://opensource.org/licenses/MIT MIT License
9
 */
10
declare(strict_types=1);
11
12
namespace Linna\Http\Message;
13
14
use InvalidArgumentException;
15
use Linna\TypedArray;
16
use Psr\Http\Message\MessageInterface;
17
use Psr\Http\Message\StreamInterface;
18
19
/**
20
 * PSR-7 Message implementation.
21
 */
22
abstract class Message implements MessageInterface
23
{
24
    /**
25
     * @var string Protocol version.
26
     */
27
    protected $protocolVersion = '1.1';
28
29
    /**
30
     * @var array Message headers .
31
     */
32
    protected $headers = [];
33
34
    /**
35
     * @var StreamInterface Body of the message.
36
     */
37
    protected $body;
38
39
    /**
40
     * Retrieves the HTTP protocol version as a string.
41
     *
42
     * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
43
     *
44
     * @return string HTTP protocol version.
45
     */
46 2
    public function getProtocolVersion(): string
47
    {
48 2
        return $this->protocolVersion;
49
    }
50
51
    /**
52
     * Return an instance with the specified HTTP protocol version.
53
     *
54
     * The version string MUST contain only the HTTP version number (e.g.,
55
     * "1.1", "1.0").
56
     *
57
     * This method MUST be implemented in such a way as to retain the
58
     * immutability of the message, and MUST return an instance that has the
59
     * new protocol version.
60
     *
61
     * @param string $version HTTP protocol version
62
     *
63
     * @return MessageInterface
64
     *
65
     * @throws InvalidArgumentException if provided protocol version is invalid.
66
     */
67 2
    public function withProtocolVersion(string $version): MessageInterface
68
    {
69 2
        if (\in_array($version, ['1.0', '1.1', '2'], true)) {
70 1
            $new = clone $this;
71 1
            $new->protocolVersion = $version;
72
73 1
            return $new;
74
        }
75
76 1
        throw new InvalidArgumentException('Invalid HTTP protocol version. Must be 1.0, 1.1 or 2');
77
    }
78
79
    /**
80
     * Retrieves all message header values.
81
     *
82
     * The keys represent the header name as it will be sent over the wire, and
83
     * each value is an array of strings associated with the header.
84
     *
85
     *     // Represent the headers as a string
86
     *     foreach ($message->getHeaders() as $name => $values) {
87
     *         echo $name . ": " . implode(", ", $values);
88
     *     }
89
     *
90
     *     // Emit headers iteratively:
91
     *     foreach ($message->getHeaders() as $name => $values) {
92
     *         foreach ($values as $value) {
93
     *             header(sprintf('%s: %s', $name, $value), false);
94
     *         }
95
     *     }
96
     *
97
     * While header names are not case-sensitive, getHeaders() will preserve the
98
     * exact case in which headers were originally specified.
99
     *
100
     * @return string[][] Returns an associative array of the message's headers.
101
     *                    Each key MUST be a header name, and each value MUST be
102
     *                    an array of strings for that header.
103
     */
104 11
    public function getHeaders(): array
105
    {
106 11
        return $this->headers;
107
    }
108
109
    /**
110
     * Checks if a header exists by the given case-insensitive name.
111
     *
112
     * @param string $name Case-insensitive header field name.
113
     *
114
     * @return bool Returns true if any header names match the given header name
115
     *              using a case-insensitive string comparison. Returns false if
116
     *              no matching header name is found in the message.
117
     */
118 4
    public function hasHeader(string $name): bool
119
    {
120 4
        $this->normalize($name);
121
122 4
        return isset($this->headers[$name]);
123
    }
124
125
    /**
126
     * Retrieves a message header value by the given case-insensitive name.
127
     *
128
     * This method returns an array of all the header values of the given
129
     * case-insensitive header name.
130
     *
131
     * If the header does not appear in the message, this method MUST return an
132
     * empty array.
133
     *
134
     * @param string $name Case-insensitive header field name.
135
     *
136
     * @return string[] An array of string values as provided for the given header.
137
     *                  If the header does not appear in the message, this method MUST
138
     *                  return an empty array.
139
     */
140 1
    public function getHeader(string $name): array
141
    {
142 1
        $this->normalize($name);
143
144 1
        return isset($this->headers[$name]) ? $this->headers[$name] : [];
145
    }
146
147
    /**
148
     * Retrieves a comma-separated string of the values for a single header.
149
     *
150
     * This method returns all of the header values of the given
151
     * case-insensitive header name as a string concatenated together using
152
     * a comma.
153
     *
154
     * NOTE: Not all header values may be appropriately represented using
155
     * comma concatenation. For such headers, use getHeader() instead
156
     * and supply your own delimiter when concatenating.
157
     *
158
     * If the header does not appear in the message, this method MUST return
159
     * an empty string.
160
     *
161
     * @param string $name Case-insensitive header field name.
162
     *
163
     * @return string A string of values as provided for the given header concatenated
164
     *                together using a comma. If the header does not appear in
165
     *                the message, this method MUST return an empty string.
166
     */
167 1
    public function getHeaderLine(string $name): string
168
    {
169 1
        $this->normalize($name);
170
171 1
        return isset($this->headers[$name]) ? \implode(', ', $this->headers[$name]) : '';
172
    }
173
174
    /**
175
     * Return an instance with the provided value replacing the specified header.
176
     *
177
     * While header names are case-insensitive, the casing of the header will
178
     * be preserved by this function, and returned from getHeaders().
179
     *
180
     * This method MUST be implemented in such a way as to retain the
181
     * immutability of the message, and MUST return an instance that has the
182
     * new and/or updated header and value.
183
     *
184
     * @param string $name Case-insensitive header field name.
185
     * @param string[] $value Header value(s).
186
     *
187
     * @return static
188
     *
189
     * @throws InvalidArgumentException for invalid header names or values.
190
     */
191 11
    public function withHeader(string $name, array $value): MessageInterface
192
    {
193 11
        $this->normalize($name);
194
195 11
        $new = clone $this;
196
197
        //use typed array for assicure that array contains only strings
198 11
        $new->headers[$name] = (new TypedArray('string', $value))->getArrayCopy();
199
200 11
        return $new;
201
    }
202
203
    /**
204
     * Return an instance with the specified header appended with the given value.
205
     *
206
     * Existing values for the specified header will be maintained. The new
207
     * value(s) will be appended to the existing list. If the header did not
208
     * exist previously, it will be added.
209
     *
210
     * This method MUST be implemented in such a way as to retain the
211
     * immutability of the message, and MUST return an instance that has the
212
     * new header and/or value.
213
     *
214
     * @param string $name Case-insensitive header field name to add.
215
     * @param string[] $value Header value(s).
216
     *
217
     * @return static
218
     *
219
     * @throws InvalidArgumentException for invalid header names or values.
220
     */
221 6
    public function withAddedHeader(string $name, array $value): MessageInterface
222
    {
223 6
        $this->normalize($name);
224
225
        //use typed array for assicure that array contains only strings
226 6
        $headerValue = (new TypedArray('string', $value))->getArrayCopy();
227
228 6
        $new = clone $this;
229
230
        //check if header exist
231 6
        if (!isset($this->headers[$name])) {
232 1
            $new->headers[$name] = $headerValue;
233
234 1
            return $new;
235
        }
236
237
        //at this point header exists
238
        //remain only to append new value to existing header
239 6
        $new->headers[$name] = \array_merge($this->headers[$name], $headerValue);
240
241 6
        return $new;
242
    }
243
244
    /**
245
     * Return an instance without the specified header.
246
     *
247
     * Header resolution MUST be done without case-sensitivity.
248
     *
249
     * This method MUST be implemented in such a way as to retain the
250
     * immutability of the message, and MUST return an instance that removes
251
     * the named header.
252
     *
253
     * @param string $name Case-insensitive header field name to remove.
254
     *
255
     * @return static
256
     */
257 5
    public function withoutHeader(string $name): MessageInterface
258
    {
259 5
        $this->normalize($name);
260
261 5
        $new = clone $this;
262 5
        unset($new->headers[$name]);
263
264 5
        return $new;
265
    }
266
267
    /**
268
     * Gets the body of the message.
269
     *
270
     * @return StreamInterface Returns the body as a stream.
271
     */
272 1
    public function getBody(): StreamInterface
273
    {
274 1
        return $this->body;
275
    }
276
277
    /**
278
     * Return an instance with the specified message body.
279
     *
280
     * The body MUST be a StreamInterface object.
281
     *
282
     * This method MUST be implemented in such a way as to retain the
283
     * immutability of the message, and MUST return a new instance that has the
284
     * new body stream.
285
     *
286
     * @param StreamInterface $body Body.
287
     *
288
     * @return static
289
     *
290
     * @throws InvalidArgumentException When the body is not valid.
291
     */
292 1
    public function withBody(StreamInterface $body): MessageInterface
293
    {
294 1
        $new = clone $this;
295 1
        $new->body = $body;
296
297 1
        return $new;
298
    }
299
300
    /**
301
     * Normalize header name for case-unsensitive search.
302
     *
303
     * @param string $headerName
304
     *
305
     * @return void
306
     */
307 14
    private function normalize(string &$headerName): void
308
    {
309 14
        $headerName = \ucwords(\strtolower($headerName), '-');
310 14
    }
311
}
312