Completed
Push — master ( 2a8d76...cdb9d8 )
by Alexpts
01:48
created
src/LowercaseTrait.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -7,8 +7,8 @@
 block discarded – undo
7 7
 
8 8
 trait LowercaseTrait
9 9
 {
10
-	protected static function lowercase(string $value): string
11
-	{
12
-		return strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
13
-	}
10
+    protected static function lowercase(string $value): string
11
+    {
12
+        return strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
13
+    }
14 14
 }
15 15
\ No newline at end of file
Please login to merge, or discard this patch.
src/Response.php 1 patch
Indentation   +109 added lines, -109 removed lines patch added patch discarded remove patch
@@ -11,124 +11,124 @@
 block discarded – undo
11 11
 class Response extends Message implements ServerMessageInterface, ResponseInterface
12 12
 {
13 13
 
14
-	use ServerMessage;
14
+    use ServerMessage;
15 15
 
16
-	/** @var array Map of standard HTTP status code/reason phrases */
17
-	protected const PHRASES = [
18
-		100 => 'Continue',
19
-		101 => 'Switching Protocols',
20
-		102 => 'Processing',
21
-		200 => 'OK',
22
-		201 => 'Created',
23
-		202 => 'Accepted',
24
-		203 => 'Non-Authoritative Information',
25
-		204 => 'No Content',
26
-		205 => 'Reset Content',
27
-		206 => 'Partial Content',
28
-		207 => 'Multi-status',
29
-		208 => 'Already Reported',
30
-		300 => 'Multiple Choices',
31
-		301 => 'Moved Permanently',
32
-		302 => 'Found',
33
-		303 => 'See Other',
34
-		304 => 'Not Modified',
35
-		305 => 'Use Proxy',
36
-		306 => 'Switch Proxy',
37
-		307 => 'Temporary Redirect',
38
-		400 => 'Bad Request',
39
-		401 => 'Unauthorized',
40
-		402 => 'Payment Required',
41
-		403 => 'Forbidden',
42
-		404 => 'Not Found',
43
-		405 => 'Method Not Allowed',
44
-		406 => 'Not Acceptable',
45
-		407 => 'Proxy Authentication Required',
46
-		408 => 'Request Time-out',
47
-		409 => 'Conflict',
48
-		410 => 'Gone',
49
-		411 => 'Length Required',
50
-		412 => 'Precondition Failed',
51
-		413 => 'Request Entity Too Large',
52
-		414 => 'Request-URI Too Large',
53
-		415 => 'Unsupported Media Type',
54
-		416 => 'Requested range not satisfiable',
55
-		417 => 'Expectation Failed',
56
-		418 => 'I\'m a teapot',
57
-		422 => 'Unprocessable Entity',
58
-		423 => 'Locked',
59
-		424 => 'Failed Dependency',
60
-		425 => 'Unordered Collection',
61
-		426 => 'Upgrade Required',
62
-		428 => 'Precondition Required',
63
-		429 => 'Too Many Requests',
64
-		431 => 'Request Header Fields Too Large',
65
-		451 => 'Unavailable For Legal Reasons',
66
-		500 => 'Internal Server Error',
67
-		501 => 'Not Implemented',
68
-		502 => 'Bad Gateway',
69
-		503 => 'Service Unavailable',
70
-		504 => 'Gateway Time-out',
71
-		505 => 'HTTP Version not supported',
72
-		506 => 'Variant Also Negotiates',
73
-		507 => 'Insufficient Storage',
74
-		508 => 'Loop Detected',
75
-		511 => 'Network Authentication Required',
76
-	];
16
+    /** @var array Map of standard HTTP status code/reason phrases */
17
+    protected const PHRASES = [
18
+        100 => 'Continue',
19
+        101 => 'Switching Protocols',
20
+        102 => 'Processing',
21
+        200 => 'OK',
22
+        201 => 'Created',
23
+        202 => 'Accepted',
24
+        203 => 'Non-Authoritative Information',
25
+        204 => 'No Content',
26
+        205 => 'Reset Content',
27
+        206 => 'Partial Content',
28
+        207 => 'Multi-status',
29
+        208 => 'Already Reported',
30
+        300 => 'Multiple Choices',
31
+        301 => 'Moved Permanently',
32
+        302 => 'Found',
33
+        303 => 'See Other',
34
+        304 => 'Not Modified',
35
+        305 => 'Use Proxy',
36
+        306 => 'Switch Proxy',
37
+        307 => 'Temporary Redirect',
38
+        400 => 'Bad Request',
39
+        401 => 'Unauthorized',
40
+        402 => 'Payment Required',
41
+        403 => 'Forbidden',
42
+        404 => 'Not Found',
43
+        405 => 'Method Not Allowed',
44
+        406 => 'Not Acceptable',
45
+        407 => 'Proxy Authentication Required',
46
+        408 => 'Request Time-out',
47
+        409 => 'Conflict',
48
+        410 => 'Gone',
49
+        411 => 'Length Required',
50
+        412 => 'Precondition Failed',
51
+        413 => 'Request Entity Too Large',
52
+        414 => 'Request-URI Too Large',
53
+        415 => 'Unsupported Media Type',
54
+        416 => 'Requested range not satisfiable',
55
+        417 => 'Expectation Failed',
56
+        418 => 'I\'m a teapot',
57
+        422 => 'Unprocessable Entity',
58
+        423 => 'Locked',
59
+        424 => 'Failed Dependency',
60
+        425 => 'Unordered Collection',
61
+        426 => 'Upgrade Required',
62
+        428 => 'Precondition Required',
63
+        429 => 'Too Many Requests',
64
+        431 => 'Request Header Fields Too Large',
65
+        451 => 'Unavailable For Legal Reasons',
66
+        500 => 'Internal Server Error',
67
+        501 => 'Not Implemented',
68
+        502 => 'Bad Gateway',
69
+        503 => 'Service Unavailable',
70
+        504 => 'Gateway Time-out',
71
+        505 => 'HTTP Version not supported',
72
+        506 => 'Variant Also Negotiates',
73
+        507 => 'Insufficient Storage',
74
+        508 => 'Loop Detected',
75
+        511 => 'Network Authentication Required',
76
+    ];
77 77
 
78
-	protected string $reasonPhrase = '';
79
-	protected int $statusCode = 200;
78
+    protected string $reasonPhrase = '';
79
+    protected int $statusCode = 200;
80 80
 
81
-	/**
82
-	 * @param int $status Status code
83
-	 * @param array $headers Response headers
84
-	 * @param string|resource|StreamInterface|null $body Response body
85
-	 * @param string $version Protocol version
86
-	 * @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
87
-	 */
88
-	public function __construct(
89
-		int $status = 200,
90
-		array $headers = [],
91
-		$body = null,
92
-		string $version = '1.1',
93
-		string $reason = null
94
-	) {
95
-		// If we got no body, defer initialization of the stream until Response::getBody()
96
-		if ('' !== $body && null !== $body) {
97
-			$this->stream = Stream::create($body);
98
-		}
81
+    /**
82
+     * @param int $status Status code
83
+     * @param array $headers Response headers
84
+     * @param string|resource|StreamInterface|null $body Response body
85
+     * @param string $version Protocol version
86
+     * @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
87
+     */
88
+    public function __construct(
89
+        int $status = 200,
90
+        array $headers = [],
91
+        $body = null,
92
+        string $version = '1.1',
93
+        string $reason = null
94
+    ) {
95
+        // If we got no body, defer initialization of the stream until Response::getBody()
96
+        if ('' !== $body && null !== $body) {
97
+            $this->stream = Stream::create($body);
98
+        }
99 99
 
100
-		$this->statusCode = $status;
101
-		$this->setHeaders($headers);
100
+        $this->statusCode = $status;
101
+        $this->setHeaders($headers);
102 102
 
103
-		$this->reasonPhrase = $reason === null && isset(self::PHRASES[$this->statusCode])
104
-			? self::PHRASES[$status]
105
-			: $reason ?? '';
103
+        $this->reasonPhrase = $reason === null && isset(self::PHRASES[$this->statusCode])
104
+            ? self::PHRASES[$status]
105
+            : $reason ?? '';
106 106
 
107
-		$this->protocol = $version;
108
-	}
107
+        $this->protocol = $version;
108
+    }
109 109
 
110
-	public function getStatusCode(): int
111
-	{
112
-		return $this->statusCode;
113
-	}
110
+    public function getStatusCode(): int
111
+    {
112
+        return $this->statusCode;
113
+    }
114 114
 
115
-	public function getReasonPhrase(): string
116
-	{
117
-		return $this->reasonPhrase;
118
-	}
115
+    public function getReasonPhrase(): string
116
+    {
117
+        return $this->reasonPhrase;
118
+    }
119 119
 
120
-	public function withStatus($code, $reasonPhrase = ''): self
121
-	{
122
-		if ($code < 100 || $code > 599) {
123
-			throw new InvalidArgumentException('Status code has to be an integer between 100 and 599');
124
-		}
120
+    public function withStatus($code, $reasonPhrase = ''): self
121
+    {
122
+        if ($code < 100 || $code > 599) {
123
+            throw new InvalidArgumentException('Status code has to be an integer between 100 and 599');
124
+        }
125 125
 
126
-		$this->statusCode = $code;
127
-		if ('' === $reasonPhrase && isset(self::PHRASES[$this->statusCode])) {
128
-			$reasonPhrase = self::PHRASES[$this->statusCode];
129
-		}
126
+        $this->statusCode = $code;
127
+        if ('' === $reasonPhrase && isset(self::PHRASES[$this->statusCode])) {
128
+            $reasonPhrase = self::PHRASES[$this->statusCode];
129
+        }
130 130
 
131
-		$this->reasonPhrase = $reasonPhrase;
132
-		return $this;
133
-	}
131
+        $this->reasonPhrase = $reasonPhrase;
132
+        return $this;
133
+    }
134 134
 }
