Message::getBody()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of slick/http
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Slick\Http\Message;
13
14
use InvalidArgumentException;
15
use Psr\Http\Message\MessageInterface;
16
use Psr\Http\Message\StreamInterface;
17
use Slick\Http\Message\Stream\TextStream;
18
19
/**
20
 * Message
21
 *
22
 * @package Slick\Http\Message
23
*/
24
class Message implements MessageInterface
25
{
26
    /**
27
     * @var string
28
     */
29
    private string $protocolVersion = '1.1';
30
31
    /**
32
     * @var array<string, string[]>
33
     */
34
    protected array $headers = [];
35
36
    /**
37
     * @var StreamInterface
38
     */
39
    protected StreamInterface $body;
40
41
    /**
42
     * Creates an HTTP Message
43
     *
44
     * @param string|StreamInterface $body
45
     */
46
    public function __construct(string|StreamInterface $body = '')
47
    {
48
        $body = $body instanceof StreamInterface
49
            ? $body
50
            : new TextStream($body);
51
        $this->body = $body;
52
    }
53
54
    /**
55
     * Retrieves the HTTP protocol version as a string.
56
     *
57
     * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
58
     *
59
     * @return string HTTP protocol version.
60
     */
61
    public function getProtocolVersion(): string
62
    {
63
        return $this->protocolVersion;
64
    }
65
66
    /**
67
     * Return an instance with the specified HTTP protocol version.
68
     *
69
     * The version string MUST contain only the HTTP version number (e.g.,
70
     * "1.1", "1.0").
71
     *
72
     * This method MUST be implemented in such a way as to retain the
73
     * immutability of the message, and MUST return an instance that has the
74
     * new protocol version.
75
     *
76
     * @param string $version HTTP protocol version
77
     * @return static
78
     */
79
    public function withProtocolVersion(string $version): MessageInterface
80
    {
81
        $message = clone $this;
82
        $message->protocolVersion = $version;
83
        return $message;
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 array<string, string[]> Returns an associative array of the message's headers. Each
108
     *     key MUST be a header name, and each value MUST be an array of strings
109
     *     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
     * @return bool Returns true if any header names match the given header
121
     *     name using a case-insensitive string comparison. Returns false if
122
     *     no matching header name is found in the message.
123
     */
124
    public function hasHeader(string $name): bool
125
    {
126
        $key = $this->headerKey($name);
127
        return \array_key_exists($key, $this->headers);
128
    }
129
130
    /**
131
     * Retrieves a message header value by the given case-insensitive name.
132
     *
133
     * This method returns an array of all the header values of the given
134
     * case-insensitive header name.
135
     *
136
     * If the header does not appear in the message, this method MUST return an
137
     * empty array.
138
     *
139
     * @param string $name Case-insensitive header field name.
140
     * @return string[] An array of string values as provided for the given
141
     *    header. If the header does not appear in the message, this method MUST
142
     *    return an empty array.
143
     */
144
    public function getHeader(string $name): array
145
    {
146
        $key = $this->headerKey($name);
147
        if (\array_key_exists($key, $this->headers)) {
148
            return $this->headers[$key];
149
        }
150
151
        return [];
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
     * @return string A string of values as provided for the given header
170
     *    concatenated together using a comma. If the header does not appear in
171
     *    the message, this method MUST return an empty string.
172
     */
173
    public function getHeaderLine(string $name): string
174
    {
175
        $header = $this->getHeader($name);
176
        return empty($header)
177
            ? ''
178
            : implode(',', $header);
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|string[] $value Header value(s).
193
     * @return static
194
     * @throws InvalidArgumentException for invalid header names or values.
195
     */
196
    public function withHeader(string $name, $value): MessageInterface
197
    {
198
        $key = $this->headerKey($name);
199
200
        $message = clone $this;
201
        $message->headers[$key] = \is_array($value) ? $value : [$value];
202
        return $message;
203
    }
204
205
    /**
206
     * Return an instance with the specified header appended with the given value.
207
     *
208
     * Existing values for the specified header will be maintained. The new
209
     * value(s) will be appended to the existing list. If the header did not
210
     * exist previously, it will be added.
211
     *
212
     * This method MUST be implemented in such a way as to retain the
213
     * immutability of the message, and MUST return an instance that has the
214
     * new header and/or value.
215
     *
216
     * @param string $name Case-insensitive header field name to add.
217
     * @param string|string[] $value Header value(s).
218
     * @return static
219
     * @throws InvalidArgumentException for invalid header names or values.
220
     */
221
    public function withAddedHeader(string $name, $value): MessageInterface
222
    {
223
        $header = $this->getHeader($name);
224
        $values = \is_array($value) ? $value : [$value];
225
        foreach ($values as $newValue) {
226
            $header[] = $newValue;
227
        }
228
        return $this->withHeader($name, $header);
229
    }
230
231
    /**
232
     * Return an instance without the specified header.
233
     *
234
     * Header resolution MUST be done without case-sensitivity.
235
     *
236
     * This method MUST be implemented in such a way as to retain the
237
     * immutability of the message, and MUST return an instance that removes
238
     * the named header.
239
     *
240
     * @param string $name Case-insensitive header field name to remove.
241
     * @return static
242
     */
243
    public function withoutHeader(string $name): MessageInterface
244
    {
245
        $message = clone $this;
246
        $key = $this->headerKey($name);
247
248
        if (\array_key_exists($key, $message->headers)) {
249
            unset($message->headers[$key]);
250
        }
251
252
        return $message;
253
    }
254
255
    /**
256
     * Gets the body of the message.
257
     *
258
     * @return StreamInterface Returns the body as a stream.
259
     */
260
    public function getBody(): StreamInterface
261
    {
262
        return $this->body;
263
    }
264
265
    /**
266
     * Return an instance with the specified message body.
267
     *
268
     * The body MUST be a StreamInterface object.
269
     *
270
     * This method MUST be implemented in such a way as to retain the
271
     * immutability of the message, and MUST return a new instance that has the
272
     * new body stream.
273
     *
274
     * @param StreamInterface $body Body.
275
     * @return static
276
     * @throws InvalidArgumentException When the body is not valid.
277
     */
278
    public function withBody(StreamInterface $body): MessageInterface
279
    {
280
        $message = clone $this;
281
        $message->body = $body;
282
        return $message;
283
    }
284
285
    /**
286
     * Get the original (preserved) header name for provided name
287
     *
288
     * @param string $name
289
     * @return string
290
     */
291
    protected function headerKey(string $name): string
292
    {
293
        $key = $name;
294
        foreach (array_keys($this->headers) as $existingName) {
295
            if (strtolower($name) == strtolower($existingName)) {
296
                $key = $existingName;
297
                break;
298
            }
299
        }
300
        return $key;
301
    }
302
}
303