Passed
Pull Request — master (#45)
by Anatoly
06:18
created
src/Uri/Component/Scheme.php 1 patch
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -22,40 +22,40 @@
 block discarded – undo
22 22
  */
23 23
 final class Scheme implements ComponentInterface
24 24
 {
25
-    private const VALIDATION_REGEX = '/^(?:[A-Za-z][0-9A-Za-z\x2b\x2d\x2e]*)?$/';
26
-
27
-    private string $value = '';
28
-
29
-    /**
30
-     * @param mixed $value
31
-     *
32
-     * @throws InvalidArgumentException
33
-     */
34
-    public function __construct($value)
35
-    {
36
-        if ($value === '') {
37
-            return;
38
-        }
39
-
40
-        if (!is_string($value)) {
41
-            throw new InvalidArgumentException('URI component "scheme" must be a string');
42
-        }
43
-
44
-        if (!preg_match(self::VALIDATION_REGEX, $value)) {
45
-            throw new InvalidArgumentException('Invalid URI component "scheme"');
46
-        }
47
-
48
-        // the component is case-insensitive...
49
-        $this->value = strtolower($value);
50
-    }
51
-
52
-    /**
53
-     * {@inheritdoc}
54
-     *
55
-     * @return string
56
-     */
57
-    public function getValue(): string
58
-    {
59
-        return $this->value;
60
-    }
25
+	private const VALIDATION_REGEX = '/^(?:[A-Za-z][0-9A-Za-z\x2b\x2d\x2e]*)?$/';
26
+
27
+	private string $value = '';
28
+
29
+	/**
30
+	 * @param mixed $value
31
+	 *
32
+	 * @throws InvalidArgumentException
33
+	 */
34
+	public function __construct($value)
35
+	{
36
+		if ($value === '') {
37
+			return;
38
+		}
39
+
40
+		if (!is_string($value)) {
41
+			throw new InvalidArgumentException('URI component "scheme" must be a string');
42
+		}
43
+
44
+		if (!preg_match(self::VALIDATION_REGEX, $value)) {
45
+			throw new InvalidArgumentException('Invalid URI component "scheme"');
46
+		}
47
+
48
+		// the component is case-insensitive...
49
+		$this->value = strtolower($value);
50
+	}
51
+
52
+	/**
53
+	 * {@inheritdoc}
54
+	 *
55
+	 * @return string
56
+	 */
57
+	public function getValue(): string
58
+	{
59
+		return $this->value;
60
+	}
61 61
 }
Please login to merge, or discard this patch.
src/HeaderInterface.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -18,17 +18,17 @@
 block discarded – undo
18 18
  */
19 19
 interface HeaderInterface extends IteratorAggregate
20 20
 {
21
-    public const RFC822_DATE_FORMAT = 'D, d M y H:i:s O';
21
+	public const RFC822_DATE_FORMAT = 'D, d M y H:i:s O';
22 22
 
23
-    public const RFC7230_TOKEN_REGEX = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+$/';
23
+	public const RFC7230_TOKEN_REGEX = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+$/';
24 24
 
25
-    public const RFC7230_FIELD_VALUE_REGEX = '/^[\x09\x20-\x7E\x80-\xFF]*$/';
25
+	public const RFC7230_FIELD_VALUE_REGEX = '/^[\x09\x20-\x7E\x80-\xFF]*$/';
26 26
 
27
-    public const RFC7230_QUOTED_STRING_REGEX = '/^(?:[\x5C][\x22]|[\x09\x20\x21\x23-\x5B\x5D-\x7E\x80-\xFF])*$/';
27
+	public const RFC7230_QUOTED_STRING_REGEX = '/^(?:[\x5C][\x22]|[\x09\x20\x21\x23-\x5B\x5D-\x7E\x80-\xFF])*$/';
28 28
 
29
-    public function getFieldName(): string;
29
+	public function getFieldName(): string;
30 30
 
31
-    public function getFieldValue(): string;
31
+	public function getFieldValue(): string;
32 32
 
33
-    public function __toString(): string;
33
+	public function __toString(): string;
34 34
 }
Please login to merge, or discard this patch.
src/Response.php 1 patch
Indentation   +194 added lines, -194 removed lines patch added patch discarded remove patch
@@ -22,198 +22,198 @@
 block discarded – undo
22 22
 
23 23
 class Response extends Message implements ResponseInterface, StatusCodeInterface
24 24
 {
25
-    /**
26
-     * List of Reason Phrases
27
-     *
28
-     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
29
-     *
30
-     * @var array<int<100, 599>, non-empty-string>
31
-     */
32
-    public const REASON_PHRASES = [
33
-        // 1xx
34
-        100 => 'Continue',
35
-        101 => 'Switching Protocols',
36
-        102 => 'Processing',
37
-        103 => 'Early Hints',
38
-        // 2xx
39
-        200 => 'OK',
40
-        201 => 'Created',
41
-        202 => 'Accepted',
42
-        203 => 'Non-Authoritative Information',
43
-        204 => 'No Content',
44
-        205 => 'Reset Content',
45
-        206 => 'Partial Content',
46
-        207 => 'Multi-Status',
47
-        208 => 'Already Reported',
48
-        226 => 'IM Used',
49
-        // 3xx
50
-        300 => 'Multiple Choices',
51
-        301 => 'Moved Permanently',
52
-        302 => 'Found',
53
-        303 => 'See Other',
54
-        304 => 'Not Modified',
55
-        305 => 'Use Proxy',
56
-        307 => 'Temporary Redirect',
57
-        308 => 'Permanent Redirect',
58
-        // 4xx
59
-        400 => 'Bad Request',
60
-        401 => 'Unauthorized',
61
-        402 => 'Payment Required',
62
-        403 => 'Forbidden',
63
-        404 => 'Not Found',
64
-        405 => 'Method Not Allowed',
65
-        406 => 'Not Acceptable',
66
-        407 => 'Proxy Authentication Required',
67
-        408 => 'Request Timeout',
68
-        409 => 'Conflict',
69
-        410 => 'Gone',
70
-        411 => 'Length Required',
71
-        412 => 'Precondition Failed',
72
-        413 => 'Payload Too Large',
73
-        414 => 'URI Too Long',
74
-        415 => 'Unsupported Media Type',
75
-        416 => 'Range Not Satisfiable',
76
-        417 => 'Expectation Failed',
77
-        421 => 'Misdirected Request',
78
-        422 => 'Unprocessable Entity',
79
-        423 => 'Locked',
80
-        424 => 'Failed Dependency',
81
-        425 => 'Too Early',
82
-        426 => 'Upgrade Required',
83
-        428 => 'Precondition Required',
84
-        429 => 'Too Many Requests',
85
-        431 => 'Request Header Fields Too Large',
86
-        451 => 'Unavailable For Legal Reasons',
87
-        // 5xx
88
-        500 => 'Internal Server Error',
89
-        501 => 'Not Implemented',
90
-        502 => 'Bad Gateway',
91
-        503 => 'Service Unavailable',
92
-        504 => 'Gateway Timeout',
93
-        505 => 'HTTP Version Not Supported',
94
-        506 => 'Variant Also Negotiates',
95
-        507 => 'Insufficient Storage',
96
-        508 => 'Loop Detected',
97
-        510 => 'Not Extended',
98
-        511 => 'Network Authentication Required',
99
-    ];
100
-
101
-    private int $statusCode = self::STATUS_OK;
102
-    private string $reasonPhrase = self::REASON_PHRASES[self::STATUS_OK];
103
-
104
-    /**
105
-     * @param array<string, string|string[]>|null $headers
106
-     *
107
-     * @throws InvalidArgumentException
108
-     */
109
-    public function __construct(
110
-        ?int $statusCode = null,
111
-        ?string $reasonPhrase = null,
112
-        ?array $headers = null,
113
-        ?StreamInterface $body = null
114
-    ) {
115
-        if ($statusCode !== null) {
116
-            $this->setStatus($statusCode, $reasonPhrase ?? '');
117
-        }
118
-
119
-        if ($headers !== null) {
120
-            $this->setHeaders($headers);
121
-        }
122
-
123
-        if ($body !== null) {
124
-            $this->setBody($body);
125
-        }
126
-    }
127
-
128
-    /**
129
-     * @inheritDoc
130
-     */
131
-    public function getStatusCode(): int
132
-    {
133
-        return $this->statusCode;
134
-    }
135
-
136
-    /**
137
-     * @inheritDoc
138
-     */
139
-    public function getReasonPhrase(): string
140
-    {
141
-        return $this->reasonPhrase;
142
-    }
143
-
144
-    /**
145
-     * @inheritDoc
146
-     */
147
-    public function withStatus($code, $reasonPhrase = ''): ResponseInterface
148
-    {
149
-        $clone = clone $this;
150
-        $clone->setStatus($code, $reasonPhrase);
151
-
152
-        return $clone;
153
-    }
154
-
155
-    /**
156
-     * Sets the given status code to the response
157
-     *
158
-     * @param int $statusCode
159
-     * @param string $reasonPhrase
160
-     *
161
-     * @throws InvalidArgumentException
162
-     */
163
-    final protected function setStatus($statusCode, $reasonPhrase): void
164
-    {
165
-        $this->validateStatusCode($statusCode);
166
-        $this->validateReasonPhrase($reasonPhrase);
167
-
168
-        if ($reasonPhrase === '') {
169
-            $reasonPhrase = self::REASON_PHRASES[$statusCode] ?? 'Unknown Status Code';
170
-        }
171
-
172
-        $this->statusCode = $statusCode;
173
-        $this->reasonPhrase = $reasonPhrase;
174
-    }
175
-
176
-    /**
177
-     * Validates the given status code
178
-     *
179
-     * @link https://tools.ietf.org/html/rfc7230#section-3.1.2
180
-     *
181
-     * @param mixed $statusCode
182
-     *
183
-     * @throws InvalidArgumentException
184
-     */
185
-    private function validateStatusCode($statusCode): void
186
-    {
187
-        if (!is_int($statusCode)) {
188
-            throw new InvalidArgumentException('HTTP status code must be an integer');
189
-        }
190
-
191
-        if (!($statusCode >= 100 && $statusCode <= 599)) {
192
-            throw new InvalidArgumentException('Invalid HTTP status code');
193
-        }
194
-    }
195
-
196
-    /**
197
-     * Validates the given reason phrase
198
-     *
199
-     * @link https://tools.ietf.org/html/rfc7230#section-3.1.2
200
-     *
201
-     * @param mixed $reasonPhrase
202
-     *
203
-     * @throws InvalidArgumentException
204
-     */
205
-    private function validateReasonPhrase($reasonPhrase): void
206
-    {
207
-        if ($reasonPhrase === '') {
208
-            return;
209
-        }
210
-
211
-        if (!is_string($reasonPhrase)) {
212
-            throw new InvalidArgumentException('HTTP reason phrase must be a string');
213
-        }
214
-
215
-        if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $reasonPhrase)) {
216
-            throw new InvalidArgumentException('Invalid HTTP reason phrase');
217
-        }
218
-    }
25
+	/**
26
+	 * List of Reason Phrases
27
+	 *
28
+	 * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
29
+	 *
30
+	 * @var array<int<100, 599>, non-empty-string>
31
+	 */
32
+	public const REASON_PHRASES = [
33
+		// 1xx
34
+		100 => 'Continue',
35
+		101 => 'Switching Protocols',
36
+		102 => 'Processing',
37
+		103 => 'Early Hints',
38
+		// 2xx
39
+		200 => 'OK',
40
+		201 => 'Created',
41
+		202 => 'Accepted',
42
+		203 => 'Non-Authoritative Information',
43
+		204 => 'No Content',
44
+		205 => 'Reset Content',
45
+		206 => 'Partial Content',
46
+		207 => 'Multi-Status',
47
+		208 => 'Already Reported',
48
+		226 => 'IM Used',
49
+		// 3xx
50
+		300 => 'Multiple Choices',
51
+		301 => 'Moved Permanently',
52
+		302 => 'Found',
53
+		303 => 'See Other',
54
+		304 => 'Not Modified',
55
+		305 => 'Use Proxy',
56
+		307 => 'Temporary Redirect',
57
+		308 => 'Permanent Redirect',
58
+		// 4xx
59
+		400 => 'Bad Request',
60
+		401 => 'Unauthorized',
61
+		402 => 'Payment Required',
62
+		403 => 'Forbidden',
63
+		404 => 'Not Found',
64
+		405 => 'Method Not Allowed',
65
+		406 => 'Not Acceptable',
66
+		407 => 'Proxy Authentication Required',
67
+		408 => 'Request Timeout',
68
+		409 => 'Conflict',
69
+		410 => 'Gone',
70
+		411 => 'Length Required',
71
+		412 => 'Precondition Failed',
72
+		413 => 'Payload Too Large',
73
+		414 => 'URI Too Long',
74
+		415 => 'Unsupported Media Type',
75
+		416 => 'Range Not Satisfiable',
76
+		417 => 'Expectation Failed',
77
+		421 => 'Misdirected Request',
78
+		422 => 'Unprocessable Entity',
79
+		423 => 'Locked',
80
+		424 => 'Failed Dependency',
81
+		425 => 'Too Early',
82
+		426 => 'Upgrade Required',
83
+		428 => 'Precondition Required',
84
+		429 => 'Too Many Requests',
85
+		431 => 'Request Header Fields Too Large',
86
+		451 => 'Unavailable For Legal Reasons',
87
+		// 5xx
88
+		500 => 'Internal Server Error',
89
+		501 => 'Not Implemented',
90
+		502 => 'Bad Gateway',
91
+		503 => 'Service Unavailable',
92
+		504 => 'Gateway Timeout',
93
+		505 => 'HTTP Version Not Supported',
94
+		506 => 'Variant Also Negotiates',
95
+		507 => 'Insufficient Storage',
96
+		508 => 'Loop Detected',
97
+		510 => 'Not Extended',
98
+		511 => 'Network Authentication Required',
99
+	];
100
+
101
+	private int $statusCode = self::STATUS_OK;
102
+	private string $reasonPhrase = self::REASON_PHRASES[self::STATUS_OK];
103
+
104
+	/**
105
+	 * @param array<string, string|string[]>|null $headers
106
+	 *
107
+	 * @throws InvalidArgumentException
108
+	 */
109
+	public function __construct(
110
+		?int $statusCode = null,
111
+		?string $reasonPhrase = null,
112
+		?array $headers = null,
113
+		?StreamInterface $body = null
114
+	) {
115
+		if ($statusCode !== null) {
116
+			$this->setStatus($statusCode, $reasonPhrase ?? '');
117
+		}
118
+
119
+		if ($headers !== null) {
120
+			$this->setHeaders($headers);
121
+		}
122
+
123
+		if ($body !== null) {
124
+			$this->setBody($body);
125
+		}
126
+	}
127
+
128
+	/**
129
+	 * @inheritDoc
130
+	 */
131
+	public function getStatusCode(): int
132
+	{
133
+		return $this->statusCode;
134
+	}
135
+
136
+	/**
137
+	 * @inheritDoc
138
+	 */
139
+	public function getReasonPhrase(): string
140
+	{
141
+		return $this->reasonPhrase;
142
+	}
143
+
144
+	/**
145
+	 * @inheritDoc
146
+	 */
147
+	public function withStatus($code, $reasonPhrase = ''): ResponseInterface
148
+	{
149
+		$clone = clone $this;
150
+		$clone->setStatus($code, $reasonPhrase);
151
+
152
+		return $clone;
153
+	}
154
+
155
+	/**
156
+	 * Sets the given status code to the response
157
+	 *
158
+	 * @param int $statusCode
159
+	 * @param string $reasonPhrase
160
+	 *
161
+	 * @throws InvalidArgumentException
162
+	 */
163
+	final protected function setStatus($statusCode, $reasonPhrase): void
164
+	{
165
+		$this->validateStatusCode($statusCode);
166
+		$this->validateReasonPhrase($reasonPhrase);
167
+
168
+		if ($reasonPhrase === '') {
169
+			$reasonPhrase = self::REASON_PHRASES[$statusCode] ?? 'Unknown Status Code';
170
+		}
171
+
172
+		$this->statusCode = $statusCode;
173
+		$this->reasonPhrase = $reasonPhrase;
174
+	}
175
+
176
+	/**
177
+	 * Validates the given status code
178
+	 *
179
+	 * @link https://tools.ietf.org/html/rfc7230#section-3.1.2
180
+	 *
181
+	 * @param mixed $statusCode
182
+	 *
183
+	 * @throws InvalidArgumentException
184
+	 */
185
+	private function validateStatusCode($statusCode): void
186
+	{
187
+		if (!is_int($statusCode)) {
188
+			throw new InvalidArgumentException('HTTP status code must be an integer');
189
+		}
190
+
191
+		if (!($statusCode >= 100 && $statusCode <= 599)) {
192
+			throw new InvalidArgumentException('Invalid HTTP status code');
193
+		}
194
+	}
195
+
196
+	/**
197
+	 * Validates the given reason phrase
198
+	 *
199
+	 * @link https://tools.ietf.org/html/rfc7230#section-3.1.2
200
+	 *
201
+	 * @param mixed $reasonPhrase
202
+	 *
203
+	 * @throws InvalidArgumentException
204
+	 */
205
+	private function validateReasonPhrase($reasonPhrase): void
206
+	{
207
+		if ($reasonPhrase === '') {
208
+			return;
209
+		}
210
+
211
+		if (!is_string($reasonPhrase)) {
212
+			throw new InvalidArgumentException('HTTP reason phrase must be a string');
213
+		}
214
+
215
+		if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $reasonPhrase)) {
216
+			throw new InvalidArgumentException('Invalid HTTP reason phrase');
217
+		}
218
+	}
219 219
 }