135 135
\ No newline at end of file
Please login to merge, or discard this patch.
src/Request.php 1 patch
Indentation   +103 added lines, -103 removed lines patch added patch discarded remove patch
@@ -11,107 +11,107 @@
 block discarded – undo
11 11
 
12 12
 class Request extends Message implements RequestInterface
13 13
 {
14
-	protected string $method;
15
-
16
-	protected ?string $requestTarget = null;
17
-	protected ?UriInterface $uri;
18
-
19
-	/**
20
-	 * @param string $method HTTP method
21
-	 * @param UriInterface $uri URI
22
-	 * @param array $headers Request headers
23
-	 * @param string|resource|StreamInterface|null $body Request body
24
-	 * @param string $version Protocol version
25
-	 */
26
-	public function __construct(
27
-		string $method,
28
-		UriInterface $uri,
29
-		array $headers = [],
30
-		$body = null,
31
-		string $version = '1.1'
32
-	) {
33
-		$this->method = $method;
34
-		$this->uri = $uri;
35
-		$this->setHeaders($headers);
36
-		$this->protocol = $version;
37
-
38
-		if (!$this->hasHeader('host')) {
39
-			$this->updateHostFromUri();
40
-		}
41
-
42
-		if ('' !== $body && null !== $body) {
43
-			$this->stream = Stream::create($body);
44
-		}
45
-	}
46
-
47
-	public function getRequestTarget(): string
48
-	{
49
-		if (null !== $this->requestTarget) {
50
-			return $this->requestTarget;
51
-		}
52
-
53
-		$target = $this->uri->getPath();
54
-		$target = $target ?: '/';
55
-
56
-		if ('' !== $this->uri->getQuery()) {
57
-			$target .= '?' . $this->uri->getQuery();
58
-		}
59
-
60
-		$this->requestTarget = $target;
61
-		return $target;
62
-	}
63
-
64
-	public function withRequestTarget($requestTarget): self
65
-	{
66
-		if (preg_match('#\s#', $requestTarget)) {
67
-			throw new InvalidArgumentException('Invalid request target provided; cannot contain whitespace');
68
-		}
69
-
70
-		$this->requestTarget = $requestTarget;
71
-		return $this;
72
-	}
73
-
74
-	public function getMethod(): string
75
-	{
76
-		return $this->method;
77
-	}
78
-
79
-	public function withMethod($method): self
80
-	{
81
-		$this->method = $method;
82
-		return $this;
83
-	}
84
-
85
-	public function getUri(): UriInterface
86
-	{
87
-		return $this->uri;
88
-	}
89
-
90
-	public function withUri(UriInterface $uri, $preserveHost = false): self
91
-	{
92
-		if ($uri === $this->uri) {
93
-			return $this;
94
-		}
95
-
96
-		$this->uri = $uri;
97
-
98
-		if (!$preserveHost || !$this->hasHeader('Host')) {
99
-			$this->updateHostFromUri();
100
-		}
101
-
102
-		return $this;
103
-	}
104
-
105
-	protected function updateHostFromUri(): void
106
-	{
107
-		if ('' === $host = $this->uri->getHost()) {
108
-			return;
109
-		}
110
-
111
-		if (null !== ($port = $this->uri->getPort())) {
112
-			$host .= ':' . $port;
113
-		}
114
-
115
-		$this->headers['host'] = [$host];
116
-	}
14
+    protected string $method;
15
+
16
+    protected ?string $requestTarget = null;
17
+    protected ?UriInterface $uri;
18
+
19
+    /**
20
+     * @param string $method HTTP method
21
+     * @param UriInterface $uri URI
22
+     * @param array $headers Request headers
23
+     * @param string|resource|StreamInterface|null $body Request body
24
+     * @param string $version Protocol version
25
+     */
26
+    public function __construct(
27
+        string $method,
28
+        UriInterface $uri,
29
+        array $headers = [],
30
+        $body = null,
31
+        string $version = '1.1'
32
+    ) {
33
+        $this->method = $method;
34
+        $this->uri = $uri;
35
+        $this->setHeaders($headers);
36
+        $this->protocol = $version;
37
+
38
+        if (!$this->hasHeader('host')) {
39
+            $this->updateHostFromUri();
40
+        }
41
+
42
+        if ('' !== $body && null !== $body) {
43
+            $this->stream = Stream::create($body);
44
+        }
45
+    }
46
+
47
+    public function getRequestTarget(): string
48
+    {
49
+        if (null !== $this->requestTarget) {
50
+            return $this->requestTarget;
51
+        }
52
+
53
+        $target = $this->uri->getPath();
54
+        $target = $target ?: '/';
55
+
56
+        if ('' !== $this->uri->getQuery()) {
57
+            $target .= '?' . $this->uri->getQuery();
58
+        }
59
+
60
+        $this->requestTarget = $target;
61
+        return $target;
62
+    }
63
+
64
+    public function withRequestTarget($requestTarget): self
65
+    {
66
+        if (preg_match('#\s#', $requestTarget)) {
67
+            throw new InvalidArgumentException('Invalid request target provided; cannot contain whitespace');
68
+        }
69
+
70
+        $this->requestTarget = $requestTarget;
71
+        return $this;
72
+    }
73
+
74
+    public function getMethod(): string
75
+    {
76
+        return $this->method;
77
+    }
78
+
79
+    public function withMethod($method): self
80
+    {
81
+        $this->method = $method;
82
+        return $this;
83
+    }
84
+
85
+    public function getUri(): UriInterface
86
+    {
87
+        return $this->uri;
88
+    }
89
+
90
+    public function withUri(UriInterface $uri, $preserveHost = false): self
91
+    {
92
+        if ($uri === $this->uri) {
93
+            return $this;
94
+        }
95
+
96
+        $this->uri = $uri;
97
+
98
+        if (!$preserveHost || !$this->hasHeader('Host')) {
99
+            $this->updateHostFromUri();
100
+        }
101
+
102
+        return $this;
103
+    }
104
+
105
+    protected function updateHostFromUri(): void
106
+    {
107
+        if ('' === $host = $this->uri->getHost()) {
108
+            return;
109
+        }
110
+
111
+        if (null !== ($port = $this->uri->getPort())) {
112
+            $host .= ':' . $port;
113
+        }
114
+
115
+        $this->headers['host'] = [$host];
116
+    }
117 117
 }
