Implemented changes requested by FuzeWorks\Application

- Added Priorities to the routes array. Routes can now be saved with priorities, making higher priority routes load before lower priorities
- Fixed bug where callable is not reset upon attempting routing a new route after another was not satisfied
This commit is contained in:
Abel Hoogeveen 2019-03-04 21:33:38 +01:00
parent be414aa2cd
commit f49c5dd882
No known key found for this signature in database
GPG Key ID: 96C2234920BF4292
2 changed files with 76 additions and 63 deletions

View File

@ -156,7 +156,14 @@ class Router
}
}
public function addRoute(string $route, $routeConfig = null, bool $prepend = true)
/**
* Add a route to the Router
*
* @param string $route
* @param null $routeConfig
* @param int $priority
*/
public function addRoute(string $route, $routeConfig = null, int $priority = Priority::NORMAL)
{
// Set defaultCallable if no value provided
if (is_null($routeConfig))
@ -165,23 +172,27 @@ class Router
// Convert wildcards to Regex
$route = str_replace([':any',':num'], ['[^/]+', '[0-9]+'], $route);
if ($prepend)
$this->routes = [$route => $routeConfig] + $this->routes;
else
$this->routes[$route] = $routeConfig;
if (!isset($this->routes[$priority]))
$this->routes[$priority] = [];
Logger::log('Route added at '.($prepend ? 'top' : 'bottom').': "'.$route.'"');
if (!isset($this->routes[$priority][$route]))
$this->routes[$priority][$route] = $routeConfig;
Logger::log('Route added with ' . Priority::getPriority($priority) . ": '" . $route."'");
}
/**
* Removes a route from the array based on the given route.
*
* @param $route string The route to remove
* @param int $priority
*/
public function removeRoute(string $route)
public function removeRoute(string $route, int $priority = Priority::NORMAL)
{
unset($this->routes[$route]);
if (!isset($this->routes[$priority][$route]))
return;
unset($this->routes[$priority][$route]);
Logger::log('Route removed: '.$route);
}
@ -194,54 +205,60 @@ class Router
*/
public function route(string $path)
{
// Check all the provided custom paths
foreach ($this->routes as $route => $routeConfig)
{
// Match the path against the routes
if (!preg_match('#^'.$route.'$#', $path, $matches))
// Check all the provided custom paths, ordered by priority
for ($i=Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++) {
if (!isset($this->routes[$i]))
continue;
// Save the matches
Logger::log('Route matched: '.$route);
$this->matches = $matches;
$this->route = $route;
// Call callable if routeConfig is callable, so routeConfig can be replaced
// This is an example of 'Dynamic Rewrite'
// e.g: '.*$' => callable
if (is_callable($routeConfig))
$routeConfig = call_user_func_array($routeConfig, [$matches]);
// If routeConfig is an array, multiple things might be at hand
if (is_array($routeConfig))
foreach ($this->routes[$i] as $route => $routeConfig)
{
// Replace defaultCallable if a custom callable is provided
// This is an example of 'Custom Callable'
// e.g: '.*$' => ['callable' => [$object, 'method']]
if (isset($routeConfig['callable']) && is_callable($routeConfig['callable']))
$this->callable = $routeConfig['callable'];
// Match the path against the routes
if (!preg_match('#^'.$route.'$#', $path, $matches))
continue;
// If the route provides a configuration, use that
// This is an example of 'Static Rewrite'
// e.g: '.*$' => ['viewName' => 'custom', 'viewType' => 'cli', 'function' => 'index']
else
$this->matches = array_merge($this->matches, $routeConfig);
// Save the matches
Logger::log("Route matched: '" . $route . "' with " . Priority::getPriority($i));
$this->matches = $matches;
$this->route = $route;
$this->callable = null;
// Call callable if routeConfig is callable, so routeConfig can be replaced
// This is an example of 'Dynamic Rewrite'
// e.g: '.*$' => callable
if (is_callable($routeConfig))
$routeConfig = call_user_func_array($routeConfig, [$matches]);
// If routeConfig is an array, multiple things might be at hand
if (is_array($routeConfig))
{
// Replace defaultCallable if a custom callable is provided
// This is an example of 'Custom Callable'
// e.g: '.*$' => ['callable' => [$object, 'method']]
if (isset($routeConfig['callable']) && is_callable($routeConfig['callable']))
$this->callable = $routeConfig['callable'];
// If the route provides a configuration, use that
// This is an example of 'Static Rewrite'
// e.g: '.*$' => ['viewName' => 'custom', 'viewType' => 'cli', 'function' => 'index']
else
$this->matches = array_merge($this->matches, $routeConfig);
}
// If no custom callable is provided, use default
// This is an example of 'Default Callable'
if (is_null($this->callable))
$this->callable = [$this, 'defaultCallable'];
// Attempt and load callable. If false, continue
$output = $this->loadCallable($this->callable, $this->matches, $route);
if (is_bool($output) && $output === FALSE)
{
Logger::log('Callable not satisfied, skipping to next callable');
continue;
}
return $output;
}
// If no custom callable is provided, use default
// This is an example of 'Default Callable'
if (is_null($this->callable))
$this->callable = [$this, 'defaultCallable'];
// Attempt and load callable. If false, continue
$output = $this->loadCallable($this->callable, $this->matches, $route);
if (is_bool($output) && $output === FALSE)
{
Logger::log('Callable not satisfied, skipping to next callable');
continue;
}
return $output;
}
throw new NotFoundException("Could not load view. Router could not find matching route with satisfied callable.");
@ -387,12 +404,13 @@ class Router
/**
* Returns an array with all the routes.
*
* @param int $priority
* @return array
* @codeCoverageIgnore
*/
public function getRoutes(): array
public function getRoutes(int $priority = Priority::NORMAL): array
{
return $this->routes;
return $this->routes[$priority];
}
/**

View File

@ -120,19 +120,14 @@ class RouterTest extends MVCRTestAbstract
$testAppendRouteFunction = [function () {
}];
$this->router->addRoute('testRoute', $testRouteFunction);
$this->router->addRoute('testAppendRoute', $testAppendRouteFunction, false);
$this->router->addRoute('testAppendRoute', $testAppendRouteFunction, Priority::LOW);
// Test if the order is correct
$this->assertSame(
['testRoute' => $testRouteFunction, 'testAppendRoute' => $testAppendRouteFunction],
$this->router->getRoutes()
);
// First for Priority::NORMAL
$this->assertSame(['testRoute' => $testRouteFunction], $this->router->getRoutes(Priority::NORMAL));
// Test if the order is not incorrect
$this->assertNotSame(
['testAppendRoute' => $testAppendRouteFunction, 'testRoute' => $testRouteFunction],
$this->router->getRoutes()
);
// Then for Priority::LOW
$this->assertSame(['testAppendRoute' => $testAppendRouteFunction], $this->router->getRoutes(Priority::LOW));
}
/**