Skip to content
This repository was archived by the owner on Sep 22, 2025. It is now read-only.

Commit 0d4d540

Browse files
committed
Improve Url implementation
1 parent 9282357 commit 0d4d540

File tree

3 files changed

+66
-8
lines changed

3 files changed

+66
-8
lines changed

src/WhatWg/Url.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ public function getScheme(): string
8888

8989
public function withScheme(string $scheme): self
9090
{
91+
$scheme = strtolower($scheme);
92+
if ($scheme === $this->getScheme() || $scheme === $this->url->protocol) {
93+
return $this;
94+
}
95+
9196
$copy = $this->copy();
9297
$copy->url->protocol = $scheme;
9398

@@ -101,6 +106,10 @@ public function getUsername(): ?string
101106

102107
public function withUsername(?string $user): self
103108
{
109+
if ($user === $this->getUsername() || $user === $this->url->username) {
110+
return $this;
111+
}
112+
104113
$copy = $this->copy();
105114
$copy->url->username = (string) $user;
106115

@@ -114,6 +123,10 @@ public function getPassword(): ?string
114123

115124
public function withPassword(#[SensitiveParameter] ?string $password): self
116125
{
126+
if ($password === $this->getPassword() || $password === $this->url->password) {
127+
return $this;
128+
}
129+
117130
$copy = $this->copy();
118131
$copy->url->password = (string) $password;
119132

@@ -142,6 +155,10 @@ public function getUnicodeHost(): ?string
142155

143156
public function withHost(string $host): self
144157
{
158+
if ($host === $this->getAsciiHost() || $host === $this->getUnicodeHost()) {
159+
return $this;
160+
}
161+
145162
$copy = $this->copy();
146163
$copy->url->hostname = $host;
147164

@@ -155,6 +172,10 @@ public function getPort(): ?int
155172

156173
public function withPort(?int $port): self
157174
{
175+
if ($port === $this->getPort()) {
176+
return $this;
177+
}
178+
158179
$copy = $this->copy();
159180
$copy->url->port = (string) $port;
160181

@@ -168,6 +189,10 @@ public function getPath(): string
168189

169190
public function withPath(string $path): self
170191
{
192+
if ($path === $this->url->pathname) {
193+
return $this;
194+
}
195+
171196
$copy = $this->copy();
172197
$copy->url->pathname = $path;
173198

@@ -186,6 +211,10 @@ public function getQuery(): ?string
186211

187212
public function withQuery(?string $query): self
188213
{
214+
if ($query === $this->url->search || $query === $this->getQuery()) {
215+
return $this;
216+
}
217+
189218
$copy = $this->copy();
190219
$copy->url->search = (string) $query;
191220

@@ -204,6 +233,10 @@ public function getFragment(): ?string
204233

205234
public function withFragment(?string $fragment): self
206235
{
236+
if ($fragment === $this->url->hash || $fragment === $this->getFragment()) {
237+
return $this;
238+
}
239+
207240
$copy = $this->copy();
208241
$copy->url->hash = (string) $fragment;
209242

src/WhatWg/UrlValidationErrorCollector.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,16 @@ public function log(mixed $level, string|Stringable $message, array $context = [
8181

8282
$this->errors[] = new UrlValidationError(
8383
$errorContext,
84+
// \Rowbot\URL\URL makes no usage of string interpolation
85+
// the message is a string representing one of
86+
// the defined \Uri\WhatWg\UrlValidationErrorType case
87+
// mapping is done using https://url.spec.whatwg.org/#writing
8488
match ((string) $message) {
85-
'missing-scheme-non-relative-URL' => UrlValidationErrorType::MissingSchemeNonRelativeUrl,
86-
'special-scheme-missing-following-solidus' => UrlValidationErrorType::SpecialSchemeMissingFollowingSolidus,
87-
'invalid-credentials' => UrlValidationErrorType::InvalidCredentials,
88-
'invalid-reverse-solidus' => UrlValidationErrorType::InvalidReverseSoldius,
89+
// IDNA Error Tyoe
8990
'domain-to-ASCII' => UrlValidationErrorType::DomainToAscii,
9091
'domain-to-unicode' => UrlValidationErrorType::DomainToUnicode,
9192
'domain-invalid-code-point' => UrlValidationErrorType::DomainInvalidCodePoint,
93+
// Host parsing
9294
'host-invalid-code-point' => UrlValidationErrorType::HostInvalidCodePoint,
9395
'IPv4-part-empty' => UrlValidationErrorType::Ipv4EmptyPart,
9496
'IPv4-non-decimal-part' => UrlValidationErrorType::Ipv4NonDecimalPart,
@@ -105,14 +107,21 @@ public function log(mixed $level, string|Stringable $message, array $context = [
105107
'IPv4-in-IPv6-too-few-parts' => UrlValidationErrorType::Ipv4InIpv6TooFewParts,
106108
'IPv4-in-IPv6-invalid-code-point' => UrlValidationErrorType::Ipv4InIpv6InvalidCodePoint,
107109
'IPv4-in-IPv6-too-many-pieces' => UrlValidationErrorType::Ipv4InIpv6TooManyPieces,
110+
// URL parsing
111+
'invalid-URL-unit' => UrlValidationErrorType::InvalidUrlUnit,
112+
'special-scheme-missing-following-solidus' => UrlValidationErrorType::SpecialSchemeMissingFollowingSolidus,
113+
'missing-scheme-non-relative-URL' => UrlValidationErrorType::MissingSchemeNonRelativeUrl,
114+
'invalid-credentials' => UrlValidationErrorType::InvalidCredentials,
115+
'invalid-reverse-solidus' => UrlValidationErrorType::InvalidReverseSoldius,
108116
'host-missing' => UrlValidationErrorType::HostMissing,
109117
'port-out-of-range' => UrlValidationErrorType::PortOutOfRange,
110118
'port-invalid' => UrlValidationErrorType::PortInvalid,
111-
'invalid-URL-unit' => UrlValidationErrorType::InvalidUrlUnit,
112119
'file-invalid-Windows-drive-letter-host' => UrlValidationErrorType::FileInvalidWindowsDriveLetterHost,
113120
'file-invalid-Windows-drive-letter' => UrlValidationErrorType::FileInvalidWindowsDriveLetter,
114121
default => throw new OutOfBoundsException('unknown error type:'.$message),
115122
},
123+
// \Rowbot\URL\URL emits LogLevel::WARNING when the error is a failure
124+
// otherwise LogLevel::NOTICE is used
116125
LogLevel::WARNING === $level,
117126
);
118127
}

tests/WhatWg/UrlTest.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function it_will_resolve_an_uri(): void
5050
}
5151

5252
#[Test]
53-
public function it_can_retrive_url_components(): void
53+
public function it_can_retrieve_url_components(): void
5454
{
5555
$url = new Url("HTTPS://%61pple:p%61ss@ex%61mple.com:433/foob%61r?%61bc=%61bc#%61bc");
5656

@@ -191,9 +191,7 @@ public function it_will_not_update_on_invalid_with_input(): void
191191
$url = new Url("https://user:pass@example.com/foo/bar");
192192
$urlBis = $url
193193
->withScheme('gopher')
194-
->withUsername('user')
195194
->withPort(12345678)
196-
->withPassword('pass')
197195
->withHost('::1');
198196

199197
self::assertTrue($urlBis->equals($url));
@@ -213,4 +211,22 @@ public function it_will_handle_window_uri(): void
213211
self::assertSame("file:///c:/Users/JohnDoe/Documents/report.txt", $url->toUnicodeString());
214212
self::assertSame($url->toUnicodeString(), $url->toAsciiString());
215213
}
214+
215+
#[Test]
216+
public function it_return_the_same_instance_if_nothing_is_changed(): void
217+
{
218+
$url = new Url("https://apple:pass@example.com:433/foobar?abc=abc#abc");
219+
$urlBis = $url
220+
->withScheme('https:')
221+
->withUsername('apple')
222+
->withPassword('pass')
223+
->withHost('example.com')
224+
->withPort(433)
225+
->withPath('/foobar')
226+
->withQuery('?abc=abc')
227+
->withFragment('#abc');
228+
229+
self::assertTrue($urlBis->equals($url));
230+
self::assertSame($urlBis, $url);
231+
}
216232
}

0 commit comments

Comments
 (0)