Please login to merge, or discard this patch.
src/Response/JsonResponseInterface.php 1 patch
Indentation   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -5,8 +5,8 @@
 block discarded – undo
5 5
 
6 6
 interface JsonResponseInterface extends ServerMessageInterface
7 7
 {
8
-	public function getData(): array;
9
-	public function setData(array $data): self;
10
-	public function reset(): self;
8
+    public function getData(): array;
9
+    public function setData(array $data): self;
10
+    public function reset(): self;
11 11
 
12 12
 }
13 13
\ No newline at end of file
Please login to merge, or discard this patch.
src/Response/ServerMessageInterface.php 1 patch
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -5,31 +5,31 @@
 block discarded – undo
5 5
 
6 6
 interface ServerMessageInterface
7 7
 {
8
-	/**
9
-	 * @return array
10
-	 */
11
-	public function getAttributes(): array;
8
+    /**
9
+     * @return array
10
+     */
11
+    public function getAttributes(): array;
12 12
 
13
-	/**
14
-	 * @param string $attribute
15
-	 * @param mixed $default
16
-	 *
17
-	 * @return mixed
18
-	 */
19
-	public function getAttribute($attribute, $default = null);
13
+    /**
14
+     * @param string $attribute
15
+     * @param mixed $default
16
+     *
17
+     * @return mixed
18
+     */
19
+    public function getAttribute($attribute, $default = null);
20 20
 
21
-	/**
22
-	 * @param string $attribute
23
-	 * @param mixed $value
24
-	 *
25
-	 * @return $this
26
-	 */
27
-	public function withAttribute($attribute, $value);
21
+    /**
22
+     * @param string $attribute
23
+     * @param mixed $value
24
+     *
25
+     * @return $this
26
+     */
27
+    public function withAttribute($attribute, $value);
28 28
 
29
-	/**
30
-	 * @param string $attribute
31
-	 *
32
-	 * @return $this
33
-	 */
34
-	public function withoutAttribute($attribute);
29
+    /**
30
+     * @param string $attribute
31
+     *
32
+     * @return $this
33
+     */
34
+    public function withoutAttribute($attribute);
35 35
 }
36 36
\ No newline at end of file
Please login to merge, or discard this patch.
src/UploadedFile.php 1 patch
Indentation   +150 added lines, -150 removed lines patch added patch discarded remove patch
@@ -17,154 +17,154 @@
 block discarded – undo
17 17
 
