Passed
Pull Request — master (#31)
by Anatoly
39:16
created
src/Stream/TempFileStream.php 1 patch
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -32,30 +32,30 @@
 block discarded – undo
32 32
 final class TempFileStream extends Stream
33 33
 {
34 34
 
35
-    /**
36
-     * Constructor of the class
37
-     *
38
-     * @param string $prefix
39
-     *
40
-     * @throws RuntimeException
41
-     */
42
-    public function __construct(string $prefix = '')
43
-    {
44
-        $dirname = sys_get_temp_dir();
45
-        if (!is_writable($dirname)) {
46
-            throw new RuntimeException('Temporary files directory is not writable');
47
-        }
48
-
49
-        $filename = tempnam($dirname, $prefix);
50
-        if ($filename === false) {
51
-            throw new RuntimeException('Temporary file name cannot be generated');
52
-        }
53
-
54
-        $resource = fopen($filename, 'w+b');
55
-        if (!is_resource($resource)) {
56
-            throw new RuntimeException('Temporary file cannot be created or opened');
57
-        }
58
-
59
-        parent::__construct($resource);
60
-    }
35
+	/**
36
+	 * Constructor of the class
37
+	 *
38
+	 * @param string $prefix
39
+	 *
40
+	 * @throws RuntimeException
41
+	 */
42
+	public function __construct(string $prefix = '')
43
+	{
44
+		$dirname = sys_get_temp_dir();
45
+		if (!is_writable($dirname)) {
46
+			throw new RuntimeException('Temporary files directory is not writable');
47
+		}
48
+
49
+		$filename = tempnam($dirname, $prefix);
50
+		if ($filename === false) {
51
+			throw new RuntimeException('Temporary file name cannot be generated');
52
+		}
53
+
54
+		$resource = fopen($filename, 'w+b');
55
+		if (!is_resource($resource)) {
56
+			throw new RuntimeException('Temporary file cannot be created or opened');
57
+		}
58
+
59
+		parent::__construct($resource);
60
+	}
61 61
 }
Please login to merge, or discard this patch.
src/Response/HtmlResponse.php 1 patch
Indentation   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -32,51 +32,51 @@
 block discarded – undo
32 32
 final class HtmlResponse extends Response
33 33
 {
34 34
 
35
-    /**
36
-     * Constructor of the class
37
-     *
38
-     * @param int $statusCode
39
-     * @param mixed $html
40
-     *
41
-     * @throws InvalidArgumentException
42
-     */
43
-    public function __construct(int $statusCode, $html)
44
-    {
45
-        parent::__construct($statusCode);
35
+	/**
36
+	 * Constructor of the class
37
+	 *
38
+	 * @param int $statusCode
39
+	 * @param mixed $html
40
+	 *
41
+	 * @throws InvalidArgumentException
42
+	 */
43
+	public function __construct(int $statusCode, $html)
44
+	{
45
+		parent::__construct($statusCode);
46 46
 
47
-        $this->setBody($this->createBody($html));
47
+		$this->setBody($this->createBody($html));
48 48
 
49
-        $this->setHeader('Content-Type', 'text/html; charset=utf-8');
50
-    }
49
+		$this->setHeader('Content-Type', 'text/html; charset=utf-8');
50
+	}
51 51
 
52
-    /**
53
-     * Creates the response body from the given HTML data
54
-     *
55
-     * @param mixed $html
56
-     *
57
-     * @return StreamInterface
58
-     *
59
-     * @throws InvalidArgumentException
60
-     */
61
-    private function createBody($html): StreamInterface
62
-    {
63
-        if ($html instanceof StreamInterface) {
64
-            return $html;
65
-        }
52
+	/**
53
+	 * Creates the response body from the given HTML data
54
+	 *
55
+	 * @param mixed $html
56
+	 *
57
+	 * @return StreamInterface
58
+	 *
59
+	 * @throws InvalidArgumentException
60
+	 */
61
+	private function createBody($html): StreamInterface
62
+	{
63
+		if ($html instanceof StreamInterface) {
64
+			return $html;
65
+		}
66 66
 
67
-        if (is_object($html) && method_exists($html, '__toString')) {
68
-            /** @var string */
69
-            $html = $html->__toString();
70
-        }
67
+		if (is_object($html) && method_exists($html, '__toString')) {
68
+			/** @var string */
69
+			$html = $html->__toString();
70
+		}
71 71
 
72
-        if (!is_string($html)) {
73
-            throw new InvalidArgumentException('Unable to create HTML response due to unexpected HTML data');
74
-        }
72
+		if (!is_string($html)) {
73
+			throw new InvalidArgumentException('Unable to create HTML response due to unexpected HTML data');
74
+		}
75 75
 
76
-        $stream = new PhpTempStream('r+b');
77
-        $stream->write($html);
78
-        $stream->rewind();
76
+		$stream = new PhpTempStream('r+b');
77
+		$stream->write($html);
78
+		$stream->rewind();
79 79
 
80
-        return $stream;
81
-    }
80
+		return $stream;
81
+	}
82 82
 }
Please login to merge, or discard this patch.
src/UploadedFile.php 1 patch
Indentation   +225 added lines, -225 removed lines patch added patch discarded remove patch
@@ -49,229 +49,229 @@
 block discarded – undo
49 49
 class UploadedFile implements UploadedFileInterface
50 50
 {
51 51
 
52
-    /**
53
-     * List of upload errors
54
-     *
55
-     * @link https://www.php.net/manual/en/features.file-upload.errors.php
56
-     *
57
-     * @var array<int, string>
58
-     */
59
-    public const UPLOAD_ERRORS = [
60
-        UPLOAD_ERR_OK         => 'No error',
61
-        UPLOAD_ERR_INI_SIZE   => 'Uploaded file exceeds the upload_max_filesize directive in the php.ini',
62
-        UPLOAD_ERR_FORM_SIZE  => 'Uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form',
63
-        UPLOAD_ERR_PARTIAL    => 'Uploaded file was only partially uploaded',
64
-        UPLOAD_ERR_NO_FILE    => 'No file was uploaded',
65
-        UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary directory',
66
-        UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
67
-        UPLOAD_ERR_EXTENSION  => 'File upload was stopped by a PHP extension',
68
-    ];
69
-
70
-    /**
71
-     * The file stream
72
-     *
73
-     * @var StreamInterface|null
74
-     */
75
-    private ?StreamInterface $stream = null;
76
-
77
-    /**
78
-     * The file size
79
-     *
80
-     * @var int|null
81
-     */
82
-    private ?int $size;
83
-
84
-    /**
85
-     * The file's error code
86
-     *
87
-     * @var int
88
-     */
89
-    private int $errorCode;
90
-
91
-    /**
92
-     * The file's error message
93
-     *
94
-     * @var string
95
-     */
96
-    private string $errorMessage;
97
-
98
-    /**
99
-     * The client's file name
100
-     *
101
-     * @var string|null
102
-     */
103
-    private ?string $clientFilename;
104
-
105
-    /**
106
-     * The client's file media type
107
-     *
108
-     * @var string|null
109
-     */
110
-    private ?string $clientMediaType;
111
-
112
-    /**
113
-     * Constructor of the class
114
-     *
115
-     * @param StreamInterface|null $stream
116
-     * @param int|null $size
117
-     * @param int $error
118
-     * @param string|null $clientFilename
119
-     * @param string|null $clientMediaType
120
-     */
121
-    public function __construct(
122
-        ?StreamInterface $stream,
123
-        ?int $size = null,
124
-        int $error = UPLOAD_ERR_OK,
125
-        ?string $clientFilename = null,
126
-        ?string $clientMediaType = null
127
-    ) {
128
-        if (UPLOAD_ERR_OK === $error) {
129
-            $this->stream = $stream;
130
-        }
131
-
132
-        $this->size = $size;
133
-        $this->errorCode = $error;
134
-        $this->errorMessage = self::UPLOAD_ERRORS[$error] ?? 'Unknown error';
135
-        $this->clientFilename = $clientFilename;
136
-        $this->clientMediaType = $clientMediaType;
137
-    }
138
-
139
-    /**
140
-     * Gets the file stream
141
-     *
142
-     * @return StreamInterface
143
-     *
144
-     * @throws RuntimeException
145
-     *         - If the file has no a stream due to an error;
146
-     *         - If the file was already moved.
147
-     */
148
-    public function getStream(): StreamInterface
149
-    {
150
-        if (UPLOAD_ERR_OK <> $this->errorCode) {
151
-            throw new RuntimeException(sprintf(
152
-                'Uploaded file has no a stream due to the error #%d (%s)',
153
-                $this->errorCode,
154
-                $this->errorMessage
155
-            ));
156
-        }
157
-
158
-        if (!isset($this->stream)) {
159
-            throw new RuntimeException(
160
-                'Uploaded file has no a stream because it was already moved'
161
-            );
162
-        }
163
-
164
-        return $this->stream;
165
-    }
166
-
167
-    /**
168
-     * Moves the file to the given path
169
-     *
170
-     * @param string $targetPath
171
-     *
172
-     * @return void
173
-     *
174
-     * @throws RuntimeException
175
-     *         - If the file has no a stream due to an error;
176
-     *         - If the file was already moved;
177
-     *         - If the file cannot be read;
178
-     *         - If the target path cannot be used.
179
-     */
180
-    public function moveTo($targetPath): void
181
-    {
182
-        if (UPLOAD_ERR_OK <> $this->errorCode) {
183
-            throw new RuntimeException(sprintf(
184
-                'Uploaded file cannot be moved due to the error #%d (%s)',
185
-                $this->errorCode,
186
-                $this->errorMessage
187
-            ));
188
-        }
189
-
190
-        if (!isset($this->stream)) {
191
-            throw new RuntimeException(
192
-                'Uploaded file cannot be moved because it was already moved'
193
-            );
194
-        }
195
-
196
-        if (!$this->stream->isReadable()) {
197
-            throw new RuntimeException(
198
-                'Uploaded file cannot be moved because it is not readable'
199
-            );
200
-        }
201
-
202
-        $targetDir = dirname($targetPath);
203
-        if (!is_dir($targetDir) || !is_writable($targetDir)) {
204
-            throw new RuntimeException(sprintf(
205
-                'Uploaded file cannot be moved because the directory "%s" is not writable',
206
-                $targetDir
207
-            ));
208
-        }
209
-
210
-        $targetStream = new FileStream($targetPath, 'wb');
211
-
212
-        if ($this->stream->isSeekable()) {
213
-            $this->stream->rewind();
214
-        }
215
-
216
-        while (!$this->stream->eof()) {
217
-            $targetStream->write(
218
-                $this->stream->read(4096)
219
-            );
220
-        }
221
-
222
-        $targetStream->close();
223
-
224
-        /** @var string|null */
225
-        $sourcePath = $this->stream->getMetadata('uri');
226
-
227
-        $this->stream->close();
228
-        $this->stream = null;
229
-
230
-        if (isset($sourcePath) && is_file($sourcePath)) {
231
-            $sourceDir = dirname($sourcePath);
232
-            if (is_writable($sourceDir)) {
233
-                unlink($sourcePath);
234
-            }
235
-        }
236
-    }
237
-
238
-    /**
239
-     * Gets the file size
240
-     *
241
-     * @return int|null
242
-     */
243
-    public function getSize(): ?int
244
-    {
245
-        return $this->size;
246
-    }
247
-
248
-    /**
249
-     * Gets the file's error code
250
-     *
251
-     * @return int
252
-     */
253
-    public function getError(): int
254
-    {
255
-        return $this->errorCode;
256
-    }
257
-
258
-    /**
259
-     * Gets the client's file name
260
-     *
261
-     * @return string|null
262
-     */
263
-    public function getClientFilename(): ?string
264
-    {
265
-        return $this->clientFilename;
266
-    }
267
-
268
-    /**
269
-     * Gets the client's file media type
270
-     *
271
-     * @return string|null
272
-     */
273
-    public function getClientMediaType(): ?string
274
-    {
275
-        return $this->clientMediaType;
276
-    }
52
+	/**
53
+	 * List of upload errors
54
+	 *
55
+	 * @link https://www.php.net/manual/en/features.file-upload.errors.php
56
+	 *
57
+	 * @var array<int, string>
58
+	 */
59
+	public const UPLOAD_ERRORS = [
60
+		UPLOAD_ERR_OK         => 'No error',
61
+		UPLOAD_ERR_INI_SIZE   => 'Uploaded file exceeds the upload_max_filesize directive in the php.ini',
62
+		UPLOAD_ERR_FORM_SIZE  => 'Uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form',
63
+		UPLOAD_ERR_PARTIAL    => 'Uploaded file was only partially uploaded',
64
+		UPLOAD_ERR_NO_FILE    => 'No file was uploaded',
65
+		UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary directory',
66
+		UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
67
+		UPLOAD_ERR_EXTENSION  => 'File upload was stopped by a PHP extension',
68
+	];
69
+
70
+	/**
71
+	 * The file stream
72
+	 *
73
+	 * @var StreamInterface|null
74
+	 */
75
+	private ?StreamInterface $stream = null;
76
+
77
+	/**
78
+	 * The file size
79
+	 *
80
+	 * @var int|null
81
+	 */
82
+	private ?int $size;
83
+
84
+	/**
85
+	 * The file's error code
86
+	 *
87
+	 * @var int
88
+	 */
89
+	private int $errorCode;
90
+
91
+	/**
92
+	 * The file's error message
93
+	 *
94
+	 * @var string
95
+	 */
96
+	private string $errorMessage;
97
+
98
+	/**
99
+	 * The client's file name
100
+	 *
101
+	 * @var string|null
102
+	 */
103
+	private ?string $clientFilename;
104
+
105
+	/**
106
+	 * The client's file media type
107
+	 *
108
+	 * @var string|null
109
+	 */
110
+	private ?string $clientMediaType;
111
+
112
+	/**
113
+	 * Constructor of the class
114
+	 *
115
+	 * @param StreamInterface|null $stream
116
+	 * @param int|null $size
117
+	 * @param int $error
118
+	 * @param string|null $clientFilename
119
+	 * @param string|null $clientMediaType
120
+	 */
121
+	public function __construct(
122
+		?StreamInterface $stream,
123
+		?int $size = null,
124
+		int $error = UPLOAD_ERR_OK,
125
+		?string $clientFilename = null,
126
+		?string $clientMediaType = null
127
+	) {
128
+		if (UPLOAD_ERR_OK === $error) {
129
+			$this->stream = $stream;
130
+		}
131
+
132
+		$this->size = $size;
133
+		$this->errorCode = $error;
134
+		$this->errorMessage = self::UPLOAD_ERRORS[$error] ?? 'Unknown error';
135
+		$this->clientFilename = $clientFilename;
136
+		$this->clientMediaType = $clientMediaType;
137
+	}
138
+
139
+	/**
140
+	 * Gets the file stream
141
+	 *
142
+	 * @return StreamInterface
143
+	 *
144
+	 * @throws RuntimeException
145
+	 *         - If the file has no a stream due to an error;
146
+	 *         - If the file was already moved.
147
+	 */
148
+	public function getStream(): StreamInterface
149
+	{
150
+		if (UPLOAD_ERR_OK <> $this->errorCode) {
151
+			throw new RuntimeException(sprintf(
152
+				'Uploaded file has no a stream due to the error #%d (%s)',
153
+				$this->errorCode,
154
+				$this->errorMessage
155
+			));
156
+		}
157
+
158
+		if (!isset($this->stream)) {
159
+			throw new RuntimeException(
160
+				'Uploaded file has no a stream because it was already moved'
161
+			);
162
+		}
163
+
164
+		return $this->stream;
165
+	}
166
+
167
+	/**
168
+	 * Moves the file to the given path
169
+	 *
170
+	 * @param string $targetPath
171
+	 *
172
+	 * @return void
173
+	 *
174
+	 * @throws RuntimeException
175
+	 *         - If the file has no a stream due to an error;
176
+	 *         - If the file was already moved;
177
+	 *         - If the file cannot be read;
178
+	 *         - If the target path cannot be used.
179
+	 */
180
+	public function moveTo($targetPath): void
181
+	{
182
+		if (UPLOAD_ERR_OK <> $this->errorCode) {
183
+			throw new RuntimeException(sprintf(
184
+				'Uploaded file cannot be moved due to the error #%d (%s)',
185
+				$this->errorCode,
186
+				$this->errorMessage
187
+			));
188
+		}
189
+
190
+		if (!isset($this->stream)) {
191
+			throw new RuntimeException(
192
+				'Uploaded file cannot be moved because it was already moved'
193
+			);
194
+		}
195
+
196
+		if (!$this->stream->isReadable()) {
197
+			throw new RuntimeException(
198
+				'Uploaded file cannot be moved because it is not readable'
199
+			);
200
+		}
201
+
202
+		$targetDir = dirname($targetPath);
203
+		if (!is_dir($targetDir) || !is_writable($targetDir)) {
204
+			throw new RuntimeException(sprintf(
205
+				'Uploaded file cannot be moved because the directory "%s" is not writable',
206
+				$targetDir
207
+			));
208
+		}
209
+
210
+		$targetStream = new FileStream($targetPath, 'wb');
211
+
212
+		if ($this->stream->isSeekable()) {
213
+			$this->stream->rewind();
214
+		}
215
+
216
+		while (!$this->stream->eof()) {
217
+			$targetStream->write(
218
+				$this->stream->read(4096)
219
+			);
220
+		}
221
+
222
+		$targetStream->close();
223
+
224
+		/** @var string|null */
225
+		$sourcePath = $this->stream->getMetadata('uri');
226
+
227
+		$this->stream->close();
228
+		$this->stream = null;
229
+
230
+		if (isset($sourcePath) && is_file($sourcePath)) {
231
+			$sourceDir = dirname($sourcePath);
232
+			if (is_writable($sourceDir)) {
233
+				unlink($sourcePath);
234
+			}
235
+		}
236
+	}
237
+
238
+	/**
239
+	 * Gets the file size
240
+	 *
241
+	 * @return int|null
242
+	 */
243
+	public function getSize(): ?int
244
+	{
245
+		return $this->size;
246
+	}
247
+
248
+	/**
249
+	 * Gets the file's error code
250
+	 *
251
+	 * @return int
252
+	 */
253
+	public function getError(): int
254
+	{
255
+		return $this->errorCode;
256
+	}
257
+
258
+	/**
259
+	 * Gets the client's file name
260
+	 *
261
+	 * @return string|null
262
+	 */
263
+	public function getClientFilename(): ?string
264
+	{
265
+		return $this->clientFilename;
266
+	}
267
+
268
+	/**
269
+	 * Gets the client's file media type
270
+	 *
271
+	 * @return string|null
272
+	 */
273
+	public function getClientMediaType(): ?string
274
+	{
275
+		return $this->clientMediaType;
276
+	}
277 277
 }
Please login to merge, or discard this patch.
src/Request.php 1 patch
Indentation   +281 added lines, -281 removed lines patch added patch discarded remove patch
@@ -36,285 +36,285 @@
 block discarded – undo
36 36
 class Request extends Message implements RequestInterface, RequestMethodInterface
37 37
 {
38 38
 
39
-    /**
40
-     * Regular Expression used for a request target validation
41
-     *
42
-     * @var string
43
-     */
44
-    public const RFC7230_REQUEST_TARGET_REGEX = '/^[\x21-\x7E\x80-\xFF]+$/';
45
-
46
-    /**
47
-     * The request method (aka verb)
48
-     *
49
-     * @var string
50
-     */
51
-    private string $method = self::METHOD_GET;
52
-
53
-    /**
54
-     * The request URI
55
-     *
56
-     * @var UriInterface
57
-     */
58
-    private UriInterface $uri;
59
-
60
-    /**
61
-     * The request target
62
-     *
63
-     * @var string|null
64
-     */
65
-    private ?string $requestTarget = null;
66
-
67
-    /**
68
-     * Constructor of the class
69
-     *
70
-     * @param string|null $method
71
-     * @param mixed $uri
72
-     * @param array<string, string|string[]>|null $headers
73
-     * @param StreamInterface|null $body
74
-     *
75
-     * @throws InvalidArgumentException
76
-     *         If one of the arguments isn't valid.
77
-     */
78
-    public function __construct(
79
-        ?string $method = null,
80
-        $uri = null,
81
-        ?array $headers = null,
82
-        ?StreamInterface $body = null
83
-    ) {
84
-        if (isset($method)) {
85
-            $this->setMethod($method);
86
-        }
87
-
88
-        $this->setUri($uri ?? '/');
89
-
90
-        if (isset($headers)) {
91
-            $this->setHeaders($headers);
92
-        }
93
-
94
-        if (isset($body)) {
95
-            $this->setBody($body);
96
-        }
97
-    }
98
-
99
-    /**
100
-     * Gets the request method
101
-     *
102
-     * @return string
103
-     */
104
-    public function getMethod(): string
105
-    {
106
-        return $this->method;
107
-    }
108
-
109
-    /**
110
-     * Creates a new instance of the request with the given method
111
-     *
112
-     * @param string $method
113
-     *
114
-     * @return static
115
-     *
116
-     * @throws InvalidArgumentException
117
-     *         If the method isn't valid.
118
-     */
119
-    public function withMethod($method): RequestInterface
120
-    {
121
-        $clone = clone $this;
122
-        $clone->setMethod($method);
123
-
124
-        return $clone;
125
-    }
126
-
127
-    /**
128
-     * Gets the request URI
129
-     *
130
-     * @return UriInterface
131
-     */
132
-    public function getUri(): UriInterface
133
-    {
134
-        return $this->uri;
135
-    }
136
-
137
-    /**
138
-     * Creates a new instance of the request with the given URI
139
-     *
140
-     * @param UriInterface $uri
141
-     * @param bool $preserveHost
142
-     *
143
-     * @return static
144
-     */
145
-    public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
146
-    {
147
-        $clone = clone $this;
148
-        $clone->setUri($uri, $preserveHost);
149
-
150
-        return $clone;
151
-    }
152
-
153
-    /**
154
-     * Gets the request target
155
-     *
156
-     * @return string
157
-     */
158
-    public function getRequestTarget(): string
159
-    {
160
-        if (isset($this->requestTarget)) {
161
-            return $this->requestTarget;
162
-        }
163
-
164
-        $requestTarget = $this->uri->getPath();
165
-
166
-        // https://tools.ietf.org/html/rfc7230#section-5.3.1
167
-        // https://tools.ietf.org/html/rfc7230#section-2.7
168
-        //
169
-        // origin-form = absolute-path [ "?" query ]
170
-        // absolute-path = 1*( "/" segment )
171
-        if (strncmp($requestTarget, '/', 1) !== 0) {
172
-            return '/';
173
-        }
174
-
175
-        $queryString = $this->uri->getQuery();
176
-        if ($queryString !== '') {
177
-            $requestTarget .= '?' . $queryString;
178
-        }
179
-
180
-        return $requestTarget;
181
-    }
182
-
183
-    /**
184
-     * Creates a new instance of the request with the given request target
185
-     *
186
-     * @param mixed $requestTarget
187
-     *
188
-     * @return static
189
-     *
190
-     * @throws InvalidArgumentException
191
-     *         If the request target isn't valid.
192
-     */
193
-    public function withRequestTarget($requestTarget): RequestInterface
194
-    {
195
-        $clone = clone $this;
196
-        $clone->setRequestTarget($requestTarget);
197
-
198
-        return $clone;
199
-    }
200
-
201
-    /**
202
-     * Sets the given method to the request
203
-     *
204
-     * @param string $method
205
-     *
206
-     * @return void
207
-     *
208
-     * @throws InvalidArgumentException
209
-     *         If the method isn't valid.
210
-     */
211
-    final protected function setMethod($method): void
212
-    {
213
-        $this->validateMethod($method);
214
-
215
-        $this->method = $method;
216
-    }
217
-
218
-    /**
219
-     * Sets the given URI to the request
220
-     *
221
-     * @param mixed $uri
222
-     * @param bool $preserveHost
223
-     *
224
-     * @return void
225
-     *
226
-     * @throws InvalidArgumentException
227
-     *         If the URI isn't valid.
228
-     */
229
-    final protected function setUri($uri, $preserveHost = false): void
230
-    {
231
-        $this->uri = Uri::create($uri);
232
-
233
-        if ($preserveHost && $this->hasHeader('Host')) {
234
-            return;
235
-        }
236
-
237
-        $host = $this->uri->getHost();
238
-        if ($host === '') {
239
-            return;
240
-        }
241
-
242
-        $port = $this->uri->getPort();
243
-        if (isset($port)) {
244
-            $host .= ':' . $port;
245
-        }
246
-
247
-        $this->setHeader('Host', $host, true);
248
-    }
249
-
250
-    /**
251
-     * Sets the given request target to the request
252
-     *
253
-     * @param mixed $requestTarget
254
-     *
255
-     * @return void
256
-     *
257
-     * @throws InvalidArgumentException
258
-     *         If the request target isn't valid.
259
-     */
260
-    final protected function setRequestTarget($requestTarget): void
261
-    {
262
-        $this->validateRequestTarget($requestTarget);
263
-
264
-        /** @var string $requestTarget */
265
-
266
-        $this->requestTarget = $requestTarget;
267
-    }
268
-
269
-    /**
270
-     * Validates the given method
271
-     *
272
-     * @link https://tools.ietf.org/html/rfc7230#section-3.1.1
273
-     *
274
-     * @param mixed $method
275
-     *
276
-     * @return void
277
-     *
278
-     * @throws InvalidArgumentException
279
-     *         If the method isn't valid.
280
-     */
281
-    private function validateMethod($method): void
282
-    {
283
-        if ('' === $method) {
284
-            throw new InvalidArgumentException('HTTP method cannot be an empty');
285
-        }
286
-
287
-        if (!is_string($method)) {
288
-            throw new InvalidArgumentException('HTTP method must be a string');
289
-        }
290
-
291
-        if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $method)) {
292
-            throw new InvalidArgumentException('Invalid HTTP method');
293
-        }
294
-    }
295
-
296
-    /**
297
-     * Validates the given request target
298
-     *
299
-     * @param mixed $requestTarget
300
-     *
301
-     * @return void
302
-     *
303
-     * @throws InvalidArgumentException
304
-     *         If the request target isn't valid.
305
-     */
306
-    private function validateRequestTarget($requestTarget): void
307
-    {
308
-        if ('' === $requestTarget) {
309
-            throw new InvalidArgumentException('HTTP request target cannot be an empty');
310
-        }
311
-
312
-        if (!is_string($requestTarget)) {
313
-            throw new InvalidArgumentException('HTTP request target must be a string');
314
-        }
315
-
316
-        if (!preg_match(self::RFC7230_REQUEST_TARGET_REGEX, $requestTarget)) {
317
-            throw new InvalidArgumentException('Invalid HTTP request target');
318
-        }
319
-    }
39
+	/**
40
+	 * Regular Expression used for a request target validation
41
+	 *
42
+	 * @var string
43
+	 */
44
+	public const RFC7230_REQUEST_TARGET_REGEX = '/^[\x21-\x7E\x80-\xFF]+$/';
45
+
46
+	/**
47
+	 * The request method (aka verb)
48
+	 *
49
+	 * @var string
50
+	 */
51
+	private string $method = self::METHOD_GET;
52
+
53
+	/**
54
+	 * The request URI
55
+	 *
56
+	 * @var UriInterface
57
+	 */
58
+	private UriInterface $uri;
59
+
60
+	/**
61
+	 * The request target
62
+	 *
63
+	 * @var string|null
64
+	 */
65
+	private ?string $requestTarget = null;
66
+
67
+	/**
68
+	 * Constructor of the class
69
+	 *
70
+	 * @param string|null $method
71
+	 * @param mixed $uri
72
+	 * @param array<string, string|string[]>|null $headers
73
+	 * @param StreamInterface|null $body
74
+	 *
75
+	 * @throws InvalidArgumentException
76
+	 *         If one of the arguments isn't valid.
77
+	 */
78
+	public function __construct(
79
+		?string $method = null,
80
+		$uri = null,
81
+		?array $headers = null,
82
+		?StreamInterface $body = null
83
+	) {
84
+		if (isset($method)) {
85
+			$this->setMethod($method);
86
+		}
87
+
88
+		$this->setUri($uri ?? '/');
89
+
90
+		if (isset($headers)) {
91
+			$this->setHeaders($headers);
92
+		}
93
+
94
+		if (isset($body)) {
95
+			$this->setBody($body);
96
+		}
97
+	}
98
+
99
+	/**
100
+	 * Gets the request method
101
+	 *
102
+	 * @return string
103
+	 */
104
+	public function getMethod(): string
105
+	{
106
+		return $this->method;
107
+	}
108
+
109
+	/**
110
+	 * Creates a new instance of the request with the given method
111
+	 *
112
+	 * @param string $method
113
+	 *
114
+	 * @return static
115
+	 *
116
+	 * @throws InvalidArgumentException
117
+	 *         If the method isn't valid.
118
+	 */
119
+	public function withMethod($method): RequestInterface
120
+	{
121
+		$clone = clone $this;
122
+		$clone->setMethod($method);
123
+
124
+		return $clone;
125
+	}
126
+
127
+	/**
128
+	 * Gets the request URI
129
+	 *
130
+	 * @return UriInterface
131
+	 */
132
+	public function getUri(): UriInterface
133
+	{
134
+		return $this->uri;
135
+	}
136
+
137
+	/**
138
+	 * Creates a new instance of the request with the given URI
139
+	 *
140
+	 * @param UriInterface $uri
141
+	 * @param bool $preserveHost
142
+	 *
143
+	 * @return static
144
+	 */
145
+	public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
146
+	{
147
+		$clone = clone $this;
148
+		$clone->setUri($uri, $preserveHost);
149
+
150
+		return $clone;
151
+	}
152
+
153
+	/**
154
+	 * Gets the request target
155
+	 *
156
+	 * @return string
157
+	 */
158
+	public function getRequestTarget(): string
159
+	{
160
+		if (isset($this->requestTarget)) {
161
+			return $this->requestTarget;
162
+		}
163
+
164
+		$requestTarget = $this->uri->getPath();
165
+
166
+		// https://tools.ietf.org/html/rfc7230#section-5.3.1
167
+		// https://tools.ietf.org/html/rfc7230#section-2.7
168
+		//
169
+		// origin-form = absolute-path [ "?" query ]
170
+		// absolute-path = 1*( "/" segment )
171
+		if (strncmp($requestTarget, '/', 1) !== 0) {
172
+			return '/';
173
+		}
174
+
175
+		$queryString = $this->uri->getQuery();
176
+		if ($queryString !== '') {
177
+			$requestTarget .= '?' . $queryString;
178
+		}
179
+
180
+		return $requestTarget;
181
+	}
182
+
183
+	/**
184
+	 * Creates a new instance of the request with the given request target
185
+	 *
186
+	 * @param mixed $requestTarget
187
+	 *
188
+	 * @return static
189
+	 *
190
+	 * @throws InvalidArgumentException
191
+	 *         If the request target isn't valid.
192
+	 */
193
+	public function withRequestTarget($requestTarget): RequestInterface
194
+	{
195
+		$clone = clone $this;
196
+		$clone->setRequestTarget($requestTarget);
197
+
198
+		return $clone;
199
+	}
200
+
201
+	/**
202
+	 * Sets the given method to the request
203
+	 *
204
+	 * @param string $method
205
+	 *
206
+	 * @return void
207
+	 *
208
+	 * @throws InvalidArgumentException
209
+	 *         If the method isn't valid.
210
+	 */
211
+	final protected function setMethod($method): void
212
+	{
213
+		$this->validateMethod($method);
214
+
215
+		$this->method = $method;
216
+	}
217
+
218
+	/**
219
+	 * Sets the given URI to the request
220
+	 *
221
+	 * @param mixed $uri
222
+	 * @param bool $preserveHost
223
+	 *
224
+	 * @return void
225
+	 *
226
+	 * @throws InvalidArgumentException
227
+	 *         If the URI isn't valid.
228
+	 */
229
+	final protected function setUri($uri, $preserveHost = false): void
230
+	{
231
+		$this->uri = Uri::create($uri);
232
+
233
+		if ($preserveHost && $this->hasHeader('Host')) {
234
+			return;
235
+		}
236
+
237
+		$host = $this->uri->getHost();
238
+		if ($host === '') {
239
+			return;
240
+		}
241
+
242
+		$port = $this->uri->getPort();
243
+		if (isset($port)) {
244
+			$host .= ':' . $port;
245
+		}
246
+
247
+		$this->setHeader('Host', $host, true);
248
+	}
249
+
250
+	/**
251
+	 * Sets the given request target to the request
252
+	 *
253
+	 * @param mixed $requestTarget
254
+	 *
255
+	 * @return void
256
+	 *
257
+	 * @throws InvalidArgumentException
258
+	 *         If the request target isn't valid.
259
+	 */
260
+	final protected function setRequestTarget($requestTarget): void
261
+	{
262
+		$this->validateRequestTarget($requestTarget);
263
+
264
+		/** @var string $requestTarget */
265
+
266
+		$this->requestTarget = $requestTarget;
267
+	}
268
+
269
+	/**
270
+	 * Validates the given method
271
+	 *
272
+	 * @link https://tools.ietf.org/html/rfc7230#section-3.1.1
273
+	 *
274
+	 * @param mixed $method
275
+	 *
276
+	 * @return void
277
+	 *
278
+	 * @throws InvalidArgumentException
279
+	 *         If the method isn't valid.
280
+	 */
281
+	private function validateMethod($method): void
282
+	{
283
+		if ('' === $method) {
284
+			throw new InvalidArgumentException('HTTP method cannot be an empty');
285
+		}
286
+
287
+		if (!is_string($method)) {
288
+			throw new InvalidArgumentException('HTTP method must be a string');
289
+		}
290
+
291
+		if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $method)) {
292
+			throw new InvalidArgumentException('Invalid HTTP method');
293
+		}
294
+	}
295
+
296
+	/**
297
+	 * Validates the given request target
298
+	 *
299
+	 * @param mixed $requestTarget
300
+	 *
301
+	 * @return void
302
+	 *
303
+	 * @throws InvalidArgumentException
304
+	 *         If the request target isn't valid.
305
+	 */
306
+	private function validateRequestTarget($requestTarget): void
307
+	{
308
+		if ('' === $requestTarget) {
309
+			throw new InvalidArgumentException('HTTP request target cannot be an empty');
310
+		}
311
+
312
+		if (!is_string($requestTarget)) {
313
+			throw new InvalidArgumentException('HTTP request target must be a string');
314
+		}
315
+
316
+		if (!preg_match(self::RFC7230_REQUEST_TARGET_REGEX, $requestTarget)) {
317
+			throw new InvalidArgumentException('Invalid HTTP request target');
318
+		}
319
+	}
320 320
 }
