First of all you have to define your routes a little more detailed. Otherwise, it is not clear how your placeholders in the curly brackets should be used. Of course you as a programmer know that there should be a numerical value for an ID. Your validator may not know this.
So let us have a look, how you can define your routes a little bit more detailed.
$routes = [
[
'controller' => SomeController::class,
'route' => '/order/{id}',
'parameters' => [ 'id' => '([0-9a-z]+)' ],
'allowed_methods' => [ 'GET' ],
]
];
This is just an example entry for a route. A route contains the controller, which has to be called, when this route is requested. Also the route itself is mentioned here. Beside that we define a parameter called id which acts as a placeholder in your route and we define the allowed request methods. In this case the route should only be accessible via GET requests. In our small example here, we just need the parameters and the route. The below shown router class does not recognize the request method and the controller.
So how a route can be resolved and validated? All we have to know is now defined in the route. When a request happens, we can check against the route.
Here 's a small router class example. Ofcourse this small example should not be used for production.
declare(strict_types=1);
namespace Marcel\Router;
class Router
{
protected array $routes = [];
protected array $filters = [];
public function __construct(array $routes)
{
$this->routes = $routes;
}
public function match(string $request) : bool
{
foreach ($this->routes as $route) {
// find parameters and filters (regex) in route
if (preg_match_all('/\{([\w\-%]+)\}/', $route['route'], $keys)) {
$keys = $keys[1];
}
foreach ($keys as $key => $name) {
if (isset($route['parameters'][$name])) {
$this->filters[$name] = $route['parameters'][$name];
}
}
// match requested route against defined route
$regex = preg_replace_callback('/\{(\w+)\}/', [ $this, 'substituteFilter' ], $route['route']);
$filter = rtrim($regex, '/');
$pattern = '@^' . $filter . '/?$@i';
// if not matching, check the next route
if (!preg_match($pattern, $request, $matches)) {
continue;
}
return true;
}
return false;
}
protected function substituteFilter(array $matches): string
{
if (isset($matches[1], $this->filters[$matches[1]])) {
return $this->filters[$matches[1]];
}
return '([\w\-%]+)';
}
}
This small router class example tests the given urls against the collection of routes. The class pays attention to the placeholders that can be filled with a regular expression. So the class checks every request against the defined regex for the given placeholder.
So let us test this little class against some requests
$router = new Router($routes);
$result = $router->match('/order/BPQ123');
var_dump($result); // true
$result = $router->match('/bla/yadda/blubb');
var_dump($result); // false