18 18
 final class UploadedFile implements UploadedFileInterface
19 19
 {
20
-	/** @var array */
21
-	private const ERRORS = [
22
-		\UPLOAD_ERR_OK => 1,
23
-		\UPLOAD_ERR_INI_SIZE => 1,
24
-		\UPLOAD_ERR_FORM_SIZE => 1,
25
-		\UPLOAD_ERR_PARTIAL => 1,
26
-		\UPLOAD_ERR_NO_FILE => 1,
27
-		\UPLOAD_ERR_NO_TMP_DIR => 1,
28
-		\UPLOAD_ERR_CANT_WRITE => 1,
29
-		\UPLOAD_ERR_EXTENSION => 1,
30
-	];
31
-
32
-	protected ?string $clientFilename = null;
33
-	protected ?string $clientMediaType = null;
34
-	protected int $error;
35
-	private ?string $file = null;
36
-	private bool $moved = false;
37
-	private int $size;
38
-	private ?StreamInterface $stream = null;
39
-
40
-	/**
41
-	 * @param StreamInterface|string|resource $streamOrFile
42
-	 * @param int $size
43
-	 * @param int $errorStatus
44
-	 * @param string|null $clientFilename
45
-	 * @param string|null $clientMediaType
46
-	 */
47
-	public function __construct($streamOrFile, $size, $errorStatus, $clientFilename = null, $clientMediaType = null)
48
-	{
49
-		if (false === is_int($errorStatus) || !isset(self::ERRORS[$errorStatus])) {
50
-			throw new InvalidArgumentException('Upload file error status must be an integer value and one of the "UPLOAD_ERR_*" constants.');
51
-		}
52
-
53
-		if (false === is_int($size)) {
54
-			throw new InvalidArgumentException('Upload file size must be an integer');
55
-		}
56
-
57
-		if (null !== $clientFilename && !is_string($clientFilename)) {
58
-			throw new InvalidArgumentException('Upload file client filename must be a string or null');
59
-		}
60
-
61
-		if (null !== $clientMediaType && !is_string($clientMediaType)) {
62
-			throw new InvalidArgumentException('Upload file client media type must be a string or null');
63
-		}
64
-
65
-		$this->error = $errorStatus;
66
-		$this->size = $size;
67
-		$this->clientFilename = $clientFilename;
68
-		$this->clientMediaType = $clientMediaType;
69
-
70
-		if (\UPLOAD_ERR_OK === $this->error) {
71
-			// Depending on the value set file or stream variable.
72
-			if (is_string($streamOrFile)) {
73
-				$this->file = $streamOrFile;
74
-			} elseif (\is_resource($streamOrFile)) {
75
-				$this->stream = Stream::create($streamOrFile);
76
-			} elseif ($streamOrFile instanceof StreamInterface) {
77
-				$this->stream = $streamOrFile;
78
-			} else {
79
-				throw new InvalidArgumentException('Invalid stream or file provided for UploadedFile');
80
-			}
81
-		}
82
-	}
83
-
84
-	/**
85
-	 * @throws RuntimeException if is moved or not ok
86
-	 */
87
-	private function validateActive(): void
88
-	{
89
-		if (\UPLOAD_ERR_OK !== $this->error) {
90
-			throw new RuntimeException('Cannot retrieve stream due to upload error');
91
-		}
92
-
93
-		if ($this->moved) {
94
-			throw new RuntimeException('Cannot retrieve stream after it has already been moved');
95
-		}
96
-	}
97
-
98
-	public function getStream(): StreamInterface
99
-	{
100
-		$this->validateActive();
101
-
102
-		if ($this->stream instanceof StreamInterface) {
103
-			return $this->stream;
104
-		}
105
-
106
-		$resource = fopen($this->file, 'r');
107
-
108
-		return Stream::create($resource);
109
-	}
110
-
111
-	public function moveTo($targetPath): void
112
-	{
113
-		$this->validateActive();
114
-
115
-		if (!is_string($targetPath) || '' === $targetPath) {
116
-			throw new InvalidArgumentException('Invalid path provided for move operation; must be a non-empty string');
117
-		}
118
-
119
-		if (null !== $this->file) {
120
-			$this->moved = 'cli' === \PHP_SAPI
121
-				? rename($this->file, $targetPath)
122
-				: move_uploaded_file($this->file, $targetPath);
123
-
124
-			if (false === $this->moved) {
125
-				throw new RuntimeException(sprintf('Uploaded file could not be moved to %s', $targetPath));
126
-			}
127
-
128
-			return;
129
-		}
130
-
131
-		$stream = $this->getStream();
132
-		if ($stream->isSeekable()) {
133
-			$stream->rewind();
134
-		}
135
-
136
-		// Copy the contents of a stream into another stream until end-of-file.
137
-		try {
138
-			$dest = Stream::create(fopen($targetPath, 'w'));
139
-			while (!$stream->eof()) {
140
-				if (!$dest->write($stream->read(1048576))) {
141
-					break;
142
-				}
143
-			}
144
-
145
-			$this->moved = true;
146
-		} catch (Throwable $throwable) {
147
-			throw new RuntimeException(sprintf('Uploaded file could not be moved to %s', $targetPath), 0, $throwable);
148
-		}
149
-	}
150
-
151
-	public function getSize(): int
152
-	{
153
-		return $this->size;
154
-	}
155
-
156
-	public function getError(): int
157
-	{
158
-		return $this->error;
159
-	}
160
-
161
-	public function getClientFilename(): ?string
162
-	{
163
-		return $this->clientFilename;
164
-	}
165
-
166
-	public function getClientMediaType(): ?string
167
-	{
168
-		return $this->clientMediaType;
169
-	}
20
+    /** @var array */
21
+    private const ERRORS = [
22
+        \UPLOAD_ERR_OK => 1,
23
+        \UPLOAD_ERR_INI_SIZE => 1,
24
+        \UPLOAD_ERR_FORM_SIZE => 1,
25
+        \UPLOAD_ERR_PARTIAL => 1,
26
+        \UPLOAD_ERR_NO_FILE => 1,
27
+        \UPLOAD_ERR_NO_TMP_DIR => 1,
28
+        \UPLOAD_ERR_CANT_WRITE => 1,
29
+        \UPLOAD_ERR_EXTENSION => 1,
30
+    ];
31
+
32
+    protected ?string $clientFilename = null;
33
+    protected ?string $clientMediaType = null;
34
+    protected int $error;
35
+    private ?string $file = null;
36
+    private bool $moved = false;
37
+    private int $size;
38
+    private ?StreamInterface $stream = null;
39
+
40
+    /**
41
+     * @param StreamInterface|string|resource $streamOrFile
42
+     * @param int $size
43
+     * @param int $errorStatus
44
+     * @param string|null $clientFilename
45
+     * @param string|null $clientMediaType
46
+     */
47
+    public function __construct($streamOrFile, $size, $errorStatus, $clientFilename = null, $clientMediaType = null)
48
+    {
49
+        if (false === is_int($errorStatus) || !isset(self::ERRORS[$errorStatus])) {
50
+            throw new InvalidArgumentException('Upload file error status must be an integer value and one of the "UPLOAD_ERR_*" constants.');
51
+        }
52
+
53
+        if (false === is_int($size)) {
54
+            throw new InvalidArgumentException('Upload file size must be an integer');
55
+        }
56
+
57
+        if (null !== $clientFilename && !is_string($clientFilename)) {
58
+            throw new InvalidArgumentException('Upload file client filename must be a string or null');
59
+        }
60
+
61
+        if (null !== $clientMediaType && !is_string($clientMediaType)) {
62
+            throw new InvalidArgumentException('Upload file client media type must be a string or null');
63
+        }
64
+
65
+        $this->error = $errorStatus;
66
+        $this->size = $size;
67
+        $this->clientFilename = $clientFilename;
68
+        $this->clientMediaType = $clientMediaType;
69
+
70
+        if (\UPLOAD_ERR_OK === $this->error) {
71
+            // Depending on the value set file or stream variable.
72
+            if (is_string($streamOrFile)) {
73
+                $this->file = $streamOrFile;
74
+            } elseif (\is_resource($streamOrFile)) {
75
+                $this->stream = Stream::create($streamOrFile);
76
+            } elseif ($streamOrFile instanceof StreamInterface) {
77
+                $this->stream = $streamOrFile;
78
+            } else {
79
+                throw new InvalidArgumentException('Invalid stream or file provided for UploadedFile');
80
+            }
81
+        }
82
+    }
83
+
84
+    /**
85
+     * @throws RuntimeException if is moved or not ok
86
+     */
87
+    private function validateActive(): void
88
+    {
89
+        if (\UPLOAD_ERR_OK !== $this->error) {
90
+            throw new RuntimeException('Cannot retrieve stream due to upload error');
91
+        }
92
+
93
+        if ($this->moved) {
94
+            throw new RuntimeException('Cannot retrieve stream after it has already been moved');
95
+        }
96
+    }
97
+
98
+    public function getStream(): StreamInterface
99
+    {
100
+        $this->validateActive();
101
+
102
+        if ($this->stream instanceof StreamInterface) {
103
+            return $this->stream;
104
+        }
105
+
106
+        $resource = fopen($this->file, 'r');
107
+
108
+        return Stream::create($resource);
109
+    }
110
+
111
+    public function moveTo($targetPath): void
112
+    {
113
+        $this->validateActive();
114
+
115
+        if (!is_string($targetPath) || '' === $targetPath) {
116
+            throw new InvalidArgumentException('Invalid path provided for move operation; must be a non-empty string');
117
+        }
118
+
119
+        if (null !== $this->file) {
120
+            $this->moved = 'cli' === \PHP_SAPI
121
+                ? rename($this->file, $targetPath)
122
+                : move_uploaded_file($this->file, $targetPath);
123
+
124
+            if (false === $this->moved) {
125
+                throw new RuntimeException(sprintf('Uploaded file could not be moved to %s', $targetPath));
126
+            }
127
+
128
+            return;
129
+        }
130
+
131
+        $stream = $this->getStream();
132
+        if ($stream->isSeekable()) {
133
+            $stream->rewind();
134
+        }
135
+
136
+        // Copy the contents of a stream into another stream until end-of-file.
137
+        try {
138
+            $dest = Stream::create(fopen($targetPath, 'w'));
139
+            while (!$stream->eof()) {
140
+                if (!$dest->write($stream->read(1048576))) {
141
+                    break;
142
+                }
143
+            }
144
+
145
+            $this->moved = true;
146
+        } catch (Throwable $throwable) {
147
+            throw new RuntimeException(sprintf('Uploaded file could not be moved to %s', $targetPath), 0, $throwable);
148
+        }
149
+    }
150
+
151
+    public function getSize(): int
152
+    {
153
+        return $this->size;
154
+    }
155
+
156
+    public function getError(): int
157
+    {
158
+        return $this->error;
159
+    }
160
+
161
+    public function getClientFilename(): ?string
162
+    {
163
+        return $this->clientFilename;
164
+    }
165
+
166
+    public function getClientMediaType(): ?string
167
+    {
168
+        return $this->clientMediaType;
169
+    }
170 170
 }
