@@ -151,13 +151,58 @@ private function setNormalizedComponents(): void
151151 */
152152 private function withComponent (array $ components ): self
153153 {
154- try {
155- $ uri = UriString::build ([...$ this ->rawComponents , ...$ components ]);
156- } catch (Exception $ exception ) {
157- throw new InvalidUriException ($ exception ->getMessage (), previous: $ exception );
154+ return new self (UriString::build (
155+ $ this ->prepareModification ([...$ this ->rawComponents , ...$ components ])
156+ ));
157+ }
158+
159+ /**
160+ * Formatting the path when setting the path to avoid
161+ * exception to be thrown on an invalid path.
162+ * see https://github.com/php/php-src/issues/19897.
163+ *
164+ * @param InputComponentMap $components
165+ *
166+ * @return InputComponentMap
167+ */
168+ private function prepareModification (array $ components ): array
169+ {
170+ if (!isset ($ components ['path ' ]) || '' === $ components ['path ' ]) {
171+ return $ components ;
172+ }
173+
174+ $ path = $ components ['path ' ];
175+ $ isAbsolute = str_starts_with ($ path , '/ ' );
176+ $ authority = UriString::buildAuthority ($ components );
177+ if (null !== $ authority ) {
178+ // If there is an authority, the path must start with a `/`
179+ $ components ['path ' ] = $ isAbsolute ? $ path : '/ ' .$ path ;
180+
181+ return $ components ;
182+ }
183+
184+ // If there is no authority, the path cannot start with `//`
185+ if ($ isAbsolute ) {
186+ $ components ['path ' ] = '/. ' .$ path ;
187+
188+ return $ components ;
189+ }
190+
191+ $ colonPos = strpos ($ path , ': ' );
192+ if (false === $ colonPos ) {
193+ $ components ['path ' ] = $ path ;
194+
195+ return $ components ;
158196 }
159197
160- return new self ($ uri );
198+ // In the absence of a scheme and of an authority,
199+ // the first path segment cannot contain a colon (":") character.'
200+ $ slashPos = strpos ($ path , '/ ' );
201+ if (false === $ slashPos || $ colonPos < $ slashPos ) {
202+ $ components ['path ' ] = './ ' .$ path ;
203+ }
204+
205+ return $ components ;
161206 }
162207
163208 public function getRawScheme (): ?string
@@ -175,19 +220,11 @@ public function getScheme(): ?string
175220 */
176221 public function withScheme (?string $ scheme ): self
177222 {
178- if ($ scheme === $ this ->getRawScheme ()) {
179- return $ this ;
180- }
181-
182- if (!UriString::isValidScheme ($ scheme )) {
183- throw new InvalidUriException ('The scheme string component ` ' .$ scheme .'` is an invalid scheme. ' );
184- }
185-
186- $ components = $ this ->rawComponents ;
187- $ components ['scheme ' ] = $ scheme ;
188- $ components ['path ' ] = $ this ->preparePathForModification ($ components ['path ' ], $ components );
189-
190- return $ this ->withComponent ($ components );
223+ return match (true ) {
224+ $ scheme === $ this ->getRawScheme () => $ this ,
225+ UriString::isValidScheme ($ scheme ) => $ this ->withComponent (['scheme ' => $ scheme ]),
226+ default => throw new InvalidUriException ('The scheme string component ` ' .$ scheme .'` is an invalid scheme. ' ),
227+ };
191228 }
192229
193230 public function getRawUserInfo (): ?string
@@ -255,19 +292,11 @@ public function getHost(): ?string
255292 */
256293 public function withHost (?string $ host ): self
257294 {
258- if ($ host === $ this ->getRawHost ()) {
259- return $ this ;
260- }
261-
262- if (!UriString::isValidHost ($ host )) {
263- throw new InvalidUriException ('The host component value ` ' .$ host .'` is not a valid host. ' );
264- }
265-
266- $ components = $ this ->rawComponents ;
267- $ components ['host ' ] = $ host ;
268- $ components ['path ' ] = $ this ->preparePathForModification ($ components ['path ' ], $ components );
269-
270- return $ this ->withComponent ($ components );
295+ return match (true ) {
296+ $ host === $ this ->getRawHost () => $ this ,
297+ UriString::isValidHost ($ host ) => $ this ->withComponent (['host ' => $ host ]),
298+ default => throw new InvalidUriException ('The host component value ` ' .$ host .'` is not a valid host. ' ),
299+ };
271300 }
272301
273302 public function getPort (): ?int
@@ -310,7 +339,7 @@ public function withPath(string $path): self
310339 {
311340 return match (true ) {
312341 $ path === $ this ->getRawPath () => $ this ,
313- Encoder::isPathEncoded ($ path ) => $ this ->withComponent (['path ' => $ this -> preparePathForModification ( $ path, $ this -> rawComponents ) ]),
342+ Encoder::isPathEncoded ($ path ) => $ this ->withComponent (['path ' => $ path ]),
314343 default => throw new InvalidUriException ('The encoded path component ` ' .$ path .'` contains invalid characters. ' ),
315344 };
316345 }
@@ -428,41 +457,5 @@ public function __debugInfo(): array
428457 'fragment ' => $ this ->rawComponents ['fragment ' ],
429458 ];
430459 }
431-
432- /**
433- * Formatting the path when setting the path to avoid
434- * exception to be thrown on an invalid path.
435- * see https://github.com/php/php-src/issues/19897.
436- *
437- * @param InputComponentMap $components
438- */
439- private function preparePathForModification (string $ path , array $ components ): string
440- {
441- $ isAbsolute = str_starts_with ($ path , '/ ' );
442- $ authority = UriString::buildAuthority ($ components );
443- if (null !== $ authority ) {
444- // If there is an authority, the path must start with a `/`
445- return $ isAbsolute ? $ path : '/ ' .$ path ;
446- }
447-
448- // If there is no authority, the path cannot start with `//`
449- if ($ isAbsolute ) {
450- return '/. ' .$ path ;
451- }
452-
453- $ colonPos = strpos ($ path , ': ' );
454- if (false === $ colonPos ) {
455- return $ path ;
456- }
457-
458- // In the absence of a scheme and of an authority,
459- // the first path segment cannot contain a colon (":") character.'
460- $ slashPos = strpos ($ path , '/ ' );
461- if (false === $ slashPos || $ colonPos < $ slashPos ) {
462- return './ ' .$ path ;
463- }
464-
465- return $ path ;
466- }
467460 }
468461}
0 commit comments