Passed
Pull Request — master (#31)
by Anatoly
39:30
created
src/Stream/FileStream.php 1 patch
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -31,30 +31,30 @@
 block discarded – undo
31 31
 final class FileStream extends Stream
32 32
 {
33 33
 
34
-    /**
35
-     * Constructor of the class
36
-     *
37
-     * @param string $filename
38
-     * @param string $mode
39
-     *
40
-     * @throws RuntimeException
41
-     */
42
-    public function __construct(string $filename, string $mode)
43
-    {
44
-        try {
45
-            $resource = fopen($filename, $mode);
46
-        } catch (Throwable $e) {
47
-            $resource = false;
48
-        }
49
-
50
-        if (!is_resource($resource)) {
51
-            throw new RuntimeException(sprintf(
52
-                'Unable to open the file "%s" in the mode "%s"',
53
-                $filename,
54
-                $mode
55
-            ));
56
-        }
57
-
58
-        parent::__construct($resource);
59
-    }
34
+	/**
35
+	 * Constructor of the class
36
+	 *
37
+	 * @param string $filename
38
+	 * @param string $mode
39
+	 *
40
+	 * @throws RuntimeException
41
+	 */
42
+	public function __construct(string $filename, string $mode)
43
+	{
44
+		try {
45
+			$resource = fopen($filename, $mode);
46
+		} catch (Throwable $e) {
47
+			$resource = false;
48
+		}
49
+
50
+		if (!is_resource($resource)) {
51
+			throw new RuntimeException(sprintf(
52
+				'Unable to open the file "%s" in the mode "%s"',
53
+				$filename,
54
+				$mode
55
+			));
56
+		}
57
+
58
+		parent::__construct($resource);
59
+	}
60 60
 }
Please login to merge, or discard this patch.
src/Stream/TempFileStream.php 1 patch
Indentation   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -32,32 +32,32 @@
 block discarded – undo
32 32
 final class TempFileStream extends Stream
33 33
 {
34 34
 
35
-    /**
36
-     * Constructor of the class
37
-     *
38
-     * @param string|null $prefix
39
-     *
40
-     * @throws RuntimeException
41
-     */
42
-    public function __construct(?string $prefix = null)
43
-    {
44
-        $prefix ??= 'sunrisephp';
45
-
46
-        $dirname = sys_get_temp_dir();
47
-        if (!is_writable($dirname)) {
48
-            throw new RuntimeException('Temporary files directory is not writable');
49
-        }
50
-
51
-        $filename = tempnam($dirname, $prefix);
52
-        if ($filename === false) {
53
-            throw new RuntimeException('Temporary file name cannot be generated');
54
-        }
55
-
56
-        $resource = fopen($filename, 'w+b');
57
-        if (!is_resource($resource)) {
58
-            throw new RuntimeException('Temporary file cannot be created or opened');
59
-        }
60
-
61
-        parent::__construct($resource);
62
-    }
35
+	/**
36
+	 * Constructor of the class
37
+	 *
38
+	 * @param string|null $prefix
39
+	 *
40
+	 * @throws RuntimeException
41
+	 */
42
+	public function __construct(?string $prefix = null)
43
+	{
44
+		$prefix ??= 'sunrisephp';
45
+
46
+		$dirname = sys_get_temp_dir();
47
+		if (!is_writable($dirname)) {
48
+			throw new RuntimeException('Temporary files directory is not writable');
49
+		}
50
+
51
+		$filename = tempnam($dirname, $prefix);
52
+		if ($filename === false) {
53
+			throw new RuntimeException('Temporary file name cannot be generated');
54
+		}
55
+
56
+		$resource = fopen($filename, 'w+b');
57
+		if (!is_resource($resource)) {
58
+			throw new RuntimeException('Temporary file cannot be created or opened');
59
+		}
60
+
61
+		parent::__construct($resource);
62
+	}
63 63
 }
Please login to merge, or discard this patch.
src/Stream/TmpfileStream.php 1 patch
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -35,23 +35,23 @@
 block discarded – undo
35 35
 final class TmpfileStream extends Stream
36 36
 {
37 37
 
38
-    /**
39
-     * Constructor of the class
40
-     *
41
-     * @throws RuntimeException
42
-     */
43
-    public function __construct()
44
-    {
45
-        $dirname = sys_get_temp_dir();
46
-        if (!is_writable($dirname)) {
47
-            throw new RuntimeException('Temporary files directory is not writable');
48
-        }
49
-
50
-        $resource = tmpfile();
51
-        if (!is_resource($resource)) {
52
-            throw new RuntimeException('Temporary file cannot be created or opened');
53
-        }
54
-
55
-        parent::__construct($resource);
56
-    }
38
+	/**
39
+	 * Constructor of the class
40
+	 *
41
+	 * @throws RuntimeException
42
+	 */
43
+	public function __construct()
44
+	{
45
+		$dirname = sys_get_temp_dir();
46
+		if (!is_writable($dirname)) {
47
+			throw new RuntimeException('Temporary files directory is not writable');
48
+		}
49
+
50
+		$resource = tmpfile();
51
+		if (!is_resource($resource)) {
52
+			throw new RuntimeException('Temporary file cannot be created or opened');
53
+		}
54
+
55
+		parent::__construct($resource);
56
+	}
57 57
 }
Please login to merge, or discard this patch.
src/Enum/CookieSameSite.php 1 patch
Indentation   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -19,33 +19,33 @@
 block discarded – undo
19 19
 final class CookieSameSite
20 20
 {
21 21
 
22
-    /**
23
-     * Cookies are not sent on normal cross-site subrequests, but are
24
-     * sent when a user is navigating to the origin site.
25
-     *
26
-     * This is the default cookie value if SameSite has not been
27
-     * explicitly specified in recent browser versions.
28
-     *
29
-     * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax
30
-     */
31
-    public const LAX = 'Lax';
22
+	/**
23
+	 * Cookies are not sent on normal cross-site subrequests, but are
24
+	 * sent when a user is navigating to the origin site.
25
+	 *
26
+	 * This is the default cookie value if SameSite has not been
27
+	 * explicitly specified in recent browser versions.
28
+	 *
29
+	 * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax
30
+	 */
31
+	public const LAX = 'Lax';
32 32
 
33
-    /**
34
-     * Cookies will only be sent in a first-party context and not be
35
-     * sent along with requests initiated by third party websites.
36
-     *
37
-     * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#strict
38
-     */
39
-    public const STRICT = 'Strict';
33
+	/**
34
+	 * Cookies will only be sent in a first-party context and not be
35
+	 * sent along with requests initiated by third party websites.
36
+	 *
37
+	 * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#strict
38
+	 */
39
+	public const STRICT = 'Strict';
40 40
 
41
-    /**
42
-     * Cookies will be sent in all contexts, i.e. in responses to both
43
-     * first-party and cross-site requests.
44
-     *
45
-     * If SameSite=None is set, the cookie Secure attribute must also
46
-     * be set (or the cookie will be blocked).
47
-     *
48
-     * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none
49
-     */
50
-    public const NONE = 'None';
41
+	/**
42
+	 * Cookies will be sent in all contexts, i.e. in responses to both
43
+	 * first-party and cross-site requests.
44
+	 *
45
+	 * If SameSite=None is set, the cookie Secure attribute must also
46
+	 * be set (or the cookie will be blocked).
47
+	 *
48
+	 * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none
49
+	 */
50
+	public const NONE = 'None';
51 51
 }
Please login to merge, or discard this patch.
src/Enum/Encoding.php 1 patch
Indentation   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -16,9 +16,9 @@
 block discarded – undo
16 16
  */
17 17
 final class Encoding
18 18
 {
19
-    public const BR = 'br';
20
-    public const CHUNKED = 'chunked';
21
-    public const COMPRESS = 'compress';
22
-    public const DEFLATE = 'deflate';
23
-    public const GZIP = 'gzip';
19
+	public const BR = 'br';
20
+	public const CHUNKED = 'chunked';
21
+	public const COMPRESS = 'compress';
22
+	public const DEFLATE = 'deflate';
23
+	public const GZIP = 'gzip';
24 24
 }
Please login to merge, or discard this patch.
src/Enum/WarningCode.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -18,11 +18,11 @@
 block discarded – undo
18 18
  */
19 19
 final class WarningCode
20 20
 {
21
-    public const RESPONSE_IS_STALE = 110;
22
-    public const REVALIDATION_FAILED = 111;
23
-    public const DISCONNECTED_OPERATION = 112;
24
-    public const HEURISTIC_EXPIRATION = 113;
25
-    public const MISCELLANEOUS_WARNING = 199;
26
-    public const TRANSFORMATION_APPLIED = 214;
27
-    public const MISCELLANEOUS_PERSISTENT_WARNING = 299;
21
+	public const RESPONSE_IS_STALE = 110;
22
+	public const REVALIDATION_FAILED = 111;
23
+	public const DISCONNECTED_OPERATION = 112;
24
+	public const HEURISTIC_EXPIRATION = 113;
25
+	public const MISCELLANEOUS_WARNING = 199;
26
+	public const TRANSFORMATION_APPLIED = 214;
27
+	public const MISCELLANEOUS_PERSISTENT_WARNING = 299;
28 28
 }
Please login to merge, or discard this patch.
src/Enum/AuthenticationScheme.php 1 patch
Indentation   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -18,14 +18,14 @@
 block discarded – undo
18 18
  */
19 19
 final class AuthenticationScheme
20 20
 {
21
-    public const BASIC         = 'Basic';
22
-    public const BEARER        = 'Bearer';
23
-    public const DIGEST        = 'Digest';
24
-    public const HOBA          = 'HOBA';
25
-    public const MUTUAL        = 'Mutual';
26
-    public const NEGOTIATE     = 'Negotiate';
27
-    public const OAUTH         = 'OAuth';
28
-    public const SCRAM_SHA_1   = 'SCRAM-SHA-1';
29
-    public const SCRAM_SHA_256 = 'SCRAM-SHA-256';
30
-    public const VAPID         = 'vapid';
21
+	public const BASIC         = 'Basic';
22
+	public const BEARER        = 'Bearer';
23
+	public const DIGEST        = 'Digest';
24
+	public const HOBA          = 'HOBA';
25
+	public const MUTUAL        = 'Mutual';
26
+	public const NEGOTIATE     = 'Negotiate';
27
+	public const OAUTH         = 'OAuth';
28
+	public const SCRAM_SHA_1   = 'SCRAM-SHA-1';
29
+	public const SCRAM_SHA_256 = 'SCRAM-SHA-256';
30
+	public const VAPID         = 'vapid';
31 31
 }
Please login to merge, or discard this patch.
src/Message.php 1 patch
Indentation   +391 added lines, -391 removed lines patch added patch discarded remove patch
@@ -39,395 +39,395 @@
 block discarded – undo
39 39
 abstract class Message implements MessageInterface
40 40
 {
41 41
 
42
-    /**
43
-     * Default HTTP version
44
-     *
45
-     * @var string
46
-     */
47
-    public const DEFAULT_HTTP_VERSION = '1.1';
48
-
49
-    /**
50
-     * Supported HTTP versions
51
-     *
52
-     * @var list<string>
53
-     */
54
-    public const SUPPORTED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2'];
55
-
56
-    /**
57
-     * The message HTTP version
58
-     *
59
-     * @var string
60
-     */
61
-    private string $protocolVersion = self::DEFAULT_HTTP_VERSION;
62
-
63
-    /**
64
-     * The message headers
65
-     *
66
-     * @var array<string, list<string>>
67
-     */
68
-    private array $headers = [];
69
-
70
-    /**
71
-     * Original header names (see $headers)
72
-     *
73
-     * @var array<string, string>
74
-     */
75
-    private array $headerNames = [];
76
-
77
-    /**
78
-     * The message body
79
-     *
80
-     * @var StreamInterface|null
81
-     */
82
-    private ?StreamInterface $body = null;
83
-
84
-    /**
85
-     * Gets the message HTTP version
86
-     *
87
-     * @return string
88
-     */
89
-    public function getProtocolVersion(): string
90
-    {
91
-        return $this->protocolVersion;
92
-    }
93
-
94
-    /**
95
-     * Creates a new instance of the message with the given HTTP version
96
-     *
97
-     * @param string $version
98
-     *
99
-     * @return static
100
-     *
101
-     * @throws InvalidArgumentException
102
-     *         If the HTTP version isn't valid.
103
-     */
104
-    public function withProtocolVersion($version): MessageInterface
105
-    {
106
-        $clone = clone $this;
107
-        $clone->setProtocolVersion($version);
108
-
109
-        return $clone;
110
-    }
111
-
112
-    /**
113
-     * Gets the message headers
114
-     *
115
-     * @return array<string, list<string>>
116
-     */
117
-    public function getHeaders(): array
118
-    {
119
-        return $this->headers;
120
-    }
121
-
122
-    /**
123
-     * Checks if a header exists in the message by the given name
124
-     *
125
-     * @param string $name
126
-     *
127
-     * @return bool
128
-     */
129
-    public function hasHeader($name): bool
130
-    {
131
-        $key = strtolower($name);
132
-
133
-        return isset($this->headerNames[$key]);
134
-    }
135
-
136
-    /**
137
-     * Gets a header value from the message by the given name
138
-     *
139
-     * @param string $name
140
-     *
141
-     * @return list<string>
142
-     */
143
-    public function getHeader($name): array
144
-    {
145
-        if (!$this->hasHeader($name)) {
146
-            return [];
147
-        }
148
-
149
-        $key = strtolower($name);
150
-        $originalName = $this->headerNames[$key];
151
-        $value = $this->headers[$originalName];
152
-
153
-        return $value;
154
-    }
155
-
156
-    /**
157
-     * Gets a header value as a string from the message by the given name
158
-     *
159
-     * @param string $name
160
-     *
161
-     * @return string
162
-     */
163
-    public function getHeaderLine($name): string
164
-    {
165
-        $value = $this->getHeader($name);
166
-        if ([] === $value) {
167
-            return '';
168
-        }
169
-
170
-        return implode(',', $value);
171
-    }
172
-
173
-    /**
174
-     * Creates a new instance of the message with the given header overwriting the old header
175
-     *
176
-     * @param string $name
177
-     * @param string|string[] $value
178
-     *
179
-     * @return static
180
-     *
181
-     * @throws InvalidArgumentException
182
-     *         If the header isn't valid.
183
-     */
184
-    public function withHeader($name, $value): MessageInterface
185
-    {
186
-        $clone = clone $this;
187
-        $clone->setHeader($name, $value, true);
188
-
189
-        return $clone;
190
-    }
191
-
192
-    /**
193
-     * Creates a new instance of the message with the given header NOT overwriting the old header
194
-     *
195
-     * @param string $name
196
-     * @param string|string[] $value
197
-     *
198
-     * @return static
199
-     *
200
-     * @throws InvalidArgumentException
201
-     *         If the header isn't valid.
202
-     */
203
-    public function withAddedHeader($name, $value): MessageInterface
204
-    {
205
-        $clone = clone $this;
206
-        $clone->setHeader($name, $value, false);
207
-
208
-        return $clone;
209
-    }
210
-
211
-    /**
212
-     * Creates a new instance of the message without a header by the given name
213
-     *
214
-     * @param string $name
215
-     *
216
-     * @return static
217
-     */
218
-    public function withoutHeader($name): MessageInterface
219
-    {
220
-        $clone = clone $this;
221
-        $clone->deleteHeader($name);
222
-
223
-        return $clone;
224
-    }
225
-
226
-    /**
227
-     * Gets the message body
228
-     *
229
-     * @return StreamInterface
230
-     */
231
-    public function getBody(): StreamInterface
232
-    {
233
-        return $this->body ??= new PhpTempStream();
234
-    }
235
-
236
-    /**
237
-     * Creates a new instance of the message with the given body
238
-     *
239
-     * @param StreamInterface $body
240
-     *
241
-     * @return static
242
-     */
243
-    public function withBody(StreamInterface $body): MessageInterface
244
-    {
245
-        $clone = clone $this;
246
-        $clone->setBody($body);
247
-
248
-        return $clone;
249
-    }
250
-
251
-    /**
252
-     * Sets the given HTTP version to the message
253
-     *
254
-     * @param string $protocolVersion
255
-     *
256
-     * @return void
257
-     *
258
-     * @throws InvalidArgumentException
259
-     *         If the HTTP version isn't valid.
260
-     */
261
-    final protected function setProtocolVersion($protocolVersion): void
262
-    {
263
-        $this->validateProtocolVersion($protocolVersion);
264
-
265
-        $this->protocolVersion = $protocolVersion;
266
-    }
267
-
268
-    /**
269
-     * Sets a new header to the message with the given name and value(s)
270
-     *
271
-     * @param string $name
272
-     * @param string|string[] $value
273
-     * @param bool $replace
274
-     *
275
-     * @return void
276
-     *
277
-     * @throws InvalidArgumentException
278
-     *         If the header isn't valid.
279
-     */
280
-    final protected function setHeader($name, $value, bool $replace = true): void
281
-    {
282
-        if (!is_array($value)) {
283
-            $value = [$value];
284
-        }
285
-
286
-        $this->validateHeaderName($name);
287
-        $this->validateHeaderValue($name, $value);
288
-
289
-        if ($replace) {
290
-            $this->deleteHeader($name);
291
-        }
292
-
293
-        $key = strtolower($name);
294
-
295
-        $this->headerNames[$key] ??= $name;
296
-        $this->headers[$this->headerNames[$key]] ??= [];
297
-
298
-        foreach ($value as $subvalue) {
299
-            $this->headers[$this->headerNames[$key]][] = $subvalue;
300
-        }
301
-    }
302
-
303
-    /**
304
-     * Sets the given headers to the message
305
-     *
306
-     * @param array<string, string|string[]> $headers
307
-     *
308
-     * @return void
309
-     *
310
-     * @throws InvalidArgumentException
311
-     *         If one of the headers isn't valid.
312
-     */
313
-    final protected function setHeaders(array $headers): void
314
-    {
315
-        foreach ($headers as $name => $value) {
316
-            $this->setHeader($name, $value, false);
317
-        }
318
-    }
319
-
320
-    /**
321
-     * Deletes a header from the message by the given name
322
-     *
323
-     * @param string $name
324
-     *
325
-     * @return void
326
-     */
327
-    final protected function deleteHeader($name): void
328
-    {
329
-        $key = strtolower($name);
330
-
331
-        if (isset($this->headerNames[$key])) {
332
-            unset($this->headers[$this->headerNames[$key]]);
333
-            unset($this->headerNames[$key]);
334
-        }
335
-    }
336
-
337
-    /**
338
-     * Sets the given body to the message
339
-     *
340
-     * @param StreamInterface $body
341
-     *
342
-     * @return void
343
-     */
344
-    final protected function setBody(StreamInterface $body): void
345
-    {
346
-        $this->body = $body;
347
-    }
348
-
349
-    /**
350
-     * Validates the given HTTP version
351
-     *
352
-     * @param mixed $protocolVersion
353
-     *
354
-     * @return void
355
-     *
356
-     * @throws InvalidArgumentException
357
-     *         If the HTTP version isn't valid.
358
-     */
359
-    private function validateProtocolVersion($protocolVersion): void
360
-    {
361
-        if (!in_array($protocolVersion, self::SUPPORTED_HTTP_VERSIONS, true)) {
362
-            throw new InvalidArgumentException('Invalid or unsupported HTTP version');
363
-        }
364
-    }
365
-
366
-    /**
367
-     * Validates the given header name
368
-     *
369
-     * @param mixed $name
370
-     *
371
-     * @return void
372
-     *
373
-     * @throws InvalidArgumentException
374
-     *         If the header name isn't valid.
375
-     */
376
-    private function validateHeaderName($name): void
377
-    {
378
-        if ($name === '') {
379
-            throw new InvalidArgumentException('HTTP header name cannot be an empty');
380
-        }
381
-
382
-        if (!is_string($name)) {
383
-            throw new InvalidArgumentException('HTTP header name must be a string');
384
-        }
385
-
386
-        if (!preg_match(Header::RFC7230_VALID_TOKEN, $name)) {
387
-            throw new InvalidArgumentException('HTTP header name is invalid');
388
-        }
389
-    }
390
-
391
-    /**
392
-     * Validates the given header value
393
-     *
394
-     * @param string $validName
395
-     * @param array $value
396
-     *
397
-     * @return void
398
-     *
399
-     * @throws InvalidArgumentException
400
-     *         If the header value isn't valid.
401
-     */
402
-    private function validateHeaderValue(string $validName, array $value): void
403
-    {
404
-        if ([] === $value) {
405
-            throw new InvalidArgumentException(sprintf(
406
-                'The "%s" HTTP header value cannot be an empty array',
407
-                $validName,
408
-            ));
409
-        }
410
-
411
-        foreach ($value as $key => $item) {
412
-            if ('' === $item) {
413
-                continue;
414
-            }
415
-
416
-            if (!is_string($item)) {
417
-                throw new InvalidArgumentException(sprintf(
418
-                    'The "%s[%s]" HTTP header value must be a string',
419
-                    $validName,
420
-                    $key
421
-                ));
422
-            }
423
-
424
-            if (!preg_match(Header::RFC7230_VALID_FIELD_VALUE, $item)) {
425
-                throw new InvalidArgumentException(sprintf(
426
-                    'The "%s[%s]" HTTP header value is invalid',
427
-                    $validName,
428
-                    $key
429
-                ));
430
-            }
431
-        }
432
-    }
42
+	/**
43
+	 * Default HTTP version
44
+	 *
45
+	 * @var string
46
+	 */
47
+	public const DEFAULT_HTTP_VERSION = '1.1';
48
+
49
+	/**
50
+	 * Supported HTTP versions
51
+	 *
52
+	 * @var list<string>
53
+	 */
54
+	public const SUPPORTED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2'];
55
+
56
+	/**
57
+	 * The message HTTP version
58
+	 *
59
+	 * @var string
60
+	 */
61
+	private string $protocolVersion = self::DEFAULT_HTTP_VERSION;
62
+
63
+	/**
64
+	 * The message headers
65
+	 *
66
+	 * @var array<string, list<string>>
67
+	 */
68
+	private array $headers = [];
69
+
70
+	/**
71
+	 * Original header names (see $headers)
72
+	 *
73
+	 * @var array<string, string>
74
+	 */
75
+	private array $headerNames = [];
76
+
77
+	/**
78
+	 * The message body
79
+	 *
80
+	 * @var StreamInterface|null
81
+	 */
82
+	private ?StreamInterface $body = null;
83
+
84
+	/**
85
+	 * Gets the message HTTP version
86
+	 *
87
+	 * @return string
88
+	 */
89
+	public function getProtocolVersion(): string
90
+	{
91
+		return $this->protocolVersion;
92
+	}
93
+
94
+	/**
95
+	 * Creates a new instance of the message with the given HTTP version
96
+	 *
97
+	 * @param string $version
98
+	 *
99
+	 * @return static
100
+	 *
101
+	 * @throws InvalidArgumentException
102
+	 *         If the HTTP version isn't valid.
103
+	 */
104
+	public function withProtocolVersion($version): MessageInterface
105
+	{
106
+		$clone = clone $this;
107
+		$clone->setProtocolVersion($version);
108
+
109
+		return $clone;
110
+	}
111
+
112
+	/**
113
+	 * Gets the message headers
114
+	 *
115
+	 * @return array<string, list<string>>
116
+	 */
117
+	public function getHeaders(): array
118
+	{
119
+		return $this->headers;
120
+	}
121
+
122
+	/**
123
+	 * Checks if a header exists in the message by the given name
124
+	 *
125
+	 * @param string $name
126
+	 *
127
+	 * @return bool
128
+	 */
129
+	public function hasHeader($name): bool
130
+	{
131
+		$key = strtolower($name);
132
+
133
+		return isset($this->headerNames[$key]);
134
+	}
135
+
136
+	/**
137
+	 * Gets a header value from the message by the given name
138
+	 *
139
+	 * @param string $name
140
+	 *
141
+	 * @return list<string>
142
+	 */
143
+	public function getHeader($name): array
144
+	{
145
+		if (!$this->hasHeader($name)) {
146
+			return [];
147
+		}
148
+
149
+		$key = strtolower($name);
150
+		$originalName = $this->headerNames[$key];
151
+		$value = $this->headers[$originalName];
152
+
153
+		return $value;
154
+	}
155
+
156
+	/**
157
+	 * Gets a header value as a string from the message by the given name
158
+	 *
159
+	 * @param string $name
160
+	 *
161
+	 * @return string
162
+	 */
163
+	public function getHeaderLine($name): string
164
+	{
165
+		$value = $this->getHeader($name);
166
+		if ([] === $value) {
167
+			return '';
168
+		}
169
+
170
+		return implode(',', $value);
171
+	}
172
+
173
+	/**
174
+	 * Creates a new instance of the message with the given header overwriting the old header
175
+	 *
176
+	 * @param string $name
177
+	 * @param string|string[] $value
178
+	 *
179
+	 * @return static
180
+	 *
181
+	 * @throws InvalidArgumentException
182
+	 *         If the header isn't valid.
183
+	 */
184
+	public function withHeader($name, $value): MessageInterface
185
+	{
186
+		$clone = clone $this;
187
+		$clone->setHeader($name, $value, true);
188
+
189
+		return $clone;
190
+	}
191
+
192
+	/**
193
+	 * Creates a new instance of the message with the given header NOT overwriting the old header
194
+	 *
195
+	 * @param string $name
196
+	 * @param string|string[] $value
197
+	 *
198
+	 * @return static
199
+	 *
200
+	 * @throws InvalidArgumentException
201
+	 *         If the header isn't valid.
202
+	 */
203
+	public function withAddedHeader($name, $value): MessageInterface
204
+	{
205
+		$clone = clone $this;
206
+		$clone->setHeader($name, $value, false);
207
+
208
+		return $clone;
209
+	}
210
+
211
+	/**
212
+	 * Creates a new instance of the message without a header by the given name
213
+	 *
214
+	 * @param string $name
215
+	 *
216
+	 * @return static
217
+	 */
218
+	public function withoutHeader($name): MessageInterface
219
+	{
220
+		$clone = clone $this;
221
+		$clone->deleteHeader($name);
222
+
223
+		return $clone;
224
+	}
225
+
226
+	/**
227
+	 * Gets the message body
228
+	 *
229
+	 * @return StreamInterface
230
+	 */
231
+	public function getBody(): StreamInterface
232
+	{
233
+		return $this->body ??= new PhpTempStream();
234
+	}
235
+
236
+	/**
237
+	 * Creates a new instance of the message with the given body
238
+	 *
239
+	 * @param StreamInterface $body
240
+	 *
241
+	 * @return static
242
+	 */
243
+	public function withBody(StreamInterface $body): MessageInterface
244
+	{
245
+		$clone = clone $this;
246
+		$clone->setBody($body);
247
+
248
+		return $clone;
249
+	}
250
+
251
+	/**
252
+	 * Sets the given HTTP version to the message
253
+	 *
254
+	 * @param string $protocolVersion
255
+	 *
256
+	 * @return void
257
+	 *
258
+	 * @throws InvalidArgumentException
259
+	 *         If the HTTP version isn't valid.
260
+	 */
261
+	final protected function setProtocolVersion($protocolVersion): void
262
+	{
263
+		$this->validateProtocolVersion($protocolVersion);
264
+
265
+		$this->protocolVersion = $protocolVersion;
266
+	}
267
+
268
+	/**
269
+	 * Sets a new header to the message with the given name and value(s)
270
+	 *
271
+	 * @param string $name
272
+	 * @param string|string[] $value
273
+	 * @param bool $replace
274
+	 *
275
+	 * @return void
276
+	 *
277
+	 * @throws InvalidArgumentException
278
+	 *         If the header isn't valid.
279
+	 */
280
+	final protected function setHeader($name, $value, bool $replace = true): void
281
+	{
282
+		if (!is_array($value)) {
283
+			$value = [$value];
284
+		}
285
+
286
+		$this->validateHeaderName($name);
287
+		$this->validateHeaderValue($name, $value);
288
+
289
+		if ($replace) {
290
+			$this->deleteHeader($name);
291
+		}
292
+
293
+		$key = strtolower($name);
294
+
295
+		$this->headerNames[$key] ??= $name;
296
+		$this->headers[$this->headerNames[$key]] ??= [];
297
+
298
+		foreach ($value as $subvalue) {
299
+			$this->headers[$this->headerNames[$key]][] = $subvalue;
300
+		}
301
+	}
302
+
303
+	/**
304
+	 * Sets the given headers to the message
305
+	 *
306
+	 * @param array<string, string|string[]> $headers
307
+	 *
308
+	 * @return void
309
+	 *
310
+	 * @throws InvalidArgumentException
311
+	 *         If one of the headers isn't valid.
312
+	 */
313
+	final protected function setHeaders(array $headers): void
314
+	{
315
+		foreach ($headers as $name => $value) {
316
+			$this->setHeader($name, $value, false);
317
+		}
318
+	}
319
+
320
+	/**
321
+	 * Deletes a header from the message by the given name
322
+	 *
323
+	 * @param string $name
324
+	 *
325
+	 * @return void
326
+	 */
327
+	final protected function deleteHeader($name): void
328
+	{
329
+		$key = strtolower($name);
330
+
331
+		if (isset($this->headerNames[$key])) {
332
+			unset($this->headers[$this->headerNames[$key]]);
333
+			unset($this->headerNames[$key]);
334
+		}
335
+	}
336
+
337
+	/**
338
+	 * Sets the given body to the message
339
+	 *
340
+	 * @param StreamInterface $body
341
+	 *
342
+	 * @return void
343
+	 */
344
+	final protected function setBody(StreamInterface $body): void
345
+	{
346
+		$this->body = $body;
347
+	}
348
+
349
+	/**
350
+	 * Validates the given HTTP version
351
+	 *
352
+	 * @param mixed $protocolVersion
353
+	 *
354
+	 * @return void
355
+	 *
356
+	 * @throws InvalidArgumentException
357
+	 *         If the HTTP version isn't valid.
358
+	 */
359
+	private function validateProtocolVersion($protocolVersion): void
360
+	{
361
+		if (!in_array($protocolVersion, self::SUPPORTED_HTTP_VERSIONS, true)) {
362
+			throw new InvalidArgumentException('Invalid or unsupported HTTP version');
363
+		}
364
+	}
365
+
366
+	/**
367
+	 * Validates the given header name
368
+	 *
369
+	 * @param mixed $name
370
+	 *
371
+	 * @return void
372
+	 *
373
+	 * @throws InvalidArgumentException
374
+	 *         If the header name isn't valid.
375
+	 */
376
+	private function validateHeaderName($name): void
377
+	{
378
+		if ($name === '') {
379
+			throw new InvalidArgumentException('HTTP header name cannot be an empty');
380
+		}
381
+
382
+		if (!is_string($name)) {
383
+			throw new InvalidArgumentException('HTTP header name must be a string');
384
+		}
385
+
386
+		if (!preg_match(Header::RFC7230_VALID_TOKEN, $name)) {
387
+			throw new InvalidArgumentException('HTTP header name is invalid');
388
+		}
389
+	}
390
+
391
+	/**
392
+	 * Validates the given header value
393
+	 *
394
+	 * @param string $validName
395
+	 * @param array $value
396
+	 *
397
+	 * @return void
398
+	 *
399
+	 * @throws InvalidArgumentException
400
+	 *         If the header value isn't valid.
401
+	 */
402
+	private function validateHeaderValue(string $validName, array $value): void
403
+	{
404
+		if ([] === $value) {
405
+			throw new InvalidArgumentException(sprintf(
406
+				'The "%s" HTTP header value cannot be an empty array',
407
+				$validName,
408
+			));
409
+		}
410
+
411
+		foreach ($value as $key => $item) {
412
+			if ('' === $item) {
413
+				continue;
414
+			}
415
+
416
+			if (!is_string($item)) {
417
+				throw new InvalidArgumentException(sprintf(
418
+					'The "%s[%s]" HTTP header value must be a string',
419
+					$validName,
420
+					$key
421
+				));
422
+			}
423
+
424
+			if (!preg_match(Header::RFC7230_VALID_FIELD_VALUE, $item)) {
425
+				throw new InvalidArgumentException(sprintf(
426
+					'The "%s[%s]" HTTP header value is invalid',
427
+					$validName,
428
+					$key
429
+				));
430
+			}
431
+		}
432
+	}
433 433
 }
Please login to merge, or discard this patch.
src/ServerRequest.php 1 patch
Indentation   +350 added lines, -350 removed lines patch added patch discarded remove patch
@@ -35,354 +35,354 @@
 block discarded – undo
35 35
 class ServerRequest extends Request implements ServerRequestInterface
36 36
 {
37 37
 
38
-    /**
39
-     * The server parameters
40
-     *
41
-     * @var array
42
-     */
43
-    private array $serverParams;
44
-
45
-    /**
46
-     * The request's query parameters
47
-     *
48
-     * @var array
49
-     */
50
-    private array $queryParams;
51
-
52
-    /**
53
-     * The request's cookie parameters
54
-     *
55
-     * @var array
56
-     */
57
-    private array $cookieParams;
58
-
59
-    /**
60
-     * The request's uploaded files
61
-     *
62
-     * @var array
63
-     */
64
-    private array $uploadedFiles;
65
-
66
-    /**
67
-     * The request's parsed body
68
-     *
69
-     * @var array|object|null
70
-     */
71
-    private $parsedBody;
72
-
73
-    /**
74
-     * The request attributes
75
-     *
76
-     * @var array
77
-     */
78
-    private array $attributes;
79
-
80
-    /**
81
-     * Constructor of the class
82
-     *
83
-     * @param string|null $protocolVersion
84
-     * @param string|null $method
85
-     * @param mixed $uri
86
-     * @param array<string, string|string[]>|null $headers
87
-     * @param StreamInterface|null $body
88
-     *
89
-     * @param array $serverParams
90
-     * @param array $queryParams
91
-     * @param array $cookieParams
92
-     * @param array $uploadedFiles
93
-     * @param array|object|null $parsedBody
94
-     * @param array $attributes
95
-     *
96
-     * @throws InvalidArgumentException
97
-     *         If one of the arguments isn't valid.
98
-     */
99
-    public function __construct(
100
-        ?string $protocolVersion = null,
101
-        ?string $method = null,
102
-        $uri = null,
103
-        ?array $headers = null,
104
-        ?StreamInterface $body = null,
105
-        array $serverParams = [],
106
-        array $queryParams = [],
107
-        array $cookieParams = [],
108
-        array $uploadedFiles = [],
109
-        $parsedBody = null,
110
-        array $attributes = []
111
-    ) {
112
-        if (isset($protocolVersion)) {
113
-            $this->setProtocolVersion($protocolVersion);
114
-        }
115
-
116
-        parent::__construct($method, $uri, $headers, $body);
117
-
118
-        $this->serverParams = $serverParams;
119
-        $this->queryParams = $queryParams;
120
-        $this->cookieParams = $cookieParams;
121
-        $this->setUploadedFiles($uploadedFiles);
122
-        $this->setParsedBody($parsedBody);
123
-        $this->attributes = $attributes;
124
-    }
125
-
126
-    /**
127
-     * Gets the server parameters
128
-     *
129
-     * @return array
130
-     */
131
-    public function getServerParams(): array
132
-    {
133
-        return $this->serverParams;
134
-    }
135
-
136
-    /**
137
-     * Gets the request's query parameters
138
-     *
139
-     * @return array
140
-     */
141
-    public function getQueryParams(): array
142
-    {
143
-        return $this->queryParams;
144
-    }
145
-
146
-    /**
147
-     * Creates a new instance of the request with the given query parameters
148
-     *
149
-     * @param array $query
150
-     *
151
-     * @return static
152
-     */
153
-    public function withQueryParams(array $query): ServerRequestInterface
154
-    {
155
-        $clone = clone $this;
156
-        $clone->queryParams = $query;
157
-
158
-        return $clone;
159
-    }
160
-
161
-    /**
162
-     * Gets the request's cookie parameters
163
-     *
164
-     * @return array
165
-     */
166
-    public function getCookieParams(): array
167
-    {
168
-        return $this->cookieParams;
169
-    }
170
-
171
-    /**
172
-     * Creates a new instance of the request with the given cookie parameters
173
-     *
174
-     * @param array $cookies
175
-     *
176
-     * @return static
177
-     */
178
-    public function withCookieParams(array $cookies): ServerRequestInterface
179
-    {
180
-        $clone = clone $this;
181
-        $clone->cookieParams = $cookies;
182
-
183
-        return $clone;
184
-    }
185
-
186
-    /**
187
-     * Gets the request's uploaded files
188
-     *
189
-     * @return array
190
-     */
191
-    public function getUploadedFiles(): array
192
-    {
193
-        return $this->uploadedFiles;
194
-    }
195
-
196
-    /**
197
-     * Creates a new instance of the request with the given uploaded files
198
-     *
199
-     * @param array $uploadedFiles
200
-     *
201
-     * @return static
202
-     *
203
-     * @throws InvalidArgumentException
204
-     *         If one of the files isn't valid.
205
-     */
206
-    public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
207
-    {
208
-        $clone = clone $this;
209
-        $clone->setUploadedFiles($uploadedFiles);
210
-
211
-        return $clone;
212
-    }
213
-
214
-    /**
215
-     * Gets the request's parsed body
216
-     *
217
-     * @return array|object|null
218
-     */
219
-    public function getParsedBody()
220
-    {
221
-        return $this->parsedBody;
222
-    }
223
-
224
-    /**
225
-     * Creates a new instance of the request with the given parsed body
226
-     *
227
-     * @param array|object|null $data
228
-     *
229
-     * @return static
230
-     *
231
-     * @throws InvalidArgumentException
232
-     *         If the data isn't valid.
233
-     */
234
-    public function withParsedBody($data): ServerRequestInterface
235
-    {
236
-        $clone = clone $this;
237
-        $clone->setParsedBody($data);
238
-
239
-        return $clone;
240
-    }
241
-
242
-    /**
243
-     * Gets the request attributes
244
-     *
245
-     * @return array
246
-     */
247
-    public function getAttributes(): array
248
-    {
249
-        return $this->attributes;
250
-    }
251
-
252
-    /**
253
-     * Gets the request's attribute value by the given name
254
-     *
255
-     * Returns the default value if the attribute wasn't found.
256
-     *
257
-     * @param array-key $name
258
-     * @param mixed $default
259
-     *
260
-     * @return mixed
261
-     */
262
-    public function getAttribute($name, $default = null)
263
-    {
264
-        if (!array_key_exists($name, $this->attributes)) {
265
-            return $default;
266
-        }
267
-
268
-        return $this->attributes[$name];
269
-    }
270
-
271
-    /**
272
-     * Creates a new instance of the request with the given attribute
273
-     *
274
-     * @param array-key $name
275
-     * @param mixed $value
276
-     *
277
-     * @return static
278
-     */
279
-    public function withAttribute($name, $value): ServerRequestInterface
280
-    {
281
-        $clone = clone $this;
282
-        $clone->attributes[$name] = $value;
283
-
284
-        return $clone;
285
-    }
286
-
287
-    /**
288
-     * Creates a new instance of the request without an attribute with the given name
289
-     *
290
-     * @param array-key $name
291
-     *
292
-     * @return static
293
-     */
294
-    public function withoutAttribute($name): ServerRequestInterface
295
-    {
296
-        $clone = clone $this;
297
-        unset($clone->attributes[$name]);
298
-
299
-        return $clone;
300
-    }
301
-
302
-    /**
303
-     * Sets the given uploaded files to the request
304
-     *
305
-     * @param array $files
306
-     *
307
-     * @return void
308
-     *
309
-     * @throws InvalidArgumentException
310
-     *         If one of the files isn't valid.
311
-     */
312
-    final protected function setUploadedFiles(array $files): void
313
-    {
314
-        $this->validateUploadedFiles($files);
315
-
316
-        $this->uploadedFiles = $files;
317
-    }
318
-
319
-    /**
320
-     * Sets the given parsed body to the request
321
-     *
322
-     * @param array|object|null $data
323
-     *
324
-     * @return void
325
-     *
326
-     * @throws InvalidArgumentException
327
-     *         If the parsed body isn't valid.
328
-     */
329
-    final protected function setParsedBody($data): void
330
-    {
331
-        $this->validateParsedBody($data);
332
-
333
-        $this->parsedBody = $data;
334
-    }
335
-
336
-    /**
337
-     * Validates the given uploaded files
338
-     *
339
-     * @param array $files
340
-     *
341
-     * @return void
342
-     *
343
-     * @throws InvalidArgumentException
344
-     *         If one of the files isn't valid.
345
-     */
346
-    private function validateUploadedFiles(array $files): void
347
-    {
348
-        if ([] === $files) {
349
-            return;
350
-        }
351
-
352
-        /**
353
-         * @param mixed $file
354
-         *
355
-         * @return void
356
-         *
357
-         * @throws InvalidArgumentException
358
-         *
359
-         * @psalm-suppress MissingClosureParamType
360
-         */
361
-        array_walk_recursive($files, static function ($file): void {
362
-            if (! ($file instanceof UploadedFileInterface)) {
363
-                throw new InvalidArgumentException('Invalid uploaded files');
364
-            }
365
-        });
366
-    }
367
-
368
-    /**
369
-     * Validates the given parsed body
370
-     *
371
-     * @param mixed $data
372
-     *
373
-     * @return void
374
-     *
375
-     * @throws InvalidArgumentException
376
-     *         If the parsed body isn't valid.
377
-     */
378
-    private function validateParsedBody($data): void
379
-    {
380
-        if (null === $data) {
381
-            return;
382
-        }
383
-
384
-        if (!is_array($data) && !is_object($data)) {
385
-            throw new InvalidArgumentException('Invalid parsed body');
386
-        }
387
-    }
38
+	/**
39
+	 * The server parameters
40
+	 *
41
+	 * @var array
42
+	 */
43
+	private array $serverParams;
44
+
45
+	/**
46
+	 * The request's query parameters
47
+	 *
48
+	 * @var array
49
+	 */
50
+	private array $queryParams;
51
+
52
+	/**
53
+	 * The request's cookie parameters
54
+	 *
55
+	 * @var array
56
+	 */
57
+	private array $cookieParams;
58
+
59
+	/**
60
+	 * The request's uploaded files
61
+	 *
62
+	 * @var array
63
+	 */
64
+	private array $uploadedFiles;
65
+
66
+	/**
67
+	 * The request's parsed body
68
+	 *
69
+	 * @var array|object|null
70
+	 */
71
+	private $parsedBody;
72
+
73
+	/**
74
+	 * The request attributes
75
+	 *
76
+	 * @var array
77
+	 */
78
+	private array $attributes;
79
+
80
+	/**
81
+	 * Constructor of the class
82
+	 *
83
+	 * @param string|null $protocolVersion
84
+	 * @param string|null $method
85
+	 * @param mixed $uri
86
+	 * @param array<string, string|string[]>|null $headers
87
+	 * @param StreamInterface|null $body
88
+	 *
89
+	 * @param array $serverParams
90
+	 * @param array $queryParams
91
+	 * @param array $cookieParams
92
+	 * @param array $uploadedFiles
93
+	 * @param array|object|null $parsedBody
94
+	 * @param array $attributes
95
+	 *
96
+	 * @throws InvalidArgumentException
97
+	 *         If one of the arguments isn't valid.
98
+	 */
99
+	public function __construct(
100
+		?string $protocolVersion = null,
101
+		?string $method = null,
102
+		$uri = null,
103
+		?array $headers = null,
104
+		?StreamInterface $body = null,
105
+		array $serverParams = [],
106
+		array $queryParams = [],
107
+		array $cookieParams = [],
108
+		array $uploadedFiles = [],
109
+		$parsedBody = null,
110
+		array $attributes = []
111
+	) {
112
+		if (isset($protocolVersion)) {
113
+			$this->setProtocolVersion($protocolVersion);
114
+		}
115
+
116
+		parent::__construct($method, $uri, $headers, $body);
117
+
118
+		$this->serverParams = $serverParams;
119
+		$this->queryParams = $queryParams;
120
+		$this->cookieParams = $cookieParams;
121
+		$this->setUploadedFiles($uploadedFiles);
122
+		$this->setParsedBody($parsedBody);
123
+		$this->attributes = $attributes;
124
+	}
125
+
126
+	/**
127
+	 * Gets the server parameters
128
+	 *
129
+	 * @return array
130
+	 */
131
+	public function getServerParams(): array
132
+	{
133
+		return $this->serverParams;
134
+	}
135
+
136
+	/**
137
+	 * Gets the request's query parameters
138
+	 *
139
+	 * @return array
140
+	 */
141
+	public function getQueryParams(): array
142
+	{
143
+		return $this->queryParams;
144
+	}
145
+
146
+	/**
147
+	 * Creates a new instance of the request with the given query parameters
148
+	 *
149
+	 * @param array $query
150
+	 *
151
+	 * @return static
152
+	 */
153
+	public function withQueryParams(array $query): ServerRequestInterface
154
+	{
155
+		$clone = clone $this;
156
+		$clone->queryParams = $query;
157
+
158
+		return $clone;
159
+	}
160
+
161
+	/**
162
+	 * Gets the request's cookie parameters
163
+	 *
164
+	 * @return array
165
+	 */
166
+	public function getCookieParams(): array
167
+	{
168
+		return $this->cookieParams;
169
+	}
170
+
171
+	/**
172
+	 * Creates a new instance of the request with the given cookie parameters
173
+	 *
174
+	 * @param array $cookies
175
+	 *
176
+	 * @return static
177
+	 */
178
+	public function withCookieParams(array $cookies): ServerRequestInterface
179
+	{
180
+		$clone = clone $this;
181
+		$clone->cookieParams = $cookies;
182
+
183
+		return $clone;
184
+	}
185
+
186
+	/**
187
+	 * Gets the request's uploaded files
188
+	 *
189
+	 * @return array
190
+	 */
191
+	public function getUploadedFiles(): array
192
+	{
193
+		return $this->uploadedFiles;
194
+	}
195
+
196
+	/**
197
+	 * Creates a new instance of the request with the given uploaded files
198
+	 *
199
+	 * @param array $uploadedFiles
200
+	 *
201
+	 * @return static
202
+	 *
203
+	 * @throws InvalidArgumentException
204
+	 *         If one of the files isn't valid.
205
+	 */
206
+	public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
207
+	{
208
+		$clone = clone $this;
209
+		$clone->setUploadedFiles($uploadedFiles);
210
+
211
+		return $clone;
212
+	}
213
+
214
+	/**
215
+	 * Gets the request's parsed body
216
+	 *
217
+	 * @return array|object|null
218
+	 */
219
+	public function getParsedBody()
220
+	{
221
+		return $this->parsedBody;
222
+	}
223
+
224
+	/**
225
+	 * Creates a new instance of the request with the given parsed body
226
+	 *
227
+	 * @param array|object|null $data
228
+	 *
229
+	 * @return static
230
+	 *
231
+	 * @throws InvalidArgumentException
232
+	 *         If the data isn't valid.
233
+	 */
234
+	public function withParsedBody($data): ServerRequestInterface
235
+	{
236
+		$clone = clone $this;
237
+		$clone->setParsedBody($data);
238
+
239
+		return $clone;
240
+	}
241
+
242
+	/**
243
+	 * Gets the request attributes
244
+	 *
245
+	 * @return array
246
+	 */
247
+	public function getAttributes(): array
248
+	{
249
+		return $this->attributes;
250
+	}
251
+
252
+	/**
253
+	 * Gets the request's attribute value by the given name
254
+	 *
255
+	 * Returns the default value if the attribute wasn't found.
256
+	 *
257
+	 * @param array-key $name
258
+	 * @param mixed $default
259
+	 *
260
+	 * @return mixed
261
+	 */
262
+	public function getAttribute($name, $default = null)
263
+	{
264
+		if (!array_key_exists($name, $this->attributes)) {
265
+			return $default;
266
+		}
267
+
268
+		return $this->attributes[$name];
269
+	}
270
+
271
+	/**
272
+	 * Creates a new instance of the request with the given attribute
273
+	 *
274
+	 * @param array-key $name
275
+	 * @param mixed $value
276
+	 *
277
+	 * @return static
278
+	 */
279
+	public function withAttribute($name, $value): ServerRequestInterface
280
+	{
281
+		$clone = clone $this;
282
+		$clone->attributes[$name] = $value;
283
+
284
+		return $clone;
285
+	}
286
+
287
+	/**
288
+	 * Creates a new instance of the request without an attribute with the given name
289
+	 *
290
+	 * @param array-key $name
291
+	 *
292
+	 * @return static
293
+	 */
294
+	public function withoutAttribute($name): ServerRequestInterface
295
+	{
296
+		$clone = clone $this;
297
+		unset($clone->attributes[$name]);
298
+
299
+		return $clone;
300
+	}
301
+
302
+	/**
303
+	 * Sets the given uploaded files to the request
304
+	 *
305
+	 * @param array $files
306
+	 *
307
+	 * @return void
308
+	 *
309
+	 * @throws InvalidArgumentException
310
+	 *         If one of the files isn't valid.
311
+	 */
312
+	final protected function setUploadedFiles(array $files): void
313
+	{
314
+		$this->validateUploadedFiles($files);
315
+
316
+		$this->uploadedFiles = $files;
317
+	}
318
+
319
+	/**
320
+	 * Sets the given parsed body to the request
321
+	 *
322
+	 * @param array|object|null $data
323
+	 *
324
+	 * @return void
325
+	 *
326
+	 * @throws InvalidArgumentException
327
+	 *         If the parsed body isn't valid.
328
+	 */
329
+	final protected function setParsedBody($data): void
330
+	{
331
+		$this->validateParsedBody($data);
332
+
333
+		$this->parsedBody = $data;
334
+	}
335
+
336
+	/**
337
+	 * Validates the given uploaded files
338
+	 *
339
+	 * @param array $files
340
+	 *
341
+	 * @return void
342
+	 *
343
+	 * @throws InvalidArgumentException
344
+	 *         If one of the files isn't valid.
345
+	 */
346
+	private function validateUploadedFiles(array $files): void
347
+	{
348
+		if ([] === $files) {
349
+			return;
350
+		}
351
+
352
+		/**
353
+		 * @param mixed $file
354
+		 *
355
+		 * @return void
356
+		 *
357
+		 * @throws InvalidArgumentException
358
+		 *
359
+		 * @psalm-suppress MissingClosureParamType
360
+		 */
361
+		array_walk_recursive($files, static function ($file): void {
362
+			if (! ($file instanceof UploadedFileInterface)) {
363
+				throw new InvalidArgumentException('Invalid uploaded files');
364
+			}
365
+		});
366
+	}
367
+
368
+	/**
369
+	 * Validates the given parsed body
370
+	 *
371
+	 * @param mixed $data
372
+	 *
373
+	 * @return void
374
+	 *
375
+	 * @throws InvalidArgumentException
376
+	 *         If the parsed body isn't valid.
377
+	 */
378
+	private function validateParsedBody($data): void
379
+	{
380
+		if (null === $data) {
381
+			return;
382
+		}
383
+
384
+		if (!is_array($data) && !is_object($data)) {
385
+			throw new InvalidArgumentException('Invalid parsed body');
386
+		}
387
+	}
388 388
 }
Please login to merge, or discard this patch.