Skip to content

Commit 7a9ca5a

Browse files
authored
Add strict routing
1 parent 047b8d0 commit 7a9ca5a

2 files changed

Lines changed: 52 additions & 36 deletions

File tree

Slim/Routing/Router.php

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,17 @@ public function map(array $methods, string $path, callable|string $handler): Rou
4242
throw new InvalidArgumentException('HTTP methods array cannot be empty');
4343
}
4444

45-
$routePattern = $this->normalizePath($path);
46-
$route = new Route($methods, $routePattern, $handler, null);
45+
$route = new Route($methods, $path, $handler, null);
4746

48-
$this->collector->addRoute($methods, $routePattern, $route);
47+
$this->collector->addRoute($methods, $path, $route);
4948

5049
return $route;
5150
}
5251

5352
public function group(string $path, callable $handler): RouteGroup
5453
{
55-
$routePattern = $this->normalizePath($path);
56-
$routeGroup = new RouteGroup($routePattern, $handler, $this->getRouteCollector());
57-
$this->collector->addGroup($routePattern, $routeGroup);
54+
$routeGroup = new RouteGroup($path, $handler, $this->getRouteCollector());
55+
$this->collector->addGroup($path, $routeGroup);
5856

5957
return $routeGroup;
6058
}
@@ -80,27 +78,4 @@ public function handle(ServerRequestInterface $request): ResponseInterface
8078
->withPipeline($this->getMiddleware())
8179
->handle($request);
8280
}
83-
84-
/**
85-
* Normalizes a path by ensuring:
86-
* - Starts with a forward slash
87-
* - No trailing slash (unless root path)
88-
* - No double slashes
89-
*/
90-
private function normalizePath(string $path): string
91-
{
92-
// If path is empty or just a slash, return single slash
93-
if ($path === '' || $path === '/') {
94-
return '/';
95-
}
96-
97-
// Ensure path starts with a slash
98-
$path = '/' . ltrim($path, '/');
99-
100-
// Remove trailing slash unless it's the root path
101-
$path = rtrim($path, '/');
102-
103-
// Replace multiple consecutive slashes with a single slash
104-
return preg_replace('#/+#', '/', $path) ?? '';
105-
}
10681
}

tests/AppTest.php

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,20 +277,20 @@ public static function routePatternsProvider(): array
277277
{
278278
return [
279279
// Route pattern -> http uri
280-
// Empty route
281-
['', '/'],
282280
// Single slash route
283281
['/', '/'],
284-
// Route That Does Not Start With A Slash
285-
['foo', '/foo'],
286282
// Route That Does Not End In A Slash
287283
['/foo', '/foo'],
288284
// Route That Ends In A Slash
289-
['/foo/', '/foo'],
285+
['/foo/', '/foo/'],
290286
// Route That Ends In A double Slash
291-
['/foo//', '/foo'],
287+
['/foo//', '/foo//'],
292288
// Route That contains In A double Slash
293-
['/foo//bar', '/foo/bar'],
289+
['/foo//bar', '/foo//bar'],
290+
// FastRoute optional trailing slash segment matches /foo
291+
['/foo[/]', '/foo'],
292+
// FastRoute optional trailing slash segment matches /foo/
293+
['/foo[/]', '/foo/'],
294294
];
295295
}
296296

@@ -315,6 +315,47 @@ public function testRoutePatterns(string $pattern, string $uri): void
315315
$this->assertSame('Hello World', (string)$response->getBody());
316316
}
317317

318+
public static function strictRouteMismatchProvider(): array
319+
{
320+
return [
321+
'foo does not match foo trailing slash' => [
322+
'/foo', // route pattern
323+
'/foo/', // request URI
324+
],
325+
'foo trailing slash does not match foo' => [
326+
'/foo/', // route pattern
327+
'/foo', // request URI
328+
],
329+
'foo does not match FOO uppercase' => [
330+
'/foo', // route pattern
331+
'/FOO', // request URI
332+
],
333+
'route without leading slash does not match' => [
334+
'foo', // route pattern
335+
'/foo', // request URI
336+
],
337+
];
338+
}
339+
340+
#[DataProvider('strictRouteMismatchProvider')]
341+
public function testStrictRouteMismatch(string $routePattern, string $requestUri): void
342+
{
343+
$this->expectException(HttpNotFoundException::class);
344+
345+
$app = AppFactory::create();
346+
$app->addRoutingMiddleware();
347+
348+
$request = $this
349+
->getServerRequestFactory($app)
350+
->createServerRequest('GET', $requestUri);
351+
352+
$app->get($routePattern, function () {
353+
// noop
354+
});
355+
356+
$app->handle($request);
357+
}
358+
318359
/********************************************************************************
319360
* Route Groups
320361
*******************************************************************************/

0 commit comments

Comments
 (0)