171 171
\ No newline at end of file
Please login to merge, or discard this patch.
src/Factory/Psr17Factory.php 1 patch
Indentation   +64 added lines, -64 removed lines patch added patch discarded remove patch
@@ -29,68 +29,68 @@
 block discarded – undo
29 29
 
30 30
 class Psr17Factory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
31 31
 {
32
-	public function createRequest(string $method, $uri): RequestInterface
33
-	{
34
-		if (is_string($uri)) {
35
-			$uri = $this->createUri($uri);
36
-		}
37
-
38
-		return new Request($method, $uri);
39
-	}
40
-
41
-	public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
42
-	{
43
-		if (2 > func_num_args()) {
44
-			$reasonPhrase = null;
45
-		}
46
-
47
-		return new Response($code, [], null, '1.1', $reasonPhrase);
48
-	}
49
-
50
-	public function createStream(string $content = ''): StreamInterface
51
-	{
52
-		return Stream::create($content);
53
-	}
54
-
55
-	public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface
56
-	{
57
-		$resource = @fopen($filename, $mode);
58
-		if (false === $resource) {
59
-			if ('' === $mode || false === in_array($mode[0], ['r', 'w', 'a', 'x', 'c'])) {
60
-				throw new InvalidArgumentException('The mode ' . $mode . ' is invalid.');
61
-			}
62
-
63
-			throw new RuntimeException('The file ' . $filename . ' cannot be opened.');
64
-		}
65
-
66
-		return Stream::create($resource);
67
-	}
68
-
69
-	public function createStreamFromResource($resource): StreamInterface
70
-	{
71
-		return Stream::create($resource);
72
-	}
73
-
74
-	public function createUploadedFile(StreamInterface $stream, int $size = null, int $error = \UPLOAD_ERR_OK, string $clientFilename = null, string $clientMediaType = null): UploadedFileInterface
75
-	{
76
-		if (null === $size) {
77
-			$size = $stream->getSize();
78
-		}
79
-
80
-		return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
81
-	}
82
-
83
-	public function createUri(string $uri = ''): UriInterface
84
-	{
85
-		return new Uri($uri);
86
-	}
87
-
88
-	public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
89
-	{
90
-		if (is_string($uri)) {
91
-			$uri = $this->createUri($uri);
92
-		}
93
-
94
-		return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
95
-	}
32
+    public function createRequest(string $method, $uri): RequestInterface
33
+    {
34
+        if (is_string($uri)) {
35
+            $uri = $this->createUri($uri);
36
+        }
37
+
38
+        return new Request($method, $uri);
39
+    }
40
+
41
+    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
42
+    {
43
+        if (2 > func_num_args()) {
44
+            $reasonPhrase = null;
45
+        }
46
+
47
+        return new Response($code, [], null, '1.1', $reasonPhrase);
48
+    }
49
+
50
+    public function createStream(string $content = ''): StreamInterface
51
+    {
52
+        return Stream::create($content);
53
+    }
54
+
55
+    public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface
56
+    {
57
+        $resource = @fopen($filename, $mode);
58
+        if (false === $resource) {
59
+            if ('' === $mode || false === in_array($mode[0], ['r', 'w', 'a', 'x', 'c'])) {
60
+                throw new InvalidArgumentException('The mode ' . $mode . ' is invalid.');
61
+            }
62
+
63
+            throw new RuntimeException('The file ' . $filename . ' cannot be opened.');
64
+        }
65
+
66
+        return Stream::create($resource);
67
+    }
68
+
69
+    public function createStreamFromResource($resource): StreamInterface
70
+    {
71
+        return Stream::create($resource);
72
+    }
73
+
74
+    public function createUploadedFile(StreamInterface $stream, int $size = null, int $error = \UPLOAD_ERR_OK, string $clientFilename = null, string $clientMediaType = null): UploadedFileInterface
75
+    {
76
+        if (null === $size) {
77
+            $size = $stream->getSize();
78
+        }
79
+
80
+        return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
81
+    }
82
+
83
+    public function createUri(string $uri = ''): UriInterface
84
+    {
85
+        return new Uri($uri);
86
+    }
87
+
88
+    public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
89
+    {
90
+        if (is_string($uri)) {
91
+            $uri = $this->createUri($uri);
92
+        }
93
+
94
+        return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
95
+    }
96 96
 }
