3232 * @see https://wiki.php.net/rfc/url_parsing_api
3333 *
3434 * @phpstan-type Components array{scheme: ?string, userInfo: ?string, user: ?string, pass: ?string, host: ?string, port: ?int, path: string, query: ?string, fragment: ?string}
35+ * @phpstan-import-type ComponentMap from UriString
36+ * @phpstan-import-type InputComponentMap from UriString
3537 */
3638 final class Uri
3739 {
@@ -56,13 +58,12 @@ public function __construct(string $uri, ?self $baseUri = null)
5658
5759 try {
5860 $ uri = null !== $ baseUri ? UriString::resolve ($ uri , $ baseUri ->toRawString ()) : $ uri ;
59- $ components = self ::addUserInfo (UriString::parse ($ uri ));
61+ $ components = self ::addUserInfoComponent (UriString::parse ($ uri ));
6062 } catch (Exception $ exception ) {
6163 throw new InvalidUriException ($ exception ->getMessage (), previous: $ exception );
6264 }
6365
64- Encoder::isUserEncoded ($ components ['user ' ]) || throw new InvalidUriException ('The encoded userInfo string component ` ' .$ components ['userInfo ' ].'` contains invalid characters. ' );
65- Encoder::isPasswordEncoded ($ components ['pass ' ]) || throw new InvalidUriException ('The encoded userInfo component ` ' .$ components ['userInfo ' ].'` contains invalid characters. ' );
66+ Encoder::isUserInfoEncoded ($ components ['userInfo ' ]) || throw new InvalidUriException ('The encoded userInfo string component ` ' .$ components ['userInfo ' ].'` contains invalid characters. ' );
6667 Encoder::isPathEncoded ($ components ['path ' ]) || throw new InvalidUriException ('The encoded path component ` ' .$ components ['path ' ].'` contains invalid characters. ' );
6768 Encoder::isQueryEncoded ($ components ['query ' ]) || throw new InvalidUriException ('The encoded query string component ` ' .$ components ['query ' ].'` contains invalid characters. ' );
6869 Encoder::isFragmentEncoded ($ components ['fragment ' ]) || throw new InvalidUriException ('The encoded fragment string component ` ' .$ components ['fragment ' ].'` contains invalid characters. ' );
@@ -73,29 +74,20 @@ public function __construct(string $uri, ?self $baseUri = null)
7374 }
7475
7576 /**
76- * Split the URI into its own component following RFC3986 rules.
77- *
78- * @link https://tools.ietf.org/html/rfc3986
79- *
80- * @param array{scheme: ?string, user: ?string, pass: ?string, host: ?string, port: ?int, path: string, query: ?string, fragment: ?string} $parts The URI components
77+ * @param ComponentMap $parts
8178 *
8279 * @return Components
8380 */
84- private static function addUserInfo (array $ parts ): array
81+ private static function addUserInfoComponent (array $ parts ): array
8582 {
8683 $ components = [...self ::DEFAULT_COMPONENTS , ...$ parts ];
87- if (null === $ components ['user ' ]) {
84+ $ components ['userInfo ' ] = $ components ['user ' ];
85+ if (null === $ components ['user ' ] || null === $ components ['pass ' ]) {
8886 $ components ['pass ' ] = null ;
89- $ components ['userInfo ' ] = null ;
9087
9188 return $ components ;
9289 }
9390
94- $ components ['userInfo ' ] = $ components ['user ' ];
95- if (null === $ components ['pass ' ]) {
96- return $ components ;
97- }
98-
9991 $ components ['userInfo ' ] .= ': ' .$ components ['pass ' ];
10092
10193 return $ components ;
@@ -134,14 +126,14 @@ private function setNormalizedComponents(): void
134126 }
135127
136128 $ this ->normalizedComponents = [
137- ...self ::addUserInfo (UriString::parseNormalized ($ this ->rawUri )),
129+ ...self ::addUserInfoComponent (UriString::parseNormalized ($ this ->rawUri )),
138130 ...['host ' => Encoder::normalizeHost ($ this ->rawComponents ['host ' ])],
139131 ];
140132 $ this ->isNormalized = true ;
141133 }
142134
143135 /**
144- * @param array{scheme?: ?string, user?: ?string, pass?: ?string, host?: ?string, port?: ?int, path?: string, query?: ?string, fragment?: ?string} $components
136+ * @param InputComponentMap $components
145137 *
146138 * @throws InvalidUriException
147139 */
@@ -173,7 +165,7 @@ public function withScheme(?string $scheme): self
173165 {
174166 return match (true ) {
175167 $ scheme === $ this ->getRawScheme () => $ this ,
176- UriString::isScheme ($ scheme ) => $ this ->withComponent (['scheme ' => $ scheme ]),
168+ UriString::isValidScheme ($ scheme ) => $ this ->withComponent (['scheme ' => $ scheme ]),
177169 default => throw new InvalidUriException ('The scheme string component ` ' .$ scheme .'` is an invalid scheme. ' ),
178170 };
179171 }
@@ -197,15 +189,15 @@ public function withUserInfo(#[SensitiveParameter] ?string $userInfo): self
197189 return $ this ;
198190 }
199191
200- if (null === $ userInfo ) {
201- return $ this ->withComponent (['user ' => null , 'pass ' => null ]);
202- }
192+ Encoder::isUserInfoEncoded ($ userInfo ) || throw new InvalidUriException ('The encoded userInfo string component ` ' .$ userInfo .'` contains invalid characters. ' );
203193
204- [$ user , $ password ] = explode (': ' , $ userInfo , 2 ) + [1 => null ];
205- Encoder::isUserEncoded ($ user ) || throw new InvalidUriException ('The encoded userInfo string component ` ' .$ userInfo .'` contains invalid characters. ' );
206- Encoder::isPasswordEncoded ($ password ) || throw new InvalidUriException ('The encoded userInfo string component ` ' .$ userInfo .'` contains invalid characters. ' );
194+ $ user = null ;
195+ $ pass = null ;
196+ if (null !== $ userInfo ) {
197+ [$ user , $ pass ] = explode (': ' , $ userInfo , 2 ) + [1 => null ];
198+ }
207199
208- return $ this ->withComponent (['user ' => $ user , 'pass ' => $ password ]);
200+ return $ this ->withComponent (['user ' => $ user , 'pass ' => $ pass ]);
209201 }
210202
211203 public function getRawUsername (): ?string
@@ -245,7 +237,7 @@ public function withHost(?string $host): self
245237 {
246238 return match (true ) {
247239 $ host === $ this ->getRawHost () => $ this ,
248- UriString::isHost ($ host ) => $ this ->withComponent (['host ' => $ host ]),
240+ UriString::isValidHost ($ host ) => $ this ->withComponent (['host ' => $ host ]),
249241 default => throw new InvalidUriException ('The host component value ` ' .$ host .'` is not a valid host. ' ),
250242 };
251243 }
@@ -367,15 +359,15 @@ public function resolve(string $uri): self
367359 }
368360
369361 /**
370- * @return array{0: array{uri: string}, 1: array{}}
362+ * @return UriSerializedShape
371363 */
372364 public function __serialize (): array
373365 {
374366 return [['uri ' => $ this ->toRawString ()], []];
375367 }
376368
377369 /**
378- * @param array{0: array{uri: string}, 1: array{}} $data
370+ * @param UriSerializedShape $data
379371 *
380372 * @throws Exception|InvalidUriException
381373 */
@@ -390,7 +382,7 @@ public function __unserialize(array $data): void
390382 }
391383
392384 /**
393- * @return array{scheme: ?string, username: ?string, password: ?string, host: ?string, port: ?int, path: string, query: ?string, fragment: ?string}
385+ * @return UriDebugShape
394386 */
395387 public function __debugInfo (): array
396388 {
0 commit comments