Please login to merge, or discard this patch.
src/Response/HtmlResponse.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -22,42 +22,42 @@
 block discarded – undo
22 22
 
23 23
 final class HtmlResponse extends Response
24 24
 {
25
-    /**
26
-     * @param mixed $html
27
-     *
28
-     * @throws InvalidArgumentException
29
-     */
30
-    public function __construct(int $statusCode, $html)
31
-    {
32
-        parent::__construct($statusCode);
33
-
34
-        $this->setBody(self::createBody($html));
35
-        $this->setHeader('Content-Type', 'text/html; charset=utf-8');
36
-    }
37
-
38
-    /**
39
-     * @param mixed $html
40
-     *
41
-     * @throws InvalidArgumentException
42
-     */
43
-    private static function createBody($html): StreamInterface
44
-    {
45
-        if ($html instanceof StreamInterface) {
46
-            return $html;
47
-        }
48
-
49
-        if (is_object($html) && method_exists($html, '__toString')) {
50
-            $html = (string) $html;
51
-        }
52
-
53
-        if (!is_string($html)) {
54
-            throw new InvalidArgumentException('Unable to create the HTML response due to a unexpected HTML type');
55
-        }
56
-
57
-        $stream = new PhpTempStream('r+b');
58
-        $stream->write($html);
59
-        $stream->rewind();
60
-
61
-        return $stream;
62
-    }
25
+	/**
26
+	 * @param mixed $html
27
+	 *
28
+	 * @throws InvalidArgumentException
29
+	 */
30
+	public function __construct(int $statusCode, $html)
31
+	{
32
+		parent::__construct($statusCode);
33
+
34
+		$this->setBody(self::createBody($html));
35
+		$this->setHeader('Content-Type', 'text/html; charset=utf-8');
36
+	}
37
+
38
+	/**
39
+	 * @param mixed $html
40
+	 *
41
+	 * @throws InvalidArgumentException
42
+	 */
43
+	private static function createBody($html): StreamInterface
44
+	{
45
+		if ($html instanceof StreamInterface) {
46
+			return $html;
47
+		}
48
+
49
+		if (is_object($html) && method_exists($html, '__toString')) {
50
+			$html = (string) $html;
51
+		}
52
+
53
+		if (!is_string($html)) {
54
+			throw new InvalidArgumentException('Unable to create the HTML response due to a unexpected HTML type');
55
+		}
56
+
57
+		$stream = new PhpTempStream('r+b');
58
+		$stream->write($html);
59
+		$stream->rewind();
60
+
61
+		return $stream;
62
+	}
63 63
 }
