<?php
class Router {
protected $conn;
protected $currentController = "";
protected $currentMethod = "";
protected array $params = [];
public function __construct(\PDO $conn){
$this->conn = $conn;
}
public function dispatch(){
$url = $this->getUrl();
if(isset($url[4])){
switch($url[4]){
case "events":
$this->currentController = new App\Controllers\EventController($this->conn);
$this->currentMethod = "getEvents";
break;
case "event":
$this->params = array($url[5]);
$this->currentController = new App\Controllers\EventController($this->conn);
$this->currentMethod = "getEvent";
break;
}
}
call_user_func_array([$this->currentController, $this->currentMethod], $this->params);
$this->currentController->display();
}
public function getUrl(){
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$url = explode("/", $uri);
return $url;
}
}
?>
$url[5] is supposed to be a numeric id for selecting an event or a string namely "?id=1" how can I pass this value to call_user_func_array I've seen doing it with preg_match expressions unfortunately I didn't understand how it works
CodePudding user response:
Maybe you could try out this router. This is just a SIMPLIFICATION in order to understand how a router MIGHT look like. It is much better to install existing ones over packagist with composer. for example nikic/fast-route is a good option.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 'On');
final class Router
{
/**
* This is a list of all routes with the regular expression in the key
* the result loooks like following
* (POST|GET)_/testMultiple/(\d )/(\S )
* @var array<string, callable>
*/
private array $routes = [];
/**
* Collect all routes
* @param string $path
* @param callable $action
* @param string $methods
* @return void
*/
public function any(string $path, callable $action, string $methods = 'POST|GET'): void
{
//Skip router injections
if (strpos($path, '..') !== false) {
return;
}
$this->routes['(' . $methods . ')_' . $path] = $action;
}
/**
* Create a regular expresion based on parameter and search for route
* @param string $path
* @return string
* @throws Exception
*/
public function callRoute(string $path): string
{
/**
* $path might look like GET_/testMultiple/123/test
*/
$path = $_SERVER['REQUEST_METHOD'] . '_' . parse_url($path,PHP_URL_PATH);
/**
* Iterate over all routes and cehck if "GET_/testMultiple/123/test" can be found in an routes
*/
foreach ($this->routes as $route => $action) {
$regEx = "~^$route/?$~i";
$matches = [];
if (!preg_match($regEx, $path, $matches)) {
continue;
}
//If we found the route we need to remove first 2 values from matched result so that only parameters from URL are inside $matched array
array_shift($matches);
array_shift($matches);
$arguments = $matches;
return call_user_func_array($action,$arguments);
}
throw new Exception(sprintf('Route %s not found', $path));
}
}
final class TestController
{
public function indexAction(): string
{
return 'Hello world';
}
public function testPost(): string
{
return 'Hello this is post only';
}
public function testAction(string $parameter1): string
{
return 'Hello ' . $parameter1;
}
public function testOptionalAction(?string $parameter1 = null): string
{
return 'Hello ' . $parameter1;
}
public function testMultiple(int $parameter1, string $parameter2): string
{
return 'The first value is ' . $parameter1 . ' and the second is ' . $parameter2;
}
}
$services = [];
$services[TestController::class] = function () use ($services) {
return new TestController();
};
$router = new Router();
$router->any('/', [$services[TestController::class](), 'indexAction']);
$router->any('/test/(\S )', [$services[TestController::class](), 'testAction']);
$router->any('/testOptional/?(\S )?', [$services[TestController::class](), 'testOptionalAction']);
$router->any('/testMultiple/(\d )/(\S )', [$services[TestController::class](), 'testMultiple']);
$router->any('/testJustPost', [$services[TestController::class](), 'testPost'],'POST');
echo $router->callRoute($_SERVER['REQUEST_URI']);
Hope that gives you some ideas for your router