Skip to content

Commit 7b1a49b

Browse files
committed
Add Unittests for new changes
1 parent ac02ed6 commit 7b1a49b

File tree

3 files changed

+100
-6
lines changed

3 files changed

+100
-6
lines changed

src/Map.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -431,20 +431,32 @@ public function getAsTreeRouteNode()
431431
{
432432
$treeRoutes = [];
433433
foreach ($this->routes as $route) {
434-
if (! $route->isRoutable) {
434+
if (! $route->isRoutable || $route->path === null) {
435435
continue;
436436
}
437437

438-
// replace all parameters with {}
438+
// replace "{/year,month,day}" parameters with /{}/{}/{}
439+
$routePath = preg_replace_callback(
440+
'~{/((?:\w+,?)+)}~',
441+
static function (array $matches) {
442+
$variables = explode(',', $matches[1]);
443+
444+
return '/' . implode('/', array_fill(0, count($variables), '{}'));
445+
},
446+
$route->path
447+
) ?: $route->path;
448+
$paramsAreOptional = $routePath !== $route->path;
449+
439450
// This regexp will also work with "{controller:[a-zA-Z][a-zA-Z0-9_-]{1,}}"
440-
$routePath = preg_replace('~{(?:[^{}]*|(?R))*}~', '{}', $route->path);
451+
$routePath = preg_replace('~{(?:[^{}]*|(?R))*}~', '{}', $routePath) ?: $routePath;
441452
$node = &$treeRoutes;
442453
foreach (explode('/', trim($routePath, '/')) as $segment) {
443-
if (strpos($segment, '{') === 0 || strpos($segment, ':') === 0) {
444-
for ($i = 0; $i <= substr_count($segment, ','); $i++) {
445-
$node = &$node['{}'];
454+
if (strpos($segment, '{') === 0) {
455+
if ($paramsAreOptional) {
446456
$node[spl_object_hash($route)] = $route;
447457
}
458+
$node = &$node['{}'];
459+
$node[spl_object_hash($route)] = $route;
448460
continue;
449461
}
450462
$node = &$node[$segment];

tests/MapTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,53 @@ public function testGetAndSetRoutes()
137137
$this->assertIsRoute($actual['page.read']);
138138
$this->assertEquals('/page/{id}{format}', $actual['page.read']->path);
139139
}
140+
141+
public function testGetAsTreeRouteNodeSuccess(): void
142+
{
143+
$routes = [
144+
(new Route())->path('/api/users'),
145+
(new Route())->path('/api/users/{id}'),
146+
(new Route())->path('/api/users/{id}/delete'),
147+
(new Route())->path('/api/archive{/year,month,day}'),
148+
(new Route())->path('/api/{controller:[a-zA-Z][a-zA-Z0-9_-]{1,}}/{action}'),
149+
(new Route())->path('/api/users/{id}/not-routeable')->isRoutable(false),
150+
];
151+
$sut = new Map(new Route());
152+
$sut->setRoutes($routes);
153+
154+
$result = $sut->getAsTreeRouteNode();
155+
156+
$this->assertSame([
157+
'api' => [
158+
'users' => [
159+
spl_object_hash($routes[0]) => $routes[0],
160+
'{}' => [
161+
spl_object_hash($routes[1]) => $routes[1],
162+
spl_object_hash($routes[2]) => $routes[2],
163+
'delete' => [
164+
spl_object_hash($routes[2]) => $routes[2],
165+
],
166+
],
167+
],
168+
'archive' => [
169+
spl_object_hash($routes[3]) => $routes[3],
170+
'{}' => [ // year
171+
spl_object_hash($routes[3]) => $routes[3],
172+
'{}' => [ // month
173+
spl_object_hash($routes[3]) => $routes[3],
174+
'{}' => [ // day
175+
spl_object_hash($routes[3]) => $routes[3],
176+
],
177+
],
178+
],
179+
],
180+
'{}' => [
181+
spl_object_hash($routes[4]) => $routes[4],
182+
'{}' => [
183+
spl_object_hash($routes[4]) => $routes[4],
184+
],
185+
],
186+
],
187+
], $result);
188+
}
140189
}

tests/MatcherTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<?php
22
namespace Aura\Router;
33

4+
use Aura\Router\Rule\RuleIterator;
5+
use Psr\Log\LoggerInterface;
46
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
57
use GuzzleHttp\Psr7\ServerRequest;
68

@@ -264,4 +266,35 @@ public function testLogger()
264266
$this->assertSame($expect, $actual);
265267
$this->assertRoute($bar, $matcher->getMatchedRoute());
266268
}
269+
270+
public function testMatchWithMatchedTree()
271+
{
272+
$routes = [
273+
(new Route())->path('/api/users'),
274+
(new Route())->path('/api/users/{id}'),
275+
(new Route())->path('/api/users/{id}/delete'),
276+
(new Route())->path('/api/archive{/year,month,day}'),
277+
(new Route())->path('/api/{controller:[a-zA-Z][a-zA-Z0-9_-]{1,}}/{action}'),
278+
(new Route())->path('/not-routeable')->isRoutable(false),
279+
];
280+
$map = new Map(new Route());
281+
$map->setRoutes($routes);
282+
283+
$sut = new Matcher(
284+
$map,
285+
$this->createMock(LoggerInterface::class),
286+
new RuleIterator()
287+
);
288+
289+
self::assertNotFalse($sut->match($this->newRequest('/api/users')));
290+
self::assertNotFalse($sut->match($this->newRequest('/api/users/1')));
291+
self::assertNotFalse($sut->match($this->newRequest('/api/users/1/delete')));
292+
self::assertNotFalse($sut->match($this->newRequest('/api/archive')));
293+
self::assertNotFalse($sut->match($this->newRequest('/api/archive/2025')));
294+
self::assertNotFalse($sut->match($this->newRequest('/api/archive/2025/05')));
295+
self::assertNotFalse($sut->match($this->newRequest('/api/archive/2025/05/22')));
296+
self::assertNotFalse($sut->match($this->newRequest('/api/valid-controller-name/action')));
297+
298+
self::assertFalse($sut->match($this->newRequest('/not-routeable')));
299+
}
267300
}

0 commit comments

Comments
 (0)