Please login to merge, or discard this patch.
src/Message.php 1 patch
Indentation   +292 added lines, -292 removed lines patch added patch discarded remove patch
@@ -24,296 +24,296 @@
 block discarded – undo
24 24
 
25 25
 abstract class Message implements MessageInterface
26 26
 {
27
-    /**
28
-     * @deprecated 3.2.0
29
-     */
30
-    public const ALLOWED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2'];
31
-
32
-    public const HTTP_VERSION_REGEX = '/^[0-9](?:[.][0-9])?$/';
33
-    public const DEFAULT_HTTP_VERSION = '1.1';
34
-
35
-    private string $protocolVersion = self::DEFAULT_HTTP_VERSION;
36
-
37
-    /**
38
-     * @var array<string, list<string>>
39
-     */
40
-    private array $headers = [];
41
-
42
-    /**
43
-     * @var array<string, string>
44
-     */
45
-    private array $headerNames = [];
46
-
47
-    private ?StreamInterface $body = null;
48
-
49
-    /**
50
-     * @inheritDoc
51
-     */
52
-    public function getProtocolVersion(): string
53
-    {
54
-        return $this->protocolVersion;
55
-    }
56
-
57
-    /**
58
-     * @inheritDoc
59
-     *
60
-     * @throws InvalidArgumentException
61
-     */
62
-    public function withProtocolVersion($version): MessageInterface
63
-    {
64
-        $clone = clone $this;
65
-        $clone->setProtocolVersion($version);
66
-
67
-        return $clone;
68
-    }
69
-
70
-    /**
71
-     * @inheritDoc
72
-     */
73
-    public function getHeaders(): array
74
-    {
75
-        return $this->headers;
76
-    }
77
-
78
-    /**
79
-     * @inheritDoc
80
-     */
81
-    public function hasHeader($name): bool
82
-    {
83
-        $key = strtolower($name);
84
-
85
-        return isset($this->headerNames[$key]);
86
-    }
87
-
88
-    /**
89
-     * @inheritDoc
90
-     */
91
-    public function getHeader($name): array
92
-    {
93
-        $key = strtolower($name);
94
-
95
-        if (!isset($this->headerNames[$key])) {
96
-            return [];
97
-        }
98
-
99
-        return $this->headers[$this->headerNames[$key]];
100
-    }
101
-
102
-    /**
103
-     * @inheritDoc
104
-     */
105
-    public function getHeaderLine($name): string
106
-    {
107
-        $key = strtolower($name);
108
-
109
-        if (!isset($this->headerNames[$key])) {
110
-            return '';
111
-        }
112
-
113
-        return implode(',', $this->headers[$this->headerNames[$key]]);
114
-    }
115
-
116
-    /**
117
-     * @inheritDoc
118
-     */
119
-    public function withHeader($name, $value): MessageInterface
120
-    {
121
-        $clone = clone $this;
122
-        $clone->setHeader($name, $value, true);
123
-
124
-        return $clone;
125
-    }
126
-
127
-    /**
128
-     * @inheritDoc
129
-     */
130
-    public function withAddedHeader($name, $value): MessageInterface
131
-    {
132
-        $clone = clone $this;
133
-        $clone->setHeader($name, $value, false);
134
-
135
-        return $clone;
136
-    }
137
-
138
-    /**
139
-     * @inheritDoc
140
-     */
141
-    public function withoutHeader($name): MessageInterface
142
-    {
143
-        $clone = clone $this;
144
-        $clone->deleteHeader($name);
145
-
146
-        return $clone;
147
-    }
148
-
149
-    /**
150
-     * @inheritDoc
151
-     */
152
-    public function getBody(): StreamInterface
153
-    {
154
-        return $this->body ??= new PhpTempStream();
155
-    }
156
-
157
-    /**
158
-     * @inheritDoc
159
-     */
160
-    public function withBody(StreamInterface $body): MessageInterface
161
-    {
162
-        $clone = clone $this;
163
-        $clone->setBody($body);
164
-
165
-        return $clone;
166
-    }
167
-
168
-    /**
169
-     * Sets the given HTTP version to the message
170
-     *
171
-     * @param string $protocolVersion
172
-     *
173
-     * @throws InvalidArgumentException
174
-     */
175
-    final protected function setProtocolVersion($protocolVersion): void
176
-    {
177
-        $this->validateProtocolVersion($protocolVersion);
178
-
179
-        $this->protocolVersion = $protocolVersion;
180
-    }
181
-
182
-    /**
183
-     * Sets a new header to the message with the given name and value(s)
184
-     *
185
-     * @param string $name
186
-     * @param string|string[] $value
187
-     *
188
-     * @throws InvalidArgumentException
189
-     */
190
-    final protected function setHeader($name, $value, bool $replace = true): void
191
-    {
192
-        if (!is_array($value)) {
193
-            $value = [$value];
194
-        }
195
-
196
-        $this->validateHeaderName($name);
197
-        $this->validateHeaderValue($name, $value);
198
-
199
-        $replace and $this->deleteHeader($name);
200
-
201
-        $key = strtolower($name);
202
-
203
-        $this->headerNames[$key] ??= $name;
204
-        $this->headers[$this->headerNames[$key]] ??= [];
205
-
206
-        foreach ($value as $item) {
207
-            $this->headers[$this->headerNames[$key]][] = $item;
208
-        }
209
-    }
210
-
211
-    /**
212
-     * Sets the given headers to the message
213
-     *
214
-     * @param array<string, string|string[]> $headers
215
-     *
216
-     * @throws InvalidArgumentException
217
-     */
218
-    final protected function setHeaders(array $headers): void
219
-    {
220
-        foreach ($headers as $name => $value) {
221
-            $this->setHeader($name, $value, false);
222
-        }
223
-    }
224
-
225
-    /**
226
-     * Deletes a header from the message by the given name
227
-     *
228
-     * @param string $name
229
-     */
230
-    final protected function deleteHeader($name): void
231
-    {
232
-        $key = strtolower($name);
233
-
234
-        if (isset($this->headerNames[$key])) {
235
-            unset($this->headers[$this->headerNames[$key]]);
236
-            unset($this->headerNames[$key]);
237
-        }
238
-    }
239
-
240
-    /**
241
-     * Sets the given body to the message
242
-     */
243
-    final protected function setBody(StreamInterface $body): void
244
-    {
245
-        $this->body = $body;
246
-    }
247
-
248
-    /**
249
-     * Validates the given HTTP version
250
-     *
251
-     * @param mixed $protocolVersion
252
-     *
253
-     * @throws InvalidArgumentException
254
-     */
255
-    private function validateProtocolVersion($protocolVersion): void
256
-    {
257
-        if ($protocolVersion === '') {
258
-            throw new InvalidArgumentException('HTTP version cannot be an empty');
259
-        }
260
-
261
-        if (!is_string($protocolVersion)) {
262
-            throw new InvalidArgumentException('HTTP version must be a string');
263
-        }
264
-
265
-        if (!preg_match(self::HTTP_VERSION_REGEX, $protocolVersion)) {
266
-            throw new InvalidArgumentException('HTTP version is invalid');
267
-        }
268
-    }
269
-
270
-    /**
271
-     * Validates the given header name
272
-     *
273
-     * @param mixed $name
274
-     *
275
-     * @throws InvalidArgumentException
276
-     */
277
-    private function validateHeaderName($name): void
278
-    {
279
-        if ($name === '') {
280
-            throw new InvalidArgumentException('HTTP header name cannot be an empty');
281
-        }
282
-
283
-        if (!is_string($name)) {
284
-            throw new InvalidArgumentException('HTTP header name must be a string');
285
-        }
286
-
287
-        if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $name)) {
288
-            throw new InvalidArgumentException('HTTP header name is invalid');
289
-        }
290
-    }
291
-
292
-    /**
293
-     * Validates the given header value
294
-     *
295
-     * @param array<array-key, mixed> $value
296
-     *
297
-     * @throws InvalidArgumentException
298
-     */
299
-    private function validateHeaderValue(string $name, array $value): void
300
-    {
301
-        if ($value === []) {
302
-            throw new InvalidArgumentException("The value of the HTTP header $name cannot be an empty array");
303
-        }
304
-
305
-        foreach ($value as $key => $item) {
306
-            if ($item === '') {
307
-                continue;
308
-            }
309
-
310
-            if (!is_string($item)) {
311
-                throw new InvalidArgumentException("The value of the HTTP header $name:$key must be a string");
312
-            }
313
-
314
-            if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $item)) {
315
-                throw new InvalidArgumentException("The value of the HTTP header $name:$key is invalid");
316
-            }
317
-        }
318
-    }
27
+	/**
28
+	 * @deprecated 3.2.0
29
+	 */
30
+	public const ALLOWED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2'];
31
+
32
+	public const HTTP_VERSION_REGEX = '/^[0-9](?:[.][0-9])?$/';
33
+	public const DEFAULT_HTTP_VERSION = '1.1';
34
+
35
+	private string $protocolVersion = self::DEFAULT_HTTP_VERSION;
36
+
37
+	/**
38
+	 * @var array<string, list<string>>
39
+	 */
40
+	private array $headers = [];
41
+
42
+	/**
43
+	 * @var array<string, string>
44
+	 */
45
+	private array $headerNames = [];
46
+
47
+	private ?StreamInterface $body = null;
48
+
49
+	/**
50
+	 * @inheritDoc
51
+	 */
52
+	public function getProtocolVersion(): string
53
+	{
54
+		return $this->protocolVersion;
55
+	}
56
+
57
+	/**
58
+	 * @inheritDoc
59
+	 *
60
+	 * @throws InvalidArgumentException
61
+	 */
62
+	public function withProtocolVersion($version): MessageInterface
63
+	{
64
+		$clone = clone $this;
65
+		$clone->setProtocolVersion($version);
66
+
67
+		return $clone;
68
+	}
69
+
70
+	/**
71
+	 * @inheritDoc
72
+	 */
73
+	public function getHeaders(): array
74
+	{
75
+		return $this->headers;
76
+	}
77
+
78
+	/**
79
+	 * @inheritDoc
80
+	 */
81
+	public function hasHeader($name): bool
82
+	{
83
+		$key = strtolower($name);
84
+
85
+		return isset($this->headerNames[$key]);
86
+	}
87
+
88
+	/**
89
+	 * @inheritDoc
90
+	 */
91
+	public function getHeader($name): array
92
+	{
93
+		$key = strtolower($name);
94
+
95
+		if (!isset($this->headerNames[$key])) {
96
+			return [];
97
+		}
98
+
99
+		return $this->headers[$this->headerNames[$key]];
100
+	}
101
+
102
+	/**
103
+	 * @inheritDoc
104
+	 */
105
+	public function getHeaderLine($name): string
106
+	{
107
+		$key = strtolower($name);
108
+
109
+		if (!isset($this->headerNames[$key])) {
110
+			return '';
111
+		}
112
+
113
+		return implode(',', $this->headers[$this->headerNames[$key]]);
114
+	}
115
+
116
+	/**
117
+	 * @inheritDoc
118
+	 */
119
+	public function withHeader($name, $value): MessageInterface
120
+	{
121
+		$clone = clone $this;
122
+		$clone->setHeader($name, $value, true);
123
+
124
+		return $clone;
125
+	}
126
+
127
+	/**
128
+	 * @inheritDoc
129
+	 */
130
+	public function withAddedHeader($name, $value): MessageInterface
131
+	{
132
+		$clone = clone $this;
133
+		$clone->setHeader($name, $value, false);
134
+
135
+		return $clone;
136
+	}
137
+
138
+	/**
139
+	 * @inheritDoc
140
+	 */
141
+	public function withoutHeader($name): MessageInterface
142
+	{
143
+		$clone = clone $this;
144
+		$clone->deleteHeader($name);
145
+
146
+		return $clone;
147
+	}
148
+
149
+	/**
150
+	 * @inheritDoc
151
+	 */
152
+	public function getBody(): StreamInterface
153
+	{
154
+		return $this->body ??= new PhpTempStream();
155
+	}
156
+
157
+	/**
158
+	 * @inheritDoc
159
+	 */
160
+	public function withBody(StreamInterface $body): MessageInterface
161
+	{
162
+		$clone = clone $this;
163
+		$clone->setBody($body);
164
+
165
+		return $clone;
166
+	}
167
+
168
+	/**
169
+	 * Sets the given HTTP version to the message
170
+	 *
171
+	 * @param string $protocolVersion
172
+	 *
173
+	 * @throws InvalidArgumentException
174
+	 */
175
+	final protected function setProtocolVersion($protocolVersion): void
176
+	{
177
+		$this->validateProtocolVersion($protocolVersion);
178
+
179
+		$this->protocolVersion = $protocolVersion;
180
+	}
181
+
182
+	/**
183
+	 * Sets a new header to the message with the given name and value(s)
184
+	 *
185
+	 * @param string $name
186
+	 * @param string|string[] $value
187
+	 *
188
+	 * @throws InvalidArgumentException
189
+	 */
190
+	final protected function setHeader($name, $value, bool $replace = true): void
191
+	{
192
+		if (!is_array($value)) {
193
+			$value = [$value];
194
+		}
195
+
196
+		$this->validateHeaderName($name);
197
+		$this->validateHeaderValue($name, $value);
198
+
199
+		$replace and $this->deleteHeader($name);
200
+
201
+		$key = strtolower($name);
202
+
203
+		$this->headerNames[$key] ??= $name;
204
+		$this->headers[$this->headerNames[$key]] ??= [];
205
+
206
+		foreach ($value as $item) {
207
+			$this->headers[$this->headerNames[$key]][] = $item;
208
+		}
209
+	}
210
+
211
+	/**
212
+	 * Sets the given headers to the message
213
+	 *
214
+	 * @param array<string, string|string[]> $headers
215
+	 *
216
+	 * @throws InvalidArgumentException
217
+	 */
218
+	final protected function setHeaders(array $headers): void
219
+	{
220
+		foreach ($headers as $name => $value) {
221
+			$this->setHeader($name, $value, false);
222
+		}
223
+	}
224
+
225
+	/**
226
+	 * Deletes a header from the message by the given name
227
+	 *
228
+	 * @param string $name
229
+	 */
230
+	final protected function deleteHeader($name): void
231
+	{
232
+		$key = strtolower($name);
233
+
234
+		if (isset($this->headerNames[$key])) {
235
+			unset($this->headers[$this->headerNames[$key]]);
236
+			unset($this->headerNames[$key]);
237
+		}
238
+	}
239
+
240
+	/**
241
+	 * Sets the given body to the message
242
+	 */
243
+	final protected function setBody(StreamInterface $body): void
244
+	{
245
+		$this->body = $body;
246
+	}
247
+
248
+	/**
249
+	 * Validates the given HTTP version
250
+	 *
251
+	 * @param mixed $protocolVersion
252
+	 *
253
+	 * @throws InvalidArgumentException
254
+	 */
255
+	private function validateProtocolVersion($protocolVersion): void
256
+	{
257
+		if ($protocolVersion === '') {
258
+			throw new InvalidArgumentException('HTTP version cannot be an empty');
259
+		}
260
+
261
+		if (!is_string($protocolVersion)) {
262
+			throw new InvalidArgumentException('HTTP version must be a string');
263
+		}
264
+
265
+		if (!preg_match(self::HTTP_VERSION_REGEX, $protocolVersion)) {
266
+			throw new InvalidArgumentException('HTTP version is invalid');
267
+		}
268
+	}
269
+
270
+	/**
271
+	 * Validates the given header name
272
+	 *
273
+	 * @param mixed $name
274
+	 *
275
+	 * @throws InvalidArgumentException
276
+	 */
277
+	private function validateHeaderName($name): void
278
+	{
279
+		if ($name === '') {
280
+			throw new InvalidArgumentException('HTTP header name cannot be an empty');
281
+		}
282
+
283
+		if (!is_string($name)) {
284
+			throw new InvalidArgumentException('HTTP header name must be a string');
285
+		}
286
+
287
+		if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $name)) {
288
+			throw new InvalidArgumentException('HTTP header name is invalid');
289
+		}
290
+	}
291
+
292
+	/**
293
+	 * Validates the given header value
294
+	 *
295
+	 * @param array<array-key, mixed> $value
296
+	 *
297
+	 * @throws InvalidArgumentException
298
+	 */
299
+	private function validateHeaderValue(string $name, array $value): void
300
+	{
301
+		if ($value === []) {
302
+			throw new InvalidArgumentException("The value of the HTTP header $name cannot be an empty array");
303
+		}
304
+
305
+		foreach ($value as $key => $item) {
306
+			if ($item === '') {
307
+				continue;
308
+			}
309
+
310
+			if (!is_string($item)) {
311
+				throw new InvalidArgumentException("The value of the HTTP header $name:$key must be a string");
312
+			}
313
+
314
+			if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $item)) {
315
+				throw new InvalidArgumentException("The value of the HTTP header $name:$key is invalid");
316
+			}
317
+		}
318
+	}
319 319
 }
