Skip to content

Commit ac02ed6

Browse files
committed
Convert routes to a node tree to improve route matching perfomance
1 parent c63236f commit ac02ed6

File tree

3 files changed

+68
-8
lines changed

3 files changed

+68
-8
lines changed

src/Map.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,40 @@ public function attach($namePrefix, $pathPrefix, callable $callable)
420420
$callable($this);
421421
$this->protoRoute = $old;
422422
}
423+
424+
/**
425+
* Convert all routes into a node tree array structure that contains all possible routes per route segment
426+
* This will reduce the amount of possible routes to check
427+
*
428+
* @return array<string, Route|array<string, mixed>>
429+
*/
430+
public function getAsTreeRouteNode()
431+
{
432+
$treeRoutes = [];
433+
foreach ($this->routes as $route) {
434+
if (! $route->isRoutable) {
435+
continue;
436+
}
437+
438+
// replace all parameters with {}
439+
// This regexp will also work with "{controller:[a-zA-Z][a-zA-Z0-9_-]{1,}}"
440+
$routePath = preg_replace('~{(?:[^{}]*|(?R))*}~', '{}', $route->path);
441+
$node = &$treeRoutes;
442+
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['{}'];
446+
$node[spl_object_hash($route)] = $route;
447+
}
448+
continue;
449+
}
450+
$node = &$node[$segment];
451+
}
452+
453+
$node[spl_object_hash($route)] = $route;
454+
unset($node);
455+
}
456+
457+
return $treeRoutes;
458+
}
423459
}

src/Matcher.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,12 @@ public function match(ServerRequestInterface $request)
113113
$this->failedScore = 0;
114114
$path = $request->getUri()->getPath();
115115

116-
foreach ($this->map as $name => $proto) {
117-
$route = $this->requestRoute($request, $proto, $name, $path);
116+
$possibleRoutes = $this->getMatchedTree($path);
117+
foreach ($possibleRoutes as $proto) {
118+
if (is_array($proto)) {
119+
continue;
120+
}
121+
$route = $this->requestRoute($request, $proto, $path);
118122
if ($route) {
119123
return $route;
120124
}
@@ -131,20 +135,18 @@ public function match(ServerRequestInterface $request)
131135
*
132136
* @param Route $proto The proto-route to match against.
133137
*
134-
* @param string $name The route name.
135-
*
136138
* @param string $path The request path.
137139
*
138140
* @return mixed False on failure, or a Route on match.
139141
*
140142
*/
141-
protected function requestRoute($request, $proto, $name, $path)
143+
protected function requestRoute($request, $proto, $path)
142144
{
143145
if (! $proto->isRoutable) {
144-
return;
146+
return false;
145147
}
146148
$route = clone $proto;
147-
return $this->applyRules($request, $route, $name, $path);
149+
return $this->applyRules($request, $route, $route->name, $path);
148150
}
149151

150152
/**
@@ -261,4 +263,27 @@ public function getMatchedRoute()
261263
{
262264
return $this->matchedRoute;
263265
}
266+
267+
/**
268+
* Split the URL into URL Segments and check for matching routes per segment
269+
* This segment could return a list of possible routes
270+
*
271+
* @param string $path
272+
* @return \RecursiveArrayIterator
273+
*/
274+
private function getMatchedTree($path)
275+
{
276+
$node = $this->map->getAsTreeRouteNode();
277+
foreach (explode('/', trim($path, '/')) as $segment) {
278+
if (isset($node[$segment])) {
279+
$node = $node[$segment];
280+
continue;
281+
}
282+
if (isset($node['{}'])) {
283+
$node = $node['{}'];
284+
}
285+
}
286+
287+
return new \RecursiveArrayIterator($node);
288+
}
264289
}

tests/MatcherTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ public function testLogger()
258258
$matcher->match($request);
259259

260260
$expect = [
261-
'debug: /bar FAILED Aura\Router\Rule\Path ON foo',
262261
'debug: /bar MATCHED ON bar',
263262
];
264263
$actual = $logger->lines;

0 commit comments

Comments
 (0)