Implement dependency injection and middleware support in the router

This commit is contained in:
2026-04-22 08:47:12 +02:00
parent 702803d982
commit 0ee598fbdd
6 changed files with 127 additions and 13 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
<?php <?php
use src\Controller\HomeController; use src\Controller\HomeController;
$router->get('/', [new HomeController(), 'index']); $router->get('/', [HomeController::class, 'index']);
+9 -3
View File
@@ -1,13 +1,19 @@
<?php <?php
require_once __DIR__ . '/../src/Autoloader.php'; require_once __DIR__ . '/../src/Autoloader.php';
use src\Router;
$autoloader = new Autoloader(); $autoloader = new Autoloader();
$autoloader->addNamespace('src', __DIR__ . '/../src'); $autoloader->addNamespace('src', __DIR__ . '/../src');
$autoloader->register(); $autoloader->register();
$router = new Router(); use src\Container;
use src\Router;
use src\Middleware\TestMiddleware;
$container = new Container();
$router = new Router($container);
$router->addGlobalMiddleware(TestMiddleware::class);
require_once __DIR__ . '/../config/routes.php'; require_once __DIR__ . '/../config/routes.php';
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); $router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace src;
class Container
{
private array $bindings = [];
public function bind(string $abstract, callable $factory): void
{
$this->bindings[$abstract] = $factory;
}
public function make(string $class): object
{
if (isset($this->bindings[$class])) {
return ($this->bindings[$class])($this);
}
return $this->resolve($class);
}
private function resolve(string $class): object
{
$reflector = new \ReflectionClass($class);
$constructor = $reflector->getConstructor();
if ($constructor === null) {
return new $class();
}
$dependencies = array_map(function($param) {
$type = $param->getType();
if ($type === null) {
throw new \Exception("Cannot resolve parameter \${$param->getName()} — no type hint.");
}
if ($type->isBuiltin()) {
if ($param->isDefaultValueAvailable()) {
return $param->getDefaultValue();
}
throw new \Exception("Cannot resolve primitive parameter \${$param->getName()} — no default value.");
}
return $this->make($type->getName());
}, $constructor->getParameters());
return $reflector->newInstanceArgs($dependencies);
}
}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace src\Middleware;
interface MiddlewareInterface
{
public function handle(callable $next): void;
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace src\Middleware;
class TestMiddleware implements MiddlewareInterface
{
public function handle(callable $next): void
{
error_log('TestMiddleware: Handling request...');
$next();
}
}
+49 -9
View File
@@ -4,28 +4,68 @@ namespace src;
class Router class Router
{ {
private array $routes = []; private array $routes = [];
private array $globalMiddleware = [];
private Container $container;
public function get(string $path, callable $handler): void public function __construct(Container $container)
{ {
$this->routes['GET'][$path] = $handler; $this->container = $container;
} }
public function post(string $path, callable $handler): void public function get(string $path, array $handler, array $middleware = []): void
{ {
$this->routes['POST'][$path] = $handler; $this->routes['GET'][$path] = compact('handler', 'middleware');
}
public function post(string $path, array $handler, array $middleware = []): void
{
$this->routes['POST'][$path] = compact('handler', 'middleware');
}
public function addGlobalMiddleware(string $middleware): void
{
$this->globalMiddleware[] = $middleware;
} }
public function dispatch(string $method, string $uri): void public function dispatch(string $method, string $uri): void
{ {
$path = parse_url($uri, PHP_URL_PATH) ?? '/'; $route = $this->routes[$method][$uri] ?? null;
$handler = $this->routes[$method][$path] ?? null;
if ($handler === null) { if ($route === null) {
http_response_code(404); http_response_code(404);
echo '404 Not Found'; require __DIR__ . '/../views/404.php';
return; return;
} }
call_user_func($handler); [$controllerClass, $action] = $route['handler'];
$final = function() use ($controllerClass, $action) {
$controller = $this->container->make($controllerClass);
$controller->$action();
};
$middleware = array_merge($this->globalMiddleware, $route['middleware']);
$this->run($middleware, $final);
}
private function run(array $middleware, callable $final): void
{
if (empty($middleware)) {
$final();
return;
}
$chain = array_reduce(
array_reverse($middleware),
function(callable $carry, string $middlewareClass) {
return function() use ($carry, $middlewareClass) {
$instance = $this->container->make($middlewareClass);
$instance->handle($carry);
};
},
$final
);
$chain();
} }
} }