Skip to content

Commit e22492a

Browse files
authored
fix(http): collision between route and query params for uri generator (#687)
1 parent 687e333 commit e22492a

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

src/Tempest/Http/src/GenericRouter.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Tempest\Http\Routing\Matching\RouteMatcher;
2020
use function Tempest\map;
2121
use Tempest\Reflection\ClassReflector;
22+
use function Tempest\Support\str;
2223
use Tempest\Validation\Exceptions\ValidationException;
2324
use Tempest\View\View;
2425

@@ -125,11 +126,11 @@ public function toUri(array|string $action, ...$params): string
125126
$uri = $action;
126127
}
127128

129+
$uri = str($uri);
128130
$queryParams = [];
129131

130-
131132
foreach ($params as $key => $value) {
132-
if (! str_contains($uri, sprintf('{%s', $key))) {
133+
if (! $uri->matches(sprintf('/\{%s(\}|:)/', $key))) {
133134
$queryParams[$key] = $value;
134135

135136
continue;
@@ -139,17 +140,19 @@ public function toUri(array|string $action, ...$params): string
139140
$value = $value->value;
140141
}
141142

142-
$pattern = '#\{' . $key . Route::ROUTE_PARAM_CUSTOM_REGEX . '\}#';
143-
$uri = preg_replace($pattern, (string)$value, $uri);
143+
$uri = $uri->replaceRegex(
144+
'#\{' . $key . Route::ROUTE_PARAM_CUSTOM_REGEX . '\}#',
145+
(string)$value
146+
);
144147
}
145148

146-
$uri = rtrim($this->appConfig->baseUri, '/') . $uri;
149+
$uri = $uri->prepend(rtrim($this->appConfig->baseUri, '/'));
147150

148151
if ($queryParams !== []) {
149-
return $uri . '?' . http_build_query($queryParams);
152+
return $uri->append('?' . http_build_query($queryParams))->toString();
150153
}
151154

152-
return $uri;
155+
return $uri->toString();
153156
}
154157

155158
private function createResponse(Response|View $input): Response
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Tempest\Fixtures\Controllers;
6+
7+
use Tempest\Http\Get;
8+
use Tempest\Http\Response;
9+
use Tempest\Http\Responses\Ok;
10+
11+
final readonly class UriGeneratorController
12+
{
13+
#[Get('/test-with-collision/{idea}')]
14+
public function withCollidingNames(string $idea): Response
15+
{
16+
return new Ok();
17+
}
18+
}

tests/Integration/Route/RouterTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Tests\Tempest\Fixtures\Controllers\EnumForController;
1616
use Tests\Tempest\Fixtures\Controllers\TestController;
1717
use Tests\Tempest\Fixtures\Controllers\TestGlobalMiddleware;
18+
use Tests\Tempest\Fixtures\Controllers\UriGeneratorController;
1819
use Tests\Tempest\Fixtures\Migrations\CreateAuthorTable;
1920
use Tests\Tempest\Fixtures\Migrations\CreateBookTable;
2021
use Tests\Tempest\Fixtures\Modules\Books\Models\Author;
@@ -179,4 +180,12 @@ public function test_generate_uri_with_enum(): void
179180
uri(ControllerWithEnumBinding::class, input: EnumForController::BAR),
180181
);
181182
}
183+
184+
public function test_uri_with_query_param_that_collides_partially_with_route_param(): void
185+
{
186+
$this->assertSame(
187+
'/test-with-collision/hi?id=1',
188+
uri([UriGeneratorController::class, 'withCollidingNames'], id: '1', idea: 'hi')
189+
);
190+
}
182191
}

0 commit comments

Comments
 (0)