Please login to merge, or discard this patch.
src/Message.php 1 patch
Indentation   +389 added lines, -389 removed lines patch added patch discarded remove patch
@@ -39,393 +39,393 @@
 block discarded – undo
39 39
 abstract class Message implements MessageInterface
40 40
 {
41 41
 
42
-    /**
43
-     * Supported HTTP versions
44
-     *
45
-     * @var list<string>
46
-     */
47
-    public const SUPPORTED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2'];
48
-
49
-    /**
50
-     * Default HTTP version
51
-     *
52
-     * @var string
53
-     */
54
-    public const DEFAULT_HTTP_VERSION = '1.1';
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
-        $key = strtolower($name);
146
-
147
-        if (empty($this->headerNames[$key])) {
148
-            return [];
149
-        }
150
-
151
-        return $this->headers[$this->headerNames[$key]];
152
-    }
153
-
154
-    /**
155
-     * Gets a header value as a string from the message by the given name
156
-     *
157
-     * @param string $name
158
-     *
159
-     * @return string
160
-     */
161
-    public function getHeaderLine($name): string
162
-    {
163
-        $value = $this->getHeader($name);
164
-        if ([] === $value) {
165
-            return '';
166
-        }
167
-
168
-        return implode(',', $value);
169
-    }
170
-
171
-    /**
172
-     * Creates a new instance of the message with the given header overwriting the old header
173
-     *
174
-     * @param string $name
175
-     * @param string|string[] $value
176
-     *
177
-     * @return static
178
-     *
179
-     * @throws InvalidArgumentException
180
-     *         If the header isn't valid.
181
-     */
182
-    public function withHeader($name, $value): MessageInterface
183
-    {
184
-        $clone = clone $this;
185
-        $clone->setHeader($name, $value, true);
186
-
187
-        return $clone;
188
-    }
189
-
190
-    /**
191
-     * Creates a new instance of the message with the given header NOT overwriting the old header
192
-     *
193
-     * @param string $name
194
-     * @param string|string[] $value
195
-     *
196
-     * @return static
197
-     *
198
-     * @throws InvalidArgumentException
199
-     *         If the header isn't valid.
200
-     */
201
-    public function withAddedHeader($name, $value): MessageInterface
202
-    {
203
-        $clone = clone $this;
204
-        $clone->setHeader($name, $value, false);
205
-
206
-        return $clone;
207
-    }
208
-
209
-    /**
210
-     * Creates a new instance of the message without a header by the given name
211
-     *
212
-     * @param string $name
213
-     *
214
-     * @return static
215
-     */
216
-    public function withoutHeader($name): MessageInterface
217
-    {
218
-        $clone = clone $this;
219
-        $clone->deleteHeader($name);
220
-
221
-        return $clone;
222
-    }
223
-
224
-    /**
225
-     * Gets the message body
226
-     *
227
-     * @return StreamInterface
228
-     */
229
-    public function getBody(): StreamInterface
230
-    {
231
-        return $this->body ??= new PhpTempStream();
232
-    }
233
-
234
-    /**
235
-     * Creates a new instance of the message with the given body
236
-     *
237
-     * @param StreamInterface $body
238
-     *
239
-     * @return static
240
-     */
241
-    public function withBody(StreamInterface $body): MessageInterface
242
-    {
243
-        $clone = clone $this;
244
-        $clone->setBody($body);
245
-
246
-        return $clone;
247
-    }
248
-
249
-    /**
250
-     * Sets the given HTTP version to the message
251
-     *
252
-     * @param string $protocolVersion
253
-     *
254
-     * @return void
255
-     *
256
-     * @throws InvalidArgumentException
257
-     *         If the HTTP version isn't valid.
258
-     */
259
-    final protected function setProtocolVersion($protocolVersion): void
260
-    {
261
-        $this->validateProtocolVersion($protocolVersion);
262
-
263
-        $this->protocolVersion = $protocolVersion;
264
-    }
265
-
266
-    /**
267
-     * Sets a new header to the message with the given name and value(s)
268
-     *
269
-     * @param string $name
270
-     * @param string|string[] $value
271
-     * @param bool $replace
272
-     *
273
-     * @return void
274
-     *
275
-     * @throws InvalidArgumentException
276
-     *         If the header isn't valid.
277
-     */
278
-    final protected function setHeader($name, $value, bool $replace = true): void
279
-    {
280
-        if (!is_array($value)) {
281
-            $value = [$value];
282
-        }
283
-
284
-        $this->validateHeaderName($name);
285
-        $this->validateHeaderValue($name, $value);
286
-
287
-        if ($replace) {
288
-            $this->deleteHeader($name);
289
-        }
290
-
291
-        $key = strtolower($name);
292
-
293
-        $this->headerNames[$key] ??= $name;
294
-        $this->headers[$this->headerNames[$key]] ??= [];
295
-
296
-        foreach ($value as $item) {
297
-            $this->headers[$this->headerNames[$key]][] = $item;
298
-        }
299
-    }
300
-
301
-    /**
302
-     * Sets the given headers to the message
303
-     *
304
-     * @param array<string, string|string[]> $headers
305
-     *
306
-     * @return void
307
-     *
308
-     * @throws InvalidArgumentException
309
-     *         If one of the headers isn't valid.
310
-     */
311
-    final protected function setHeaders(array $headers): void
312
-    {
313
-        foreach ($headers as $name => $value) {
314
-            $this->setHeader($name, $value, false);
315
-        }
316
-    }
317
-
318
-    /**
319
-     * Deletes a header from the message by the given name
320
-     *
321
-     * @param string $name
322
-     *
323
-     * @return void
324
-     */
325
-    final protected function deleteHeader($name): void
326
-    {
327
-        $key = strtolower($name);
328
-
329
-        if (isset($this->headerNames[$key])) {
330
-            unset($this->headers[$this->headerNames[$key]]);
331
-            unset($this->headerNames[$key]);
332
-        }
333
-    }
334
-
335
-    /**
336
-     * Sets the given body to the message
337
-     *
338
-     * @param StreamInterface $body
339
-     *
340
-     * @return void
341
-     */
342
-    final protected function setBody(StreamInterface $body): void
343
-    {
344
-        $this->body = $body;
345
-    }
346
-
347
-    /**
348
-     * Validates the given HTTP version
349
-     *
350
-     * @param mixed $protocolVersion
351
-     *
352
-     * @return void
353
-     *
354
-     * @throws InvalidArgumentException
355
-     *         If the HTTP version isn't valid.
356
-     */
357
-    private function validateProtocolVersion($protocolVersion): void
358
-    {
359
-        if (!in_array($protocolVersion, self::SUPPORTED_HTTP_VERSIONS, true)) {
360
-            throw new InvalidArgumentException('Invalid or unsupported HTTP version');
361
-        }
362
-    }
363
-
364
-    /**
365
-     * Validates the given header name
366
-     *
367
-     * @param mixed $name
368
-     *
369
-     * @return void
370
-     *
371
-     * @throws InvalidArgumentException
372
-     *         If the header name isn't valid.
373
-     */
374
-    private function validateHeaderName($name): void
375
-    {
376
-        if ($name === '') {
377
-            throw new InvalidArgumentException('HTTP header name cannot be an empty');
378
-        }
379
-
380
-        if (!is_string($name)) {
381
-            throw new InvalidArgumentException('HTTP header name must be a string');
382
-        }
383
-
384
-        if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $name)) {
385
-            throw new InvalidArgumentException('HTTP header name is invalid');
386
-        }
387
-    }
388
-
389
-    /**
390
-     * Validates the given header value
391
-     *
392
-     * @param string $name
393
-     * @param array $value
394
-     *
395
-     * @return void
396
-     *
397
-     * @throws InvalidArgumentException
398
-     *         If the header value isn't valid.
399
-     */
400
-    private function validateHeaderValue(string $name, array $value): void
401
-    {
402
-        if ([] === $value) {
403
-            throw new InvalidArgumentException(sprintf(
404
-                'The "%s" HTTP header value cannot be an empty array',
405
-                $name,
406
-            ));
407
-        }
408
-
409
-        foreach ($value as $key => $item) {
410
-            if ('' === $item) {
411
-                continue;
412
-            }
413
-
414
-            if (!is_string($item)) {
415
-                throw new InvalidArgumentException(sprintf(
416
-                    'The "%s[%s]" HTTP header value must be a string',
417
-                    $name,
418
-                    $key
419
-                ));
420
-            }
421
-
422
-            if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $item)) {
423
-                throw new InvalidArgumentException(sprintf(
424
-                    'The "%s[%s]" HTTP header value is invalid',
425
-                    $name,
426
-                    $key
427
-                ));
428
-            }
429
-        }
430
-    }
42
+	/**
43
+	 * Supported HTTP versions
44
+	 *
45
+	 * @var list<string>
46
+	 */
47
+	public const SUPPORTED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2'];
48
+
49
+	/**
50
+	 * Default HTTP version
51
+	 *
52
+	 * @var string
53
+	 */
54
+	public const DEFAULT_HTTP_VERSION = '1.1';
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
+		$key = strtolower($name);
146
+
147
+		if (empty($this->headerNames[$key])) {
148
+			return [];
149
+		}
150
+
151
+		return $this->headers[$this->headerNames[$key]];
152
+	}
153
+
154
+	/**
155
+	 * Gets a header value as a string from the message by the given name
156
+	 *
157
+	 * @param string $name
158
+	 *
159
+	 * @return string
160
+	 */
161
+	public function getHeaderLine($name): string
162
+	{
163
+		$value = $this->getHeader($name);
164
+		if ([] === $value) {
165
+			return '';
166
+		}
167
+
168
+		return implode(',', $value);
169
+	}
170
+
171
+	/**
172
+	 * Creates a new instance of the message with the given header overwriting the old header
173
+	 *
174
+	 * @param string $name
175
+	 * @param string|string[] $value
176
+	 *
177
+	 * @return static
178
+	 *
179
+	 * @throws InvalidArgumentException
180
+	 *         If the header isn't valid.
181
+	 */
182
+	public function withHeader($name, $value): MessageInterface
183
+	{
184
+		$clone = clone $this;
185
+		$clone->setHeader($name, $value, true);
186
+
187
+		return $clone;
188
+	}
189
+
190
+	/**
191
+	 * Creates a new instance of the message with the given header NOT overwriting the old header
192
+	 *
193
+	 * @param string $name
194
+	 * @param string|string[] $value
195
+	 *
196
+	 * @return static
197
+	 *
198
+	 * @throws InvalidArgumentException
199
+	 *         If the header isn't valid.
200
+	 */
201
+	public function withAddedHeader($name, $value): MessageInterface
202
+	{
203
+		$clone = clone $this;
204
+		$clone->setHeader($name, $value, false);
205
+
206
+		return $clone;
207
+	}
208
+
209
+	/**
210
+	 * Creates a new instance of the message without a header by the given name
211
+	 *
212
+	 * @param string $name
213
+	 *
214
+	 * @return static
215
+	 */
216
+	public function withoutHeader($name): MessageInterface
217
+	{
218
+		$clone = clone $this;
219
+		$clone->deleteHeader($name);
220
+
221
+		return $clone;
222
+	}
223
+
224
+	/**
225
+	 * Gets the message body
226
+	 *
227
+	 * @return StreamInterface
228
+	 */
229
+	public function getBody(): StreamInterface
230
+	{
231
+		return $this->body ??= new PhpTempStream();
232
+	}
233
+
234
+	/**
235
+	 * Creates a new instance of the message with the given body
236
+	 *
237
+	 * @param StreamInterface $body
238
+	 *
239
+	 * @return static
240
+	 */
241
+	public function withBody(StreamInterface $body): MessageInterface
242
+	{
243
+		$clone = clone $this;
244
+		$clone->setBody($body);
245
+
246
+		return $clone;
247
+	}
248
+
249
+	/**
250
+	 * Sets the given HTTP version to the message
251
+	 *
252
+	 * @param string $protocolVersion
253
+	 *
254
+	 * @return void
255
+	 *
256
+	 * @throws InvalidArgumentException
257
+	 *         If the HTTP version isn't valid.
258
+	 */
259
+	final protected function setProtocolVersion($protocolVersion): void
260
+	{
261
+		$this->validateProtocolVersion($protocolVersion);
262
+
263
+		$this->protocolVersion = $protocolVersion;
264
+	}
265
+
266
+	/**
267
+	 * Sets a new header to the message with the given name and value(s)
268
+	 *
269
+	 * @param string $name
270
+	 * @param string|string[] $value
271
+	 * @param bool $replace
272
+	 *
273
+	 * @return void
274
+	 *
275
+	 * @throws InvalidArgumentException
276
+	 *         If the header isn't valid.
277
+	 */
278
+	final protected function setHeader($name, $value, bool $replace = true): void
279
+	{
280
+		if (!is_array($value)) {
281
+			$value = [$value];
282
+		}
283
+
284
+		$this->validateHeaderName($name);
285
+		$this->validateHeaderValue($name, $value);
286
+
287
+		if ($replace) {
288
+			$this->deleteHeader($name);
289
+		}
290
+
291
+		$key = strtolower($name);
292
+
293
+		$this->headerNames[$key] ??= $name;
294
+		$this->headers[$this->headerNames[$key]] ??= [];
295
+
296
+		foreach ($value as $item) {
297
+			$this->headers[$this->headerNames[$key]][] = $item;
298
+		}
299
+	}
300
+
301
+	/**
302
+	 * Sets the given headers to the message
303
+	 *
304
+	 * @param array<string, string|string[]> $headers
305
+	 *
306
+	 * @return void
307
+	 *
308
+	 * @throws InvalidArgumentException
309
+	 *         If one of the headers isn't valid.
310
+	 */
311
+	final protected function setHeaders(array $headers): void
312
+	{
313
+		foreach ($headers as $name => $value) {
314
+			$this->setHeader($name, $value, false);
315
+		}
316
+	}
317
+
318
+	/**
319
+	 * Deletes a header from the message by the given name
320
+	 *
321
+	 * @param string $name
322
+	 *
323
+	 * @return void
324
+	 */
325
+	final protected function deleteHeader($name): void
326
+	{
327
+		$key = strtolower($name);
328
+
329
+		if (isset($this->headerNames[$key])) {
330
+			unset($this->headers[$this->headerNames[$key]]);
331
+			unset($this->headerNames[$key]);
332
+		}
333
+	}
334
+
335
+	/**
336
+	 * Sets the given body to the message
337
+	 *
338
+	 * @param StreamInterface $body
339
+	 *
340
+	 * @return void
341
+	 */
342
+	final protected function setBody(StreamInterface $body): void
343
+	{
344
+		$this->body = $body;
345
+	}
346
+
347
+	/**
348
+	 * Validates the given HTTP version
349
+	 *
350
+	 * @param mixed $protocolVersion
351
+	 *
352
+	 * @return void
353
+	 *
354
+	 * @throws InvalidArgumentException
355
+	 *         If the HTTP version isn't valid.
356
+	 */
357
+	private function validateProtocolVersion($protocolVersion): void
358
+	{
359
+		if (!in_array($protocolVersion, self::SUPPORTED_HTTP_VERSIONS, true)) {
360
+			throw new InvalidArgumentException('Invalid or unsupported HTTP version');
361
+		}
362
+	}
363
+
364
+	/**
365
+	 * Validates the given header name
366
+	 *
367
+	 * @param mixed $name
368
+	 *
369
+	 * @return void
370
+	 *
371
+	 * @throws InvalidArgumentException
372
+	 *         If the header name isn't valid.
373
+	 */
374
+	private function validateHeaderName($name): void
375
+	{
376
+		if ($name === '') {
377
+			throw new InvalidArgumentException('HTTP header name cannot be an empty');
378
+		}
379
+
380
+		if (!is_string($name)) {
381
+			throw new InvalidArgumentException('HTTP header name must be a string');
382
+		}
383
+
384
+		if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $name)) {
385
+			throw new InvalidArgumentException('HTTP header name is invalid');
386
+		}
387
+	}
388
+
389
+	/**
390
+	 * Validates the given header value
391
+	 *
392
+	 * @param string $name
393
+	 * @param array $value
394
+	 *
395
+	 * @return void
396
+	 *
397
+	 * @throws InvalidArgumentException
398
+	 *         If the header value isn't valid.
399
+	 */
400
+	private function validateHeaderValue(string $name, array $value): void
401
+	{
402
+		if ([] === $value) {
403
+			throw new InvalidArgumentException(sprintf(
404
+				'The "%s" HTTP header value cannot be an empty array',
405
+				$name,
406
+			));
407
+		}
408
+
409
+		foreach ($value as $key => $item) {
410
+			if ('' === $item) {
411
+				continue;
412
+			}
413
+
414
+			if (!is_string($item)) {
415
+				throw new InvalidArgumentException(sprintf(
416
+					'The "%s[%s]" HTTP header value must be a string',
417
+					$name,
418
+					$key
419
+				));
420
+			}
421
+
422
+			if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $item)) {
423
+				throw new InvalidArgumentException(sprintf(
424
+					'The "%s[%s]" HTTP header value is invalid',
425
+					$name,
426
+					$key
427
+				));
428
+			}
429
+		}
430
+	}
431 431
 }