Please login to merge, or discard this patch.
src/ServerRequest.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -314,7 +314,7 @@
 block discarded – undo
314 314
             return;
315 315
         }
316 316
 
317
-        array_walk_recursive($files, /** @param mixed $file */ static function ($file): void {
317
+        array_walk_recursive($files, /** @param mixed $file */ static function($file): void {
318 318
             if (!($file instanceof UploadedFileInterface)) {
319 319
                 throw new InvalidArgumentException('Invalid uploaded file');
320 320
             }
Please login to merge, or discard this patch.
Indentation   +313 added lines, -313 removed lines patch added patch discarded remove patch
@@ -26,317 +26,317 @@
 block discarded – undo
26 26
  */
27 27
 class ServerRequest extends Request implements ServerRequestInterface
28 28
 {
29
-    /**
30
-     * @var array<array-key, mixed>
31
-     */
32
-    private array $serverParams;
33
-
34
-    /**
35
-     * @var array<array-key, mixed>
36
-     */
37
-    private array $queryParams;
38
-
39
-    /**
40
-     * @var array<array-key, mixed>
41
-     */
42
-    private array $cookieParams;
43
-
44
-    /**
45
-     * @var array<array-key, mixed>
46
-     */
47
-    private array $uploadedFiles = [];
48
-
49
-    /**
50
-     * @var array<array-key, mixed>|object|null
51
-     */
52
-    private $parsedBody = null;
53
-
54
-    /**
55
-     * @var array<array-key, mixed>
56
-     */
57
-    private array $attributes;
58
-
59
-    /**
60
-     * Constructor of the class
61
-     *
62
-     * @param mixed $uri
63
-     * @param array<string, string|string[]>|null $headers
64
-     *
65
-     * @param array<array-key, mixed> $serverParams
66
-     * @param array<array-key, mixed> $queryParams
67
-     * @param array<array-key, mixed> $cookieParams
68
-     * @param array<array-key, mixed> $uploadedFiles
69
-     * @param array<array-key, mixed>|object|null $parsedBody
70
-     * @param array<array-key, mixed> $attributes
71
-     *
72
-     * @throws InvalidArgumentException
73
-     */
74
-    public function __construct(
75
-        ?string $protocolVersion = null,
76
-        ?string $method = null,
77
-        $uri = null,
78
-        ?array $headers = null,
79
-        ?StreamInterface $body = null,
80
-        array $serverParams = [],
81
-        array $queryParams = [],
82
-        array $cookieParams = [],
83
-        array $uploadedFiles = [],
84
-        $parsedBody = null,
85
-        array $attributes = []
86
-    ) {
87
-        parent::__construct($method, $uri, $headers, $body);
88
-
89
-        if ($protocolVersion !== null) {
90
-            $this->setProtocolVersion($protocolVersion);
91
-        }
92
-
93
-        if ($uploadedFiles !== []) {
94
-            $this->setUploadedFiles($uploadedFiles);
95
-        }
96
-
97
-        if ($parsedBody !== null) {
98
-            $this->setParsedBody($parsedBody);
99
-        }
100
-
101
-        $this->serverParams = $serverParams;
102
-        $this->queryParams = $queryParams;
103
-        $this->cookieParams = $cookieParams;
104
-        $this->attributes = $attributes;
105
-    }
106
-
107
-    /**
108
-     * {@inheritDoc}
109
-     *
110
-     * @return array<array-key, mixed>
111
-     */
112
-    public function getServerParams(): array
113
-    {
114
-        return $this->serverParams;
115
-    }
116
-
117
-    /**
118
-     * {@inheritDoc}
119
-     *
120
-     * @return array<array-key, mixed>
121
-     */
122
-    public function getQueryParams(): array
123
-    {
124
-        return $this->queryParams;
125
-    }
126
-
127
-    /**
128
-     * {@inheritDoc}
129
-     *
130
-     * @param array<array-key, mixed> $query
131
-     *
132
-     * @return static
133
-     */
134
-    public function withQueryParams(array $query): ServerRequestInterface
135
-    {
136
-        $clone = clone $this;
137
-        $clone->queryParams = $query;
138
-
139
-        return $clone;
140
-    }
141
-
142
-    /**
143
-     * {@inheritDoc}
144
-     *
145
-     * @return array<array-key, mixed>
146
-     */
147
-    public function getCookieParams(): array
148
-    {
149
-        return $this->cookieParams;
150
-    }
151
-
152
-    /**
153
-     * {@inheritDoc}
154
-     *
155
-     * @param array<array-key, mixed> $cookies
156
-     *
157
-     * @return static
158
-     */
159
-    public function withCookieParams(array $cookies): ServerRequestInterface
160
-    {
161
-        $clone = clone $this;
162
-        $clone->cookieParams = $cookies;
163
-
164
-        return $clone;
165
-    }
166
-
167
-    /**
168
-     * {@inheritDoc}
169
-     *
170
-     * @return array<array-key, mixed>
171
-     */
172
-    public function getUploadedFiles(): array
173
-    {
174
-        return $this->uploadedFiles;
175
-    }
176
-
177
-    /**
178
-     * {@inheritDoc}
179
-     *
180
-     * @param array<array-key, mixed> $uploadedFiles
181
-     *
182
-     * @return static
183
-     *
184
-     * @throws InvalidArgumentException
185
-     */
186
-    public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
187
-    {
188
-        $clone = clone $this;
189
-        $clone->setUploadedFiles($uploadedFiles);
190
-
191
-        return $clone;
192
-    }
193
-
194
-    /**
195
-     * {@inheritDoc}
196
-     *
197
-     * @return array<array-key, mixed>|object|null
198
-     */
199
-    public function getParsedBody()
200
-    {
201
-        return $this->parsedBody;
202
-    }
203
-
204
-    /**
205
-     * {@inheritDoc}
206
-     *
207
-     * @param array<array-key, mixed>|object|null $data
208
-     *
209
-     * @return static
210
-     *
211
-     * @throws InvalidArgumentException
212
-     */
213
-    public function withParsedBody($data): ServerRequestInterface
214
-    {
215
-        $clone = clone $this;
216
-        $clone->setParsedBody($data);
217
-
218
-        return $clone;
219
-    }
220
-
221
-    /**
222
-     * {@inheritDoc}
223
-     *
224
-     * @return array<array-key, mixed>
225
-     */
226
-    public function getAttributes(): array
227
-    {
228
-        return $this->attributes;
229
-    }
230
-
231
-    /**
232
-     * {@inheritDoc}
233
-     *
234
-     * @param array-key $name
235
-     * @param mixed $default
236
-     *
237
-     * @return mixed
238
-     */
239
-    public function getAttribute($name, $default = null)
240
-    {
241
-        if (!array_key_exists($name, $this->attributes)) {
242
-            return $default;
243
-        }
244
-
245
-        return $this->attributes[$name];
246
-    }
247
-
248
-    /**
249
-     * {@inheritDoc}
250
-     *
251
-     * @param array-key $name
252
-     * @param mixed $value
253
-     *
254
-     * @return static
255
-     */
256
-    public function withAttribute($name, $value): ServerRequestInterface
257
-    {
258
-        $clone = clone $this;
259
-        $clone->attributes[$name] = $value;
260
-
261
-        return $clone;
262
-    }
263
-
264
-    /**
265
-     * {@inheritDoc}
266
-     *
267
-     * @param array-key $name
268
-     *
269
-     * @return static
270
-     */
271
-    public function withoutAttribute($name): ServerRequestInterface
272
-    {
273
-        $clone = clone $this;
274
-        unset($clone->attributes[$name]);
275
-
276
-        return $clone;
277
-    }
278
-
279
-    /**
280
-     * Sets the given uploaded files to the request
281
-     *
282
-     * @param array<array-key, mixed> $files
283
-     *
284
-     * @throws InvalidArgumentException
285
-     */
286
-    final protected function setUploadedFiles(array $files): void
287
-    {
288
-        $this->validateUploadedFiles($files);
289
-
290
-        $this->uploadedFiles = $files;
291
-    }
292
-
293
-    /**
294
-     * Sets the given parsed body to the request
295
-     *
296
-     * @param array<array-key, mixed>|object|null $data
297
-     *
298
-     * @throws InvalidArgumentException
299
-     */
300
-    final protected function setParsedBody($data): void
301
-    {
302
-        $this->validateParsedBody($data);
303
-
304
-        $this->parsedBody = $data;
305
-    }
306
-
307
-    /**
308
-     * Validates the given uploaded files
309
-     *
310
-     * @param array<array-key, mixed> $files
311
-     *
312
-     * @throws InvalidArgumentException
313
-     */
314
-    private function validateUploadedFiles(array $files): void
315
-    {
316
-        if ($files === []) {
317
-            return;
318
-        }
319
-
320
-        array_walk_recursive($files, /** @param mixed $file */ static function ($file): void {
321
-            if (!($file instanceof UploadedFileInterface)) {
322
-                throw new InvalidArgumentException('Invalid uploaded file');
323
-            }
324
-        });
325
-    }
326
-
327
-    /**
328
-     * Validates the given parsed body
329
-     *
330
-     * @param mixed $data
331
-     *
332
-     * @throws InvalidArgumentException
333
-     */
334
-    private function validateParsedBody($data): void
335
-    {
336
-        if ($data === null || is_array($data) || is_object($data)) {
337
-            return;
338
-        }
339
-
340
-        throw new InvalidArgumentException('Invalid parsed body');
341
-    }
29
+	/**
30
+	 * @var array<array-key, mixed>
31
+	 */
32
+	private array $serverParams;
33
+
34
+	/**
35
+	 * @var array<array-key, mixed>
36
+	 */
37
+	private array $queryParams;
38
+
39
+	/**
40
+	 * @var array<array-key, mixed>
41
+	 */
42
+	private array $cookieParams;
43
+
44
+	/**
45
+	 * @var array<array-key, mixed>
46
+	 */
47
+	private array $uploadedFiles = [];
48
+
49
+	/**
50
+	 * @var array<array-key, mixed>|object|null
51
+	 */
52
+	private $parsedBody = null;
53
+
54
+	/**
55
+	 * @var array<array-key, mixed>
56
+	 */
57
+	private array $attributes;
58
+
59
+	/**
60
+	 * Constructor of the class
61
+	 *
62
+	 * @param mixed $uri
63
+	 * @param array<string, string|string[]>|null $headers
64
+	 *
65
+	 * @param array<array-key, mixed> $serverParams
66
+	 * @param array<array-key, mixed> $queryParams
67
+	 * @param array<array-key, mixed> $cookieParams
68
+	 * @param array<array-key, mixed> $uploadedFiles
69
+	 * @param array<array-key, mixed>|object|null $parsedBody
70
+	 * @param array<array-key, mixed> $attributes
71
+	 *
72
+	 * @throws InvalidArgumentException
73
+	 */
74
+	public function __construct(
75
+		?string $protocolVersion = null,
76
+		?string $method = null,
77
+		$uri = null,
78
+		?array $headers = null,
79
+		?StreamInterface $body = null,
80
+		array $serverParams = [],
81
+		array $queryParams = [],
82
+		array $cookieParams = [],
83
+		array $uploadedFiles = [],
84
+		$parsedBody = null,
85
+		array $attributes = []
86
+	) {
87
+		parent::__construct($method, $uri, $headers, $body);
88
+
89
+		if ($protocolVersion !== null) {
90
+			$this->setProtocolVersion($protocolVersion);
91
+		}
92
+
93
+		if ($uploadedFiles !== []) {
94
+			$this->setUploadedFiles($uploadedFiles);
95
+		}
96
+
97
+		if ($parsedBody !== null) {
98
+			$this->setParsedBody($parsedBody);
99
+		}
100
+
101
+		$this->serverParams = $serverParams;
102
+		$this->queryParams = $queryParams;
103
+		$this->cookieParams = $cookieParams;
104
+		$this->attributes = $attributes;
105
+	}
106
+
107
+	/**
108
+	 * {@inheritDoc}
109
+	 *
110
+	 * @return array<array-key, mixed>
111
+	 */
112
+	public function getServerParams(): array
113
+	{
114
+		return $this->serverParams;
115
+	}
116
+
117
+	/**
118
+	 * {@inheritDoc}
119
+	 *
120
+	 * @return array<array-key, mixed>
121
+	 */
122
+	public function getQueryParams(): array
123
+	{
124
+		return $this->queryParams;
125
+	}
126
+
127
+	/**
128
+	 * {@inheritDoc}
129
+	 *
130
+	 * @param array<array-key, mixed> $query
131
+	 *
132
+	 * @return static
133
+	 */
134
+	public function withQueryParams(array $query): ServerRequestInterface
135
+	{
136
+		$clone = clone $this;
137
+		$clone->queryParams = $query;
138
+
139
+		return $clone;
140
+	}
141
+
142
+	/**
143
+	 * {@inheritDoc}
144
+	 *
145
+	 * @return array<array-key, mixed>
146
+	 */
147
+	public function getCookieParams(): array
148
+	{
149
+		return $this->cookieParams;
150
+	}
151
+
152
+	/**
153
+	 * {@inheritDoc}
154
+	 *
155
+	 * @param array<array-key, mixed> $cookies
156
+	 *
157
+	 * @return static
158
+	 */
159
+	public function withCookieParams(array $cookies): ServerRequestInterface
160
+	{
161
+		$clone = clone $this;
162
+		$clone->cookieParams = $cookies;
163
+
164
+		return $clone;
165
+	}
166
+
167
+	/**
168
+	 * {@inheritDoc}
169
+	 *
170
+	 * @return array<array-key, mixed>
171
+	 */
172
+	public function getUploadedFiles(): array
173
+	{
174
+		return $this->uploadedFiles;
175
+	}
176
+
177
+	/**
178
+	 * {@inheritDoc}
179
+	 *
180
+	 * @param array<array-key, mixed> $uploadedFiles
181
+	 *
182
+	 * @return static
183
+	 *
184
+	 * @throws InvalidArgumentException
185
+	 */
186
+	public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
187
+	{
188
+		$clone = clone $this;
189
+		$clone->setUploadedFiles($uploadedFiles);
190
+
191
+		return $clone;
192
+	}
193
+
194
+	/**
195
+	 * {@inheritDoc}
196
+	 *
197
+	 * @return array<array-key, mixed>|object|null
198
+	 */
199
+	public function getParsedBody()
200
+	{
201
+		return $this->parsedBody;
202
+	}
203
+
204
+	/**
205
+	 * {@inheritDoc}
206
+	 *
207
+	 * @param array<array-key, mixed>|object|null $data
208
+	 *
209
+	 * @return static
210
+	 *
211
+	 * @throws InvalidArgumentException
212
+	 */
213
+	public function withParsedBody($data): ServerRequestInterface
214
+	{
215
+		$clone = clone $this;
216
+		$clone->setParsedBody($data);
217
+
218
+		return $clone;
219
+	}
220
+
221
+	/**
222
+	 * {@inheritDoc}
223
+	 *
224
+	 * @return array<array-key, mixed>
225
+	 */
226
+	public function getAttributes(): array
227
+	{
228
+		return $this->attributes;
229
+	}
230
+
231
+	/**
232
+	 * {@inheritDoc}
233
+	 *
234
+	 * @param array-key $name
235
+	 * @param mixed $default
236
+	 *
237
+	 * @return mixed
238
+	 */
239
+	public function getAttribute($name, $default = null)
240
+	{
241
+		if (!array_key_exists($name, $this->attributes)) {
242
+			return $default;
243
+		}
244
+
245
+		return $this->attributes[$name];
246
+	}
247
+
248
+	/**
249
+	 * {@inheritDoc}
250
+	 *
251
+	 * @param array-key $name
252
+	 * @param mixed $value
253
+	 *
254
+	 * @return static
255
+	 */
256
+	public function withAttribute($name, $value): ServerRequestInterface
257
+	{
258
+		$clone = clone $this;
259
+		$clone->attributes[$name] = $value;
260
+
261
+		return $clone;
262
+	}
263
+
264
+	/**
265
+	 * {@inheritDoc}
266
+	 *
267
+	 * @param array-key $name
268
+	 *
269
+	 * @return static
270
+	 */
271
+	public function withoutAttribute($name): ServerRequestInterface
272
+	{
273
+		$clone = clone $this;
274
+		unset($clone->attributes[$name]);
275
+
276
+		return $clone;
277
+	}
278
+
279
+	/**
280
+	 * Sets the given uploaded files to the request
281
+	 *
282
+	 * @param array<array-key, mixed> $files
283
+	 *
284
+	 * @throws InvalidArgumentException
285
+	 */
286
+	final protected function setUploadedFiles(array $files): void
287
+	{
288
+		$this->validateUploadedFiles($files);
289
+
290
+		$this->uploadedFiles = $files;
291
+	}
292
+
293
+	/**
294
+	 * Sets the given parsed body to the request
295
+	 *
296
+	 * @param array<array-key, mixed>|object|null $data
297
+	 *
298
+	 * @throws InvalidArgumentException
299
+	 */
300
+	final protected function setParsedBody($data): void
301
+	{
302
+		$this->validateParsedBody($data);
303
+
304
+		$this->parsedBody = $data;
305
+	}
306
+
307
+	/**
308
+	 * Validates the given uploaded files
309
+	 *
310
+	 * @param array<array-key, mixed> $files
311
+	 *
312
+	 * @throws InvalidArgumentException
313
+	 */
314
+	private function validateUploadedFiles(array $files): void
315
+	{
316
+		if ($files === []) {
317
+			return;
318
+		}
319
+
320
+		array_walk_recursive($files, /** @param mixed $file */ static function ($file): void {
321
+			if (!($file instanceof UploadedFileInterface)) {
322
+				throw new InvalidArgumentException('Invalid uploaded file');
323
+			}
324
+		});
325
+	}
326
+
327
+	/**
328
+	 * Validates the given parsed body
329
+	 *
330
+	 * @param mixed $data
331
+	 *
332
+	 * @throws InvalidArgumentException
333
+	 */
334
+	private function validateParsedBody($data): void
335
+	{
336
+		if ($data === null || is_array($data) || is_object($data)) {
337
+			return;
338
+		}
339
+
340
+		throw new InvalidArgumentException('Invalid parsed body');
341
+	}
342 342
 }
Please login to merge, or discard this patch.
resources/definitions/psr17.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -18,10 +18,10 @@
 block discarded – undo
18 18
 use function DI\create;
19 19
 
20 20
 return [
21
-    RequestFactoryInterface::class => create(RequestFactory::class),
22
-    ResponseFactoryInterface::class => create(ResponseFactory::class),
23
-    ServerRequestFactoryInterface::class => create(ServerRequestFactory::class),
24
-    StreamFactoryInterface::class => create(StreamFactory::class),
25
-    UploadedFileFactoryInterface::class => create(UploadedFileFactory::class),
26
-    UriFactoryInterface::class => create(UriFactory::class),
21
+	RequestFactoryInterface::class => create(RequestFactory::class),
22
+	ResponseFactoryInterface::class => create(ResponseFactory::class),
23
+	ServerRequestFactoryInterface::class => create(ServerRequestFactory::class),
24
+	StreamFactoryInterface::class => create(StreamFactory::class),
25
+	UploadedFileFactoryInterface::class => create(UploadedFileFactory::class),
26
+	UriFactoryInterface::class => create(UriFactory::class),
27 27
 ];
Please login to merge, or discard this patch.
src/Response/JsonResponse.php 1 patch
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -24,48 +24,48 @@
 block discarded – undo
24 24
 
25 25
 final class JsonResponse extends Response
26 26
 {
27
-    /**
28
-     * @param mixed $data
29
-     * @param int<1, max> $depth
30
-     * @psalm-param int<1, 2147483647> $depth
31
-     *
32
-     * @throws InvalidArgumentException
33
-     */
34
-    public function __construct(int $statusCode, $data, int $flags = 0, int $depth = 512)
35
-    {
36
-        parent::__construct($statusCode);
27
+	/**
28
+	 * @param mixed $data
29
+	 * @param int<1, max> $depth
30
+	 * @psalm-param int<1, 2147483647> $depth
31
+	 *
32
+	 * @throws InvalidArgumentException
33
+	 */
34
+	public function __construct(int $statusCode, $data, int $flags = 0, int $depth = 512)
35
+	{
36
+		parent::__construct($statusCode);
37 37
 
38
-        $this->setBody(self::createBody($data, $flags, $depth));
39
-        $this->setHeader('Content-Type', 'application/json; charset=utf-8');
40
-    }
38
+		$this->setBody(self::createBody($data, $flags, $depth));
39
+		$this->setHeader('Content-Type', 'application/json; charset=utf-8');
40
+	}
41 41
 
42
-    /**
43
-     * @param mixed $data
44
-     * @param int<1, max> $depth
45
-     * @psalm-param int<1, 2147483647> $depth
46
-     *
47
-     * @throws InvalidArgumentException
48
-     */
49
-    private static function createBody($data, int $flags, int $depth): StreamInterface
50
-    {
51
-        if ($data instanceof StreamInterface) {
52
-            return $data;
53
-        }
42
+	/**
43
+	 * @param mixed $data
44
+	 * @param int<1, max> $depth
45
+	 * @psalm-param int<1, 2147483647> $depth
46
+	 *
47
+	 * @throws InvalidArgumentException
48
+	 */
49
+	private static function createBody($data, int $flags, int $depth): StreamInterface
50
+	{
51
+		if ($data instanceof StreamInterface) {
52
+			return $data;
53
+		}
54 54
 
55
-        try {
56
-            /** @var non-empty-string $json */
57
-            $json = json_encode($data, $flags | JSON_THROW_ON_ERROR, $depth);
58
-        } catch (JsonException $e) {
59
-            throw new InvalidArgumentException(sprintf(
60
-                'Unable to create the JSON response due to an invalid data: %s',
61
-                $e->getMessage()
62
-            ), 0, $e);
63
-        }
55
+		try {
56
+			/** @var non-empty-string $json */
57
+			$json = json_encode($data, $flags | JSON_THROW_ON_ERROR, $depth);
58
+		} catch (JsonException $e) {
59
+			throw new InvalidArgumentException(sprintf(
60
+				'Unable to create the JSON response due to an invalid data: %s',
61
+				$e->getMessage()
62
+			), 0, $e);
63
+		}
64 64
 
65
-        $stream = new PhpTempStream('r+b');
66
-        $stream->write($json);
67
-        $stream->rewind();
65
+		$stream = new PhpTempStream('r+b');
66
+		$stream->write($json);
67
+		$stream->rewind();
68 68
 
69
-        return $stream;
70
-    }
69
+		return $stream;
70
+	}
71 71
 }
Please login to merge, or discard this patch.
src/Stream.php 1 patch
Indentation   +285 added lines, -285 removed lines patch added patch discarded remove patch
@@ -32,289 +32,289 @@
 block discarded – undo
32 32
 
33 33
 class Stream implements StreamInterface
34 34
 {
35
-    /**
36
-     * @var resource|null
37
-     */
38
-    private $resource;
39
-
40
-    private bool $autoClose;
41
-
42
-    /**
43
-     * @param mixed $resource
44
-     *
45
-     * @throws InvalidArgumentException
46
-     */
47
-    public function __construct($resource, bool $autoClose = true)
48
-    {
49
-        if (!is_resource($resource)) {
50
-            throw new InvalidArgumentException('Unexpected stream resource');
51
-        }
52
-
53
-        $this->resource = $resource;
54
-        $this->autoClose = $autoClose;
55
-    }
56
-
57
-    /**
58
-     * @param mixed $resource
59
-     *
60
-     * @throws InvalidArgumentException
61
-     */
62
-    public static function create($resource): StreamInterface
63
-    {
64
-        if ($resource instanceof StreamInterface) {
65
-            return $resource;
66
-        }
67
-
68
-        return new self($resource);
69
-    }
70
-
71
-    public function __destruct()
72
-    {
73
-        if ($this->autoClose) {
74
-            $this->close();
75
-        }
76
-    }
77
-
78
-    /**
79
-     * @inheritDoc
80
-     */
81
-    public function detach()
82
-    {
83
-        $resource = $this->resource;
84
-        $this->resource = null;
85
-
86
-        return $resource;
87
-    }
88
-
89
-    /**
90
-     * @inheritDoc
91
-     */
92
-    public function close(): void
93
-    {
94
-        $resource = $this->detach();
95
-        if (!is_resource($resource)) {
96
-            return;
97
-        }
98
-
99
-        fclose($resource);
100
-    }
101
-
102
-    /**
103
-     * @inheritDoc
104
-     */
105
-    public function eof(): bool
106
-    {
107
-        if (!is_resource($this->resource)) {
108
-            return true;
109
-        }
110
-
111
-        return feof($this->resource);
112
-    }
113
-
114
-    /**
115
-     * @inheritDoc
116
-     */
117
-    public function tell(): int
118
-    {
119
-        if (!is_resource($this->resource)) {
120
-            throw new RuntimeException('Stream has no resource');
121
-        }
122
-
123
-        $result = ftell($this->resource);
124
-        if ($result === false) {
125
-            throw new RuntimeException('Unable to get the stream pointer position');
126
-        }
127
-
128
-        return $result;
129
-    }
130
-
131
-    /**
132
-     * @inheritDoc
133
-     */
134
-    public function isSeekable(): bool
135
-    {
136
-        if (!is_resource($this->resource)) {
137
-            return false;
138
-        }
139
-
140
-        $metadata = stream_get_meta_data($this->resource);
141
-
142
-        return $metadata['seekable'];
143
-    }
144
-
145
-    /**
146
-     * @inheritDoc
147
-     */
148
-    public function rewind(): void
149
-    {
150
-        $this->seek(0);
151
-    }
152
-
153
-    /**
154
-     * @inheritDoc
155
-     */
156
-    public function seek($offset, $whence = SEEK_SET): void
157
-    {
158
-        if (!is_resource($this->resource)) {
159
-            throw new RuntimeException('Stream has no resource');
160
-        }
161
-
162
-        if (!$this->isSeekable()) {
163
-            throw new RuntimeException('Stream is not seekable');
164
-        }
165
-
166
-        $result = fseek($this->resource, $offset, $whence);
167
-        if ($result !== 0) {
168
-            throw new RuntimeException('Unable to move the stream pointer position');
169
-        }
170
-    }
171
-
172
-    /**
173
-     * @inheritDoc
174
-     */
175
-    public function isWritable(): bool
176
-    {
177
-        if (!is_resource($this->resource)) {
178
-            return false;
179
-        }
180
-
181
-        $metadata = stream_get_meta_data($this->resource);
182
-
183
-        return strpbrk($metadata['mode'], '+acwx') !== false;
184
-    }
185
-
186
-    /**
187
-     * @inheritDoc
188
-     */
189
-    public function write($string): int
190
-    {
191
-        if (!is_resource($this->resource)) {
192
-            throw new RuntimeException('Stream has no resource');
193
-        }
194
-
195
-        if (!$this->isWritable()) {
196
-            throw new RuntimeException('Stream is not writable');
197
-        }
198
-
199
-        $result = fwrite($this->resource, $string);
200
-        if ($result === false) {
201
-            throw new RuntimeException('Unable to write to the stream');
202
-        }
203
-
204
-        return $result;
205
-    }
206
-
207
-    /**
208
-     * @inheritDoc
209
-     */
210
-    public function isReadable(): bool
211
-    {
212
-        if (!is_resource($this->resource)) {
213
-            return false;
214
-        }
215
-
216
-        $metadata = stream_get_meta_data($this->resource);
217
-
218
-        return strpbrk($metadata['mode'], '+r') !== false;
219
-    }
220
-
221
-    /**
222
-     * @inheritDoc
223
-     *
224
-     * @psalm-param int $length
225
-     * @phpstan-param int<1, max> $length
226
-     */
227
-    public function read($length): string
228
-    {
229
-        if (!is_resource($this->resource)) {
230
-            throw new RuntimeException('Stream has no resource');
231
-        }
232
-
233
-        if (!$this->isReadable()) {
234
-            throw new RuntimeException('Stream is not readable');
235
-        }
236
-
237
-        $result = fread($this->resource, $length);
238
-        if ($result === false) {
239
-            throw new RuntimeException('Unable to read from the stream');
240
-        }
241
-
242
-        return $result;
243
-    }
244
-
245
-    /**
246
-     * @inheritDoc
247
-     */
248
-    public function getContents(): string
249
-    {
250
-        if (!is_resource($this->resource)) {
251
-            throw new RuntimeException('Stream has no resource');
252
-        }
253
-
254
-        if (!$this->isReadable()) {
255
-            throw new RuntimeException('Stream is not readable');
256
-        }
257
-
258
-        $result = stream_get_contents($this->resource);
259
-        if ($result === false) {
260
-            throw new RuntimeException('Unable to read the remainder of the stream');
261
-        }
262
-
263
-        return $result;
264
-    }
265
-
266
-    /**
267
-     * @inheritDoc
268
-     */
269
-    public function getMetadata($key = null)
270
-    {
271
-        if (!is_resource($this->resource)) {
272
-            return null;
273
-        }
274
-
275
-        $metadata = stream_get_meta_data($this->resource);
276
-        if ($key === null) {
277
-            return $metadata;
278
-        }
279
-
280
-        return $metadata[$key] ?? null;
281
-    }
282
-
283
-    /**
284
-     * @inheritDoc
285
-     */
286
-    public function getSize(): ?int
287
-    {
288
-        if (!is_resource($this->resource)) {
289
-            return null;
290
-        }
291
-
292
-        /** @var array{size: int}|false */
293
-        $stats = fstat($this->resource);
294
-        if ($stats === false) {
295
-            return null;
296
-        }
297
-
298
-        return $stats['size'];
299
-    }
300
-
301
-    /**
302
-     * @inheritDoc
303
-     */
304
-    public function __toString(): string
305
-    {
306
-        if (!$this->isReadable()) {
307
-            return '';
308
-        }
309
-
310
-        try {
311
-            if ($this->isSeekable()) {
312
-                $this->rewind();
313
-            }
314
-
315
-            return $this->getContents();
316
-        } catch (Throwable $e) {
317
-            return '';
318
-        }
319
-    }
35
+	/**
36
+	 * @var resource|null
37
+	 */
38
+	private $resource;
39
+
40
+	private bool $autoClose;
41
+
42
+	/**
43
+	 * @param mixed $resource
44
+	 *
45
+	 * @throws InvalidArgumentException
46
+	 */
47
+	public function __construct($resource, bool $autoClose = true)
48
+	{
49
+		if (!is_resource($resource)) {
50
+			throw new InvalidArgumentException('Unexpected stream resource');
51
+		}
52
+
53
+		$this->resource = $resource;
54
+		$this->autoClose = $autoClose;
55
+	}
56
+
57
+	/**
58
+	 * @param mixed $resource
59
+	 *
60
+	 * @throws InvalidArgumentException
61
+	 */
62
+	public static function create($resource): StreamInterface
63
+	{
64
+		if ($resource instanceof StreamInterface) {
65
+			return $resource;
66
+		}
67
+
68
+		return new self($resource);
69
+	}
70
+
71
+	public function __destruct()
72
+	{
73
+		if ($this->autoClose) {
74
+			$this->close();
75
+		}
76
+	}
77
+
78
+	/**
79
+	 * @inheritDoc
80
+	 */
81
+	public function detach()
82
+	{
83
+		$resource = $this->resource;
84
+		$this->resource = null;
85
+
86
+		return $resource;
87
+	}
88
+
89
+	/**
90
+	 * @inheritDoc
91
+	 */
92
+	public function close(): void
93
+	{
94
+		$resource = $this->detach();
95
+		if (!is_resource($resource)) {
96
+			return;
97
+		}
98
+
99
+		fclose($resource);
100
+	}
101
+
102
+	/**
103
+	 * @inheritDoc
104
+	 */
105
+	public function eof(): bool
106
+	{
107
+		if (!is_resource($this->resource)) {
108
+			return true;
109
+		}
110
+
111
+		return feof($this->resource);
112
+	}
113
+
114
+	/**
115
+	 * @inheritDoc
116
+	 */
117
+	public function tell(): int
118
+	{
119
+		if (!is_resource($this->resource)) {
120
+			throw new RuntimeException('Stream has no resource');
121
+		}
122
+
123
+		$result = ftell($this->resource);
124
+		if ($result === false) {
125
+			throw new RuntimeException('Unable to get the stream pointer position');
126
+		}
127
+
128
+		return $result;
129
+	}
130
+
131
+	/**
132
+	 * @inheritDoc
133
+	 */
134
+	public function isSeekable(): bool
135
+	{
136
+		if (!is_resource($this->resource)) {
137
+			return false;
138
+		}
139
+
140
+		$metadata = stream_get_meta_data($this->resource);
141
+
142
+		return $metadata['seekable'];
143
+	}
144
+
145
+	/**
146
+	 * @inheritDoc
147
+	 */
148
+	public function rewind(): void
149
+	{
150
+		$this->seek(0);
151
+	}
152
+
153
+	/**
154
+	 * @inheritDoc
155
+	 */
156
+	public function seek($offset, $whence = SEEK_SET): void
157
+	{
158
+		if (!is_resource($this->resource)) {
159
+			throw new RuntimeException('Stream has no resource');
160
+		}
161
+
162
+		if (!$this->isSeekable()) {
163
+			throw new RuntimeException('Stream is not seekable');
164
+		}
165
+
166
+		$result = fseek($this->resource, $offset, $whence);
167
+		if ($result !== 0) {
168
+			throw new RuntimeException('Unable to move the stream pointer position');
169
+		}
170
+	}
171
+
172
+	/**
173
+	 * @inheritDoc
174
+	 */
175
+	public function isWritable(): bool
176
+	{
177
+		if (!is_resource($this->resource)) {
178
+			return false;
179
+		}
180
+
181
+		$metadata = stream_get_meta_data($this->resource);
182
+
183
+		return strpbrk($metadata['mode'], '+acwx') !== false;
184
+	}
185
+
186
+	/**
187
+	 * @inheritDoc
188
+	 */
189
+	public function write($string): int
190
+	{
191
+		if (!is_resource($this->resource)) {
192
+			throw new RuntimeException('Stream has no resource');
193
+		}
194
+
195
+		if (!$this->isWritable()) {
196
+			throw new RuntimeException('Stream is not writable');
197
+		}
198
+
199
+		$result = fwrite($this->resource, $string);
200
+		if ($result === false) {
201
+			throw new RuntimeException('Unable to write to the stream');
202
+		}
203
+
204
+		return $result;
205
+	}
206
+
207
+	/**
208
+	 * @inheritDoc
209
+	 */
210
+	public function isReadable(): bool
211
+	{
212
+		if (!is_resource($this->resource)) {
213
+			return false;
214
+		}
215
+
216
+		$metadata = stream_get_meta_data($this->resource);
217
+
218
+		return strpbrk($metadata['mode'], '+r') !== false;
219
+	}
220
+
221
+	/**
222
+	 * @inheritDoc
223
+	 *
224
+	 * @psalm-param int $length
225
+	 * @phpstan-param int<1, max> $length
226
+	 */
227
+	public function read($length): string
228
+	{
229
+		if (!is_resource($this->resource)) {
230
+			throw new RuntimeException('Stream has no resource');
231
+		}
232
+
233
+		if (!$this->isReadable()) {
234
+			throw new RuntimeException('Stream is not readable');
235
+		}
236
+
237
+		$result = fread($this->resource, $length);
238
+		if ($result === false) {
239
+			throw new RuntimeException('Unable to read from the stream');
240
+		}
241
+
242
+		return $result;
243
+	}
244
+
245
+	/**
246
+	 * @inheritDoc
247
+	 */
248
+	public function getContents(): string
249
+	{
250
+		if (!is_resource($this->resource)) {
251
+			throw new RuntimeException('Stream has no resource');
252
+		}
253
+
254
+		if (!$this->isReadable()) {
255
+			throw new RuntimeException('Stream is not readable');
256
+		}
257
+
258
+		$result = stream_get_contents($this->resource);
259
+		if ($result === false) {
260
+			throw new RuntimeException('Unable to read the remainder of the stream');
261
+		}
262
+
263
+		return $result;
264
+	}
265
+
266
+	/**
267
+	 * @inheritDoc
268
+	 */
269
+	public function getMetadata($key = null)
270
+	{
271
+		if (!is_resource($this->resource)) {
272
+			return null;
273
+		}
274
+
275
+		$metadata = stream_get_meta_data($this->resource);
276
+		if ($key === null) {
277
+			return $metadata;
278
+		}
279
+
280
+		return $metadata[$key] ?? null;
281
+	}
282
+
283
+	/**
284
+	 * @inheritDoc
285
+	 */
286
+	public function getSize(): ?int
287
+	{
288
+		if (!is_resource($this->resource)) {
289
+			return null;
290
+		}
291
+
292
+		/** @var array{size: int}|false */
293
+		$stats = fstat($this->resource);
294
+		if ($stats === false) {
295
+			return null;
296
+		}
297
+
298
+		return $stats['size'];
299
+	}
300
+
301
+	/**
302
+	 * @inheritDoc
303
+	 */
304
+	public function __toString(): string
305
+	{
306
+		if (!$this->isReadable()) {
307
+			return '';
308
+		}
309
+
310
+		try {
311
+			if ($this->isSeekable()) {
312
+				$this->rewind();
313
+			}
314
+
315
+			return $this->getContents();
316
+		} catch (Throwable $e) {
317
+			return '';
318
+		}
319
+	}
320 320
 }
Please login to merge, or discard this patch.