Skip to content

Commit

Permalink
add possibility to define a route pattern inside the route group
Browse files Browse the repository at this point in the history
  • Loading branch information
IngeniozIT committed Oct 9, 2023
1 parent 32e0e72 commit aa03269
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 24 deletions.
15 changes: 9 additions & 6 deletions src/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,10 @@ public function __construct(
}

/**
* @param array<string, string> $additionalPatterns
* @return false|array<string, string>
*/
public function match(ServerRequestInterface $request): false|array
public function match(ServerRequestInterface $request, array $additionalPatterns = []): false|array
{
if (!$this->httpMethodMatches($request->getMethod())) {
return false;
Expand All @@ -140,7 +141,7 @@ public function match(ServerRequestInterface $request): false|array
return $path === $this->path ? [] : false;
}

$extractedParameters = $this->extractParametersValues($parameters, $path);
$extractedParameters = $this->extractParametersValues($parameters, $path, $additionalPatterns);
return $extractedParameters === [] ? false : $extractedParameters;
}

Expand All @@ -160,24 +161,26 @@ private function extractParametersFromPath(string $path): array

/**
* @param string[][] $parameters
* @param array<string, string> $additionalPatterns
* @return array<string, string>
*/
private function extractParametersValues(array $parameters, string $path): array
private function extractParametersValues(array $parameters, string $path, array $additionalPatterns): array
{
preg_match($this->buildRegex($parameters), $path, $parameters);
preg_match($this->buildRegex($parameters, $additionalPatterns), $path, $parameters);
return array_filter($parameters, 'is_string', ARRAY_FILTER_USE_KEY);
}

/**
* @param string[][] $parameters
* @param array<string, string> $additionalPatterns
*/
private function buildRegex(array $parameters): string
private function buildRegex(array $parameters, array $additionalPatterns): string
{
$quotedPath = '#' . preg_quote($this->path, '#') . '#';
foreach ($parameters as $parameter) {
$quotedPath = str_replace(
preg_quote($parameter[0], '#'),
'(?<' . $parameter[1] . '>' . ($parameter[2] ?? $this->patterns[$parameter[1]] ?? '[^/]+') . ')',
'(?<' . $parameter[1] . '>' . ($parameter[2] ?? $this->patterns[$parameter[1]] ?? $additionalPatterns[$parameter[1]] ?? '[^/]+') . ')',
$quotedPath
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private function callMiddlewareHandler(mixed $callback, ServerRequestInterface $
private function executeRoutes(ServerRequestInterface $request): ResponseInterface
{
foreach ($this->routeGroup->routes as $route) {
$matchedParams = $route->match($request);
$matchedParams = $route->match($request, $this->routeGroup->patterns);
if ($matchedParams === false) {
continue;
}
Expand Down
58 changes: 41 additions & 17 deletions tests/RouterRouteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,30 +89,18 @@ public static function providerInvalidHandlers(): array
];
}

public function testFiltersOutRoutesWithWrongPath(): void
public function testFiltersOutNonMatchingRoutes(): void
{
$routeGroup = new RouteGroup(routes: [
Route::get(path: '/test', callback: static fn(): ResponseInterface => self::response('TEST')),
Route::get(path: '/test2', callback: static fn(): ResponseInterface => self::response('TEST2')),
Route::get(path: '/test', callback: static fn(): ResponseInterface => self::response('KO')),
Route::post(path: '/test2', callback: static fn(): ResponseInterface => self::response('KO')),
Route::get(path: '/test2', callback: static fn(): ResponseInterface => self::response('OK')),
]);
$request = self::serverRequest('GET', '/test2');

$response = $this->router($routeGroup)->handle($request);

self::assertEquals('TEST2', (string) $response->getBody());
}

public function testFiltersOutRoutesWithWrongMethod(): void
{
$routeGroup = new RouteGroup(routes: [
Route::get(path: '/', callback: static fn(): ResponseInterface => self::response('TEST')),
Route::post(path: '/', callback: static fn(): ResponseInterface => self::response('TEST2')),
]);
$request = self::serverRequest('POST', '/');

$response = $this->router($routeGroup)->handle($request);

self::assertEquals('TEST2', (string) $response->getBody());
self::assertEquals('OK', (string) $response->getBody());
}

public function testAddsMatchedParametersToRequest(): void
Expand All @@ -131,6 +119,42 @@ public function testAddsMatchedParametersToRequest(): void
self::assertEquals("'bar'", (string) $response->getBody());
}

/**
* @dataProvider providerRouteGroupsWithCustomParameters
*/
public function testCanHaveCustomParameters(RouteGroup $routeGroup): void
{
$matchingRequest = self::serverRequest('GET', '/123');
$nonMatchingRequest = self::serverRequest('GET', '/abc');

$matchingResponse = $this->router($routeGroup)->handle($matchingRequest);
$nonMatchingResponse = $this->router($routeGroup, static fn(): string => 'KO')->handle($nonMatchingRequest);

self::assertEquals('OK', (string) $matchingResponse->getBody());
self::assertEquals('KO', (string) $nonMatchingResponse->getBody());
}

/**
* @return array<string, array{0: RouteGroup}>
*/
public static function providerRouteGroupsWithCustomParameters(): array
{
return [
'pattern defined in route group' => [
new RouteGroup(
routes: [Route::get(path: '/{foo}', callback: static fn(): string => 'OK')],
patterns: ['foo' => '\d+'],
)
],
'route pattern takes precedence over route group pattern' => [
new RouteGroup(
routes: [Route::get(path: '/{foo}', callback: static fn(): string => 'OK', patterns: ['foo' => '\d+'])],
patterns: ['foo' => '[a-z]+'],
)
],
];
}

public function testMustFindARouteToProcess(): void
{
$routeGroup = new RouteGroup(routes: [
Expand Down

0 comments on commit aa03269

Please sign in to comment.