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 // Set defaultCallable if no value provided
if (is_null($routeConfig)) if (is_null($routeConfig))
@ -165,23 +172,27 @@ class Router
// Convert wildcards to Regex // Convert wildcards to Regex
$route = str_replace([':any',':num'], ['[^/]+', '[0-9]+'], $route); $route = str_replace([':any',':num'], ['[^/]+', '[0-9]+'], $route);
if ($prepend) if (!isset($this->routes[$priority]))
$this->routes = [$route => $routeConfig] + $this->routes; $this->routes[$priority] = [];
else
$this->routes[$route] = $routeConfig;
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. * Removes a route from the array based on the given route.
* *
* @param $route string The route to remove * @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); Logger::log('Route removed: '.$route);
} }
@ -194,54 +205,60 @@ class Router
*/ */
public function route(string $path) public function route(string $path)
{ {
// Check all the provided custom paths // Check all the provided custom paths, ordered by priority
foreach ($this->routes as $route => $routeConfig) for ($i=Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++) {
{ if (!isset($this->routes[$i]))
// Match the path against the routes
if (!preg_match('#^'.$route.'$#', $path, $matches))
continue; continue;
// Save the matches foreach ($this->routes[$i] as $route => $routeConfig)
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))
{ {
// Replace defaultCallable if a custom callable is provided // Match the path against the routes
// This is an example of 'Custom Callable' if (!preg_match('#^'.$route.'$#', $path, $matches))
// e.g: '.*$' => ['callable' => [$object, 'method']] continue;
if (isset($routeConfig['callable']) && is_callable($routeConfig['callable']))
$this->callable = $routeConfig['callable'];
// If the route provides a configuration, use that // Save the matches
// This is an example of 'Static Rewrite' Logger::log("Route matched: '" . $route . "' with " . Priority::getPriority($i));
// e.g: '.*$' => ['viewName' => 'custom', 'viewType' => 'cli', 'function' => 'index'] $this->matches = $matches;
else $this->route = $route;
$this->matches = array_merge($this->matches, $routeConfig); $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."); 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. * Returns an array with all the routes.
* *
* @param int $priority
* @return array * @return array
* @codeCoverageIgnore * @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 () { $testAppendRouteFunction = [function () {
}]; }];
$this->router->addRoute('testRoute', $testRouteFunction); $this->router->addRoute('testRoute', $testRouteFunction);
$this->router->addRoute('testAppendRoute', $testAppendRouteFunction, false); $this->router->addRoute('testAppendRoute', $testAppendRouteFunction, Priority::LOW);
// Test if the order is correct // Test if the order is correct
$this->assertSame( // First for Priority::NORMAL
['testRoute' => $testRouteFunction, 'testAppendRoute' => $testAppendRouteFunction], $this->assertSame(['testRoute' => $testRouteFunction], $this->router->getRoutes(Priority::NORMAL));
$this->router->getRoutes()
);
// Test if the order is not incorrect // Then for Priority::LOW
$this->assertNotSame( $this->assertSame(['testAppendRoute' => $testAppendRouteFunction], $this->router->getRoutes(Priority::LOW));
['testAppendRoute' => $testAppendRouteFunction, 'testRoute' => $testRouteFunction],
$this->router->getRoutes()
);
} }
/** /**