Please login to merge, or discard this patch.
functions/server_request_files.php 2 patches
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -43,56 +43,56 @@
 block discarded – undo
43 43
  */
44 44
 function server_request_files(?array $files = null): array
45 45
 {
46
-    $files ??= $_FILES;
46
+	$files ??= $_FILES;
47 47
 
48
-    $walker = function (
49
-        $path,
50
-        $size,
51
-        $error,
52
-        $name,
53
-        $type
54
-    ) use (&$walker) {
55
-        if (!is_array($path)) {
56
-            // It makes no sense to create a stream if the file has not been successfully uploaded.
57
-            $stream = UPLOAD_ERR_OK <> $error ? null : new FileStream($path, 'rb');
48
+	$walker = function (
49
+		$path,
50
+		$size,
51
+		$error,
52
+		$name,
53
+		$type
54
+	) use (&$walker) {
55
+		if (!is_array($path)) {
56
+			// It makes no sense to create a stream if the file has not been successfully uploaded.
57
+			$stream = UPLOAD_ERR_OK <> $error ? null : new FileStream($path, 'rb');
58 58
 
59
-            return new UploadedFile(
60
-                $stream,
61
-                $size,
62
-                $error,
63
-                $name,
64
-                $type
65
-            );
66
-        }
59
+			return new UploadedFile(
60
+				$stream,
61
+				$size,
62
+				$error,
63
+				$name,
64
+				$type
65
+			);
66
+		}
67 67
 
68
-        $result = [];
69
-        foreach ($path as $key => $_) {
70
-            if (UPLOAD_ERR_NO_FILE <> $error[$key]) {
71
-                $result[$key] = $walker(
72
-                    $path[$key],
73
-                    $size[$key],
74
-                    $error[$key],
75
-                    $name[$key],
76
-                    $type[$key]
77
-                );
78
-            }
79
-        }
68
+		$result = [];
69
+		foreach ($path as $key => $_) {
70
+			if (UPLOAD_ERR_NO_FILE <> $error[$key]) {
71
+				$result[$key] = $walker(
72
+					$path[$key],
73
+					$size[$key],
74
+					$error[$key],
75
+					$name[$key],
76
+					$type[$key]
77
+				);
78
+			}
79
+		}
80 80
 
81
-        return $result;
82
-    };
81
+		return $result;
82
+	};
83 83
 
84
-    $result = [];
85
-    foreach ($files as $key => $file) {
86
-        if (UPLOAD_ERR_NO_FILE <> $file['error']) {
87
-            $result[$key] = $walker(
88
-                $file['tmp_name'],
89
-                $file['size'],
90
-                $file['error'],
91
-                $file['name'],
92
-                $file['type']
93
-            );
94
-        }
95
-    }
84
+	$result = [];
85
+	foreach ($files as $key => $file) {
86
+		if (UPLOAD_ERR_NO_FILE <> $file['error']) {
87
+			$result[$key] = $walker(
88
+				$file['tmp_name'],
89
+				$file['size'],
90
+				$file['error'],
91
+				$file['name'],
92
+				$file['type']
93
+			);
94
+		}
95
+	}
96 96
 
97
-    return $result;
97
+	return $result;
98 98
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -45,7 +45,7 @@
 block discarded – undo
45 45
 {
46 46
     $files ??= $_FILES;
47 47
 
48
-    $walker = function (
48
+    $walker = function(
49 49
         $path,
50 50
         $size,
51 51
         $error,
Please login to merge, or discard this patch.