Completed
Push — master ( 56c359...717fd1 )
by Sebastian
04:15 queued 01:56
created

Message::withBody()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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