97 97
\ No newline at end of file
Please login to merge, or discard this patch.
src/Factory/HttplugFactory.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -14,28 +14,28 @@
 block discarded – undo
14 14
 
15 15
 class HttplugFactory implements MessageFactory, StreamFactory, UriFactory
16 16
 {
17
-	public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1')
18
-	{
19
-		$uri = $this->createUri($uri);
20
-		return new Request($method, $uri, $headers, $body, $protocolVersion);
21
-	}
17
+    public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1')
18
+    {
19
+        $uri = $this->createUri($uri);
20
+        return new Request($method, $uri, $headers, $body, $protocolVersion);
21
+    }
22 22
 
23
-	public function createResponse($statusCode = 200, $reasonPhrase = null, array $headers = [], $body = null, $version = '1.1')
24
-	{
25
-		return new Response((int)$statusCode, $headers, $body, $version, $reasonPhrase);
26
-	}
23
+    public function createResponse($statusCode = 200, $reasonPhrase = null, array $headers = [], $body = null, $version = '1.1')
24
+    {
25
+        return new Response((int)$statusCode, $headers, $body, $version, $reasonPhrase);
26
+    }
27 27
 
28
-	public function createStream($body = null)
29
-	{
30
-		return Stream::create($body ?? '');
31
-	}
28
+    public function createStream($body = null)
29
+    {
30
+        return Stream::create($body ?? '');
31
+    }
32 32
 
33
-	public function createUri($uri = ''): UriInterface
34
-	{
35
-		if ($uri instanceof UriInterface) {
36
-			return $uri;
37
-		}
33
+    public function createUri($uri = ''): UriInterface
34
+    {
35
+        if ($uri instanceof UriInterface) {
36
+            return $uri;
37
+        }
38 38
 
39
-		return new Uri($uri);
40
-	}
39
+        return new Uri($uri);
40
+    }
41 41
 }
42 42
\ No newline at end of file
Please login to merge, or discard this patch.
src/Uri.php 1 patch
Indentation   +231 added lines, -231 removed lines patch added patch discarded remove patch
@@ -12,235 +12,235 @@
 block discarded – undo
12 12
 
13 13
 class Uri implements UriInterface
14 14
 {
15
-	use LowercaseTrait;
16
-
17
-	protected const SCHEMES = ['http' => 80, 'https' => 443];
18
-	protected const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
19
-	protected const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
20
-
21
-	protected string $scheme = '';
22
-	protected string $userInfo = '';
23
-	protected string $host = '';
24
-
25
-	protected ?int $port = null;
26
-	protected string $path = '';
27
-	protected string $query = '';
28
-	protected string $fragment = '';
29
-
30
-	public function __construct(string $uri = '')
31
-	{
32
-		if ('' !== $uri) {
33
-			$parts = parse_url($uri);
34
-			if ($parts === false) {
35
-				throw new InvalidArgumentException("Unable to parse URI: $uri");
36
-			}
37
-
38
-			$this->withScheme($parts['scheme'] ?? '');
39
-			$this->userInfo = $parts['user'] ?? '';
40
-			$this->withHost($parts['host'] ?? '');
41
-			$this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null;
42
-			$this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
43
-			$this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : '';
44
-			$this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : '';
45
-			if (isset($parts['pass'])) {
46
-				$this->userInfo .= ':' . $parts['pass'];
47
-			}
48
-		}
49
-	}
50
-
51
-	public function __toString(): string
52
-	{
53
-		return self::createUriString($this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment);
54
-	}
55
-
56
-	public function getScheme(): string
57
-	{
58
-		return $this->scheme;
59
-	}
60
-
61
-	public function getAuthority(): string
62
-	{
63
-		if ('' === $this->host) {
64
-			return '';
65
-		}
66
-
67
-		$authority = $this->host;
68
-		if ('' !== $this->userInfo) {
69
-			$authority = $this->userInfo . '@' . $authority;
70
-		}
71
-
72
-		if (null !== $this->port) {
73
-			$authority .= ':' . $this->port;
74
-		}
75
-
76
-		return $authority;
77
-	}
78
-
79
-	public function getUserInfo(): string
80
-	{
81
-		return $this->userInfo;
82
-	}
83
-
84
-	public function getHost(): string
85
-	{
86
-		return $this->host;
87
-	}
88
-
89
-	public function getPort(): ?int
90
-	{
91
-		return $this->port;
92
-	}
93
-
94
-	public function getPath(): string
95
-	{
96
-		return $this->path;
97
-	}
98
-
99
-	public function getQuery(): string
100
-	{
101
-		return $this->query;
102
-	}
103
-
104
-	public function getFragment(): string
105
-	{
106
-		return $this->fragment;
107
-	}
108
-
109
-	public function withScheme($scheme): self
110
-	{
111
-		$this->scheme = self::lowercase($scheme);
112
-		$this->port = $this->port ? $this->filterPort($this->port) : null;
113
-
114
-		return $this;
115
-	}
116
-
117
-	public function withUserInfo($user, $password = null): self
118
-	{
119
-		$info = $user;
120
-		if (null !== $password && '' !== $password) {
121
-			$info .= ':' . $password;
122
-		}
123
-
124
-		$this->userInfo = $info;
125
-
126
-		return $this;
127
-	}
128
-
129
-	public function withHost($host): self
130
-	{
131
-		$this->host = self::lowercase($host);
132
-		return $this;
133
-	}
134
-
135
-	public function withPort($port): self
136
-	{
137
-		$this->port = $port === null ? null : $this->filterPort($port);
138
-		return $this;
139
-	}
140
-
141
-	public function withPath($path): self
142
-	{
143
-		$path = $this->filterPath($path);
144
-		if ($this->path !== $path) {
145
-			$this->path = $path;
146
-		}
147
-
148
-		return $this;
149
-	}
150
-
151
-	public function withQuery($query): self
152
-	{
153
-		$query = $this->filterQueryAndFragment($query);
154
-		if ($this->query !== $query) {
155
-			$this->query = $query;
156
-		}
157
-
158
-		return $this;
159
-	}
160
-
161
-	public function withFragment($fragment): self
162
-	{
163
-		$fragment = $this->filterQueryAndFragment($fragment);
164
-		if ($this->fragment !== $fragment) {
165
-			$this->fragment = $fragment;
166
-		}
167
-
168
-		return $this;
169
-	}
170
-
171
-	private static function createUriString(string $scheme, string $authority, string $path, string $query, string $fragment): string
172
-	{
173
-		$uri = '';
174
-		if ('' !== $scheme) {
175
-			$uri .= $scheme . ':';
176
-		}
177
-
178
-		if ('' !== $authority) {
179
-			$uri .= '//' . $authority;
180
-		}
181
-
182
-		if ('' !== $path) {
183
-			if ('/' !== $path[0]) {
184
-				if ('' !== $authority) {
185
-					// If the path is rootless and an authority is present, the path MUST be prefixed by "/"
186
-					$path = '/' . $path;
187
-				}
188
-			} elseif (isset($path[1]) && '/' === $path[1]) {
189
-				if ('' === $authority) {
190
-					// If the path is starting with more than one "/" and no authority is present, the
191
-					// starting slashes MUST be reduced to one.
192
-					$path = '/' . \ltrim($path, '/');
193
-				}
194
-			}
195
-
196
-			$uri .= $path;
197
-		}
198
-
199
-		if ('' !== $query) {
200
-			$uri .= '?' . $query;
201
-		}
202
-
203
-		if ('' !== $fragment) {
204
-			$uri .= '#' . $fragment;
205
-		}
206
-
207
-		return $uri;
208
-	}
209
-
210
-	private static function isNonStandardPort(string $scheme, int $port): bool
211
-	{
212
-		return !isset(self::SCHEMES[$scheme]) || $port !== self::SCHEMES[$scheme];
213
-	}
214
-
215
-	protected function filterPort(int $port): ?int
216
-	{
217
-		if ($port < 1 || $port > 65535) {
218
-			throw new InvalidArgumentException(sprintf('Invalid port: %d. Must be between 0 and 65535', $port));
219
-		}
220
-
221
-		return self::isNonStandardPort($this->scheme, $port) ? $port : null;
222
-	}
223
-
224
-	private function filterPath($path): string
225
-	{
226
-		if (!is_string($path)) {
227
-			throw new InvalidArgumentException('Path must be a string');
228
-		}
229
-
230
-		return preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $path);
231
-	}
232
-
233
-	private function filterQueryAndFragment($str): string
234
-	{
235
-		if (!is_string($str)) {
236
-			throw new InvalidArgumentException('Query and fragment must be a string');
237
-		}
238
-
239
-		return preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $str);
240
-	}
241
-
242
-	private static function rawUrlEncodeMatchZero(array $match): string
243
-	{
244
-		return rawurlencode($match[0]);
245
-	}
15
+    use LowercaseTrait;
16
+
17
+    protected const SCHEMES = ['http' => 80, 'https' => 443];
18
+    protected const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
19
+    protected const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
20
+
21
+    protected string $scheme = '';
22
+    protected string $userInfo = '';
23
+    protected string $host = '';
24
+
25
+    protected ?int $port = null;
26
+    protected string $path = '';
27
+    protected string $query = '';
28
+    protected string $fragment = '';
29
+
30
+    public function __construct(string $uri = '')
31
+    {
32
+        if ('' !== $uri) {
33
+            $parts = parse_url($uri);
34
+            if ($parts === false) {
35
+                throw new InvalidArgumentException("Unable to parse URI: $uri");
36
+            }
37
+
38
+            $this->withScheme($parts['scheme'] ?? '');
39
+            $this->userInfo = $parts['user'] ?? '';
40
+            $this->withHost($parts['host'] ?? '');
41
+            $this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null;
42
+            $this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
43
+            $this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : '';
44
+            $this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : '';
45
+            if (isset($parts['pass'])) {
46
+                $this->userInfo .= ':' . $parts['pass'];
47
+            }
48
+        }
49
+    }
50
+
51
+    public function __toString(): string
52
+    {
53
+        return self::createUriString($this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment);
54
+    }
55
+
56
+    public function getScheme(): string
57
+    {
58
+        return $this->scheme;
59
+    }
60
+
61
+    public function getAuthority(): string
62
+    {
63
+        if ('' === $this->host) {
64
+            return '';
65
+        }
66
+
67
+        $authority = $this->host;
68
+        if ('' !== $this->userInfo) {
69
+            $authority = $this->userInfo . '@' . $authority;
70
+        }
71
+
72
+        if (null !== $this->port) {
73
+            $authority .= ':' . $this->port;
74
+        }
75
+
76
+        return $authority;
77
+    }
78
+
79
+    public function getUserInfo(): string
80
+    {
81
+        return $this->userInfo;
82
+    }
83
+
84
+    public function getHost(): string
85
+    {
86
+        return $this->host;
87
+    }
88
+
89
+    public function getPort(): ?int
90
+    {
91
+        return $this->port;
92
+    }
93
+
94
+    public function getPath(): string
95
+    {
96
+        return $this->path;
97
+    }
98
+
99
+    public function getQuery(): string
100
+    {
101
+        return $this->query;
102
+    }
103
+
104
+    public function getFragment(): string
105
+    {
106
+        return $this->fragment;
107
+    }
108
+
109
+    public function withScheme($scheme): self
110
+    {
111
+        $this->scheme = self::lowercase($scheme);
112
+        $this->port = $this->port ? $this->filterPort($this->port) : null;
113
+
114
+        return $this;
115
+    }
116
+
117
+    public function withUserInfo($user, $password = null): self
118
+    {
119
+        $info = $user;
120
+        if (null !== $password && '' !== $password) {
121
+            $info .= ':' . $password;
122
+        }
123
+
124
+        $this->userInfo = $info;
125
+
126
+        return $this;
127
+    }
128
+
129
+    public function withHost($host): self
130
+    {
131
+        $this->host = self::lowercase($host);
132
+        return $this;
133
+    }
134
+
135
+    public function withPort($port): self
136
+    {
137
+        $this->port = $port === null ? null : $this->filterPort($port);
138
+        return $this;
139
+    }
140
+
141
+    public function withPath($path): self
142
+    {
143
+        $path = $this->filterPath($path);
144
+        if ($this->path !== $path) {
145
+            $this->path = $path;
146
+        }
147
+
148
+        return $this;
149
+    }
150
+
151
+    public function withQuery($query): self
152
+    {
153
+        $query = $this->filterQueryAndFragment($query);
154
+        if ($this->query !== $query) {
155
+            $this->query = $query;
156
+        }
157
+
158
+        return $this;
159
+    }
160
+
161
+    public function withFragment($fragment): self
162
+    {
163
+        $fragment = $this->filterQueryAndFragment($fragment);
164
+        if ($this->fragment !== $fragment) {
165
+            $this->fragment = $fragment;
166
+        }
167
+
168
+        return $this;
169
+    }
170
+
171
+    private static function createUriString(string $scheme, string $authority, string $path, string $query, string $fragment): string
172
+    {
173
+        $uri = '';
174
+        if ('' !== $scheme) {
175
+            $uri .= $scheme . ':';
176
+        }
177
+
178
+        if ('' !== $authority) {
179
+            $uri .= '//' . $authority;
180
+        }
181
+
182
+        if ('' !== $path) {
183
+            if ('/' !== $path[0]) {
184
+                if ('' !== $authority) {
185
+                    // If the path is rootless and an authority is present, the path MUST be prefixed by "/"
186
+                    $path = '/' . $path;
187
+                }
188
+            } elseif (isset($path[1]) && '/' === $path[1]) {
189
+                if ('' === $authority) {
190
+                    // If the path is starting with more than one "/" and no authority is present, the
191
+                    // starting slashes MUST be reduced to one.
192
+                    $path = '/' . \ltrim($path, '/');
193
+                }
194
+            }
195
+
196
+            $uri .= $path;
197
+        }
198
+
199
+        if ('' !== $query) {
200
+            $uri .= '?' . $query;
201
+        }
202
+
203
+        if ('' !== $fragment) {
204
+            $uri .= '#' . $fragment;
205
+        }
206
+
207
+        return $uri;
208
+    }
209
+
210
+    private static function isNonStandardPort(string $scheme, int $port): bool
211
+    {
212
+        return !isset(self::SCHEMES[$scheme]) || $port !== self::SCHEMES[$scheme];
213
+    }
214
+
215
+    protected function filterPort(int $port): ?int
216
+    {
217
+        if ($port < 1 || $port > 65535) {
218
+            throw new InvalidArgumentException(sprintf('Invalid port: %d. Must be between 0 and 65535', $port));
219
+        }
220
+
221
+        return self::isNonStandardPort($this->scheme, $port) ? $port : null;
222
+    }
223
+
224
+    private function filterPath($path): string
225
+    {
226
+        if (!is_string($path)) {
227
+            throw new InvalidArgumentException('Path must be a string');
228
+        }
229
+
230
+        return preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $path);
231
+    }
232
+
233
+    private function filterQueryAndFragment($str): string
234
+    {
235
+        if (!is_string($str)) {
236
+            throw new InvalidArgumentException('Query and fragment must be a string');
237
+        }
238
+
239
+        return preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $str);
240
+    }
241
+
242
+    private static function rawUrlEncodeMatchZero(array $match): string
243
+    {
244
+        return rawurlencode($match[0]);
245
+    }
246 246
 }
247 247
\ No newline at end of file
Please login to merge, or discard this patch.