Skip to content

Commit a9c77ac

Browse files
wip support wildcards
1 parent f5fdd7b commit a9c77ac

File tree

3 files changed

+86
-12
lines changed

3 files changed

+86
-12
lines changed

packages/http/src/IsRequest.php

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Tempest\Validation\SkipValidation;
1111

1212
use function Tempest\get;
13+
use function Tempest\Support\Arr\every;
1314
use function Tempest\Support\Arr\get_by_key;
1415
use function Tempest\Support\Arr\has_key;
1516
use function Tempest\Support\str;
@@ -137,24 +138,46 @@ public function hasQuery(string $key): bool
137138
return has_key($this->query, $key);
138139
}
139140

140-
public function accepts(ContentType $contentType): bool
141+
public function accepts(ContentType ...$contentTypes): bool
141142
{
142-
$header = $this->headers->get(name: 'accept');
143+
$header = $this->headers->get(name: 'accept') ?? '';
143144

144-
if ($header === null) {
145-
return true;
145+
/** @var array{mediaType:string,subType:string} */
146+
$mediaTypes = [];
147+
148+
foreach (str($header)->explode(separator: ',') as $acceptedType) {
149+
$acceptedType = str($acceptedType)->trim();
150+
151+
if ($acceptedType->isEmpty()) {
152+
continue;
153+
}
154+
155+
$mediaTypes[] = [
156+
'mediaType' => $acceptedType->before('/')->toString(),
157+
'subType' => $acceptedType->afterFirst('/')->toString(),
158+
];
146159
}
147160

148-
$accepts = str($header)
149-
->explode(separator: ',')
150-
->map(static fn (string $item) => trim($item))
151-
->filter(static fn (string $item) => $item !== '');
161+
/** @var array<string, bool> */
162+
$supported = [];
152163

153-
if ($accepts->isEmpty() || $accepts->contains(search: '*/*')) {
154-
return true;
164+
foreach ($contentTypes as $contentType) {
165+
[$mediaType, $subType] = explode('/', $contentType->value);
166+
167+
foreach ($mediaTypes as $acceptedType) {
168+
if (
169+
($acceptedType['mediaType'] === '*' || $acceptedType['mediaType'] === $mediaType)
170+
&& ($acceptedType['subType'] === '*' || $acceptedType['subType'] === $subType)
171+
) {
172+
$supported[$contentType->value] = true;
173+
break;
174+
}
175+
176+
$supported[$contentType->value] = false;
177+
}
155178
}
156179

157-
return $accepts->contains(search: $contentType->value);
180+
return every($supported, static fn (bool $isSupported) => $isSupported);
158181
}
159182

160183
public function withMethod(Method $method): self

packages/http/src/Request.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,10 @@ public function getSessionValue(string $name): mixed;
5858

5959
public function getCookie(string $name): ?Cookie;
6060

61-
public function accepts(ContentType $contentType): bool;
61+
/**
62+
* Determines if the request's "Content-Type" header matches the given content type.
63+
*
64+
* If multiple content types are provided, the method returns true if all are matched.
65+
*/
66+
public function accepts(ContentType ...$contentType): bool;
6267
}

packages/http/tests/GenericRequestTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ public function test_accepts_with_no_accept_header(): void
9090
$this->assertTrue($request->accepts(ContentType::XML));
9191
}
9292

93+
public function test_accepts_with_empty_accept_header(): void
94+
{
95+
$request = new GenericRequest(
96+
method: Method::GET,
97+
uri: '/',
98+
headers: [
99+
'Accept' => '',
100+
],
101+
);
102+
103+
$this->assertTrue($request->accepts(ContentType::JSON));
104+
$this->assertTrue($request->accepts(ContentType::HTML));
105+
$this->assertTrue($request->accepts(ContentType::XML));
106+
}
107+
93108
public function test_accepts_with_wildcard(): void
94109
{
95110
$request = new GenericRequest(
@@ -119,4 +134,35 @@ public function test_accepts_with_multiple_values(): void
119134
$this->assertTrue($request->accepts(ContentType::HTML));
120135
$this->assertFalse($request->accepts(ContentType::XML));
121136
}
137+
138+
public function test_accepts_with_wildcard_subtype(): void
139+
{
140+
$request = new GenericRequest(
141+
method: Method::GET,
142+
uri: '/',
143+
headers: [
144+
'Accept' => 'application/*',
145+
],
146+
);
147+
148+
$this->assertTrue($request->accepts(ContentType::JSON));
149+
$this->assertFalse($request->accepts(ContentType::HTML));
150+
$this->assertTrue($request->accepts(ContentType::XML));
151+
}
152+
153+
public function test_accepts_evaluates_all_content_types(): void
154+
{
155+
$request = new GenericRequest(
156+
method: Method::GET,
157+
uri: '/',
158+
headers: [
159+
'Accept' => 'application/*, image/avif',
160+
],
161+
);
162+
163+
$this->assertFalse($request->accepts(ContentType::JSON, ContentType::HTML));
164+
$this->assertTrue($request->accepts(ContentType::JSON, ContentType::XML));
165+
$this->assertTrue($request->accepts(ContentType::JSON, ContentType::AVIF));
166+
$this->assertFalse($request->accepts(ContentType::AVIF, ContentType::PNG));
167+
}
122168
}

0 commit comments

Comments
 (0)