From 0ee598fbddbf502a88fbe1feb28c7076faa11988 Mon Sep 17 00:00:00 2001 From: WOBBLEFANG THE THIRD Date: Wed, 22 Apr 2026 08:47:12 +0200 Subject: [PATCH] Implement dependency injection and middleware support in the router --- config/routes.php | 2 +- public/index.php | 12 ++++-- src/Container.php | 50 ++++++++++++++++++++++ src/Middleware/MiddlewareInterface.php | 7 ++++ src/Middleware/TestMiddleware.php | 11 +++++ src/Router.php | 58 ++++++++++++++++++++++---- 6 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 src/Container.php create mode 100644 src/Middleware/MiddlewareInterface.php create mode 100644 src/Middleware/TestMiddleware.php diff --git a/config/routes.php b/config/routes.php index 8026f7b..45f2df3 100644 --- a/config/routes.php +++ b/config/routes.php @@ -1,4 +1,4 @@ get('/', [new HomeController(), 'index']); \ No newline at end of file +$router->get('/', [HomeController::class, 'index']); \ No newline at end of file diff --git a/public/index.php b/public/index.php index 6d72079..52294be 100644 --- a/public/index.php +++ b/public/index.php @@ -1,13 +1,19 @@ addNamespace('src', __DIR__ . '/../src'); $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'; $router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); \ No newline at end of file diff --git a/src/Container.php b/src/Container.php new file mode 100644 index 0000000..0e7a4bd --- /dev/null +++ b/src/Container.php @@ -0,0 +1,50 @@ +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); + } +} \ No newline at end of file diff --git a/src/Middleware/MiddlewareInterface.php b/src/Middleware/MiddlewareInterface.php new file mode 100644 index 0000000..a16e349 --- /dev/null +++ b/src/Middleware/MiddlewareInterface.php @@ -0,0 +1,7 @@ +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 { - $path = parse_url($uri, PHP_URL_PATH) ?? '/'; - $handler = $this->routes[$method][$path] ?? null; + $route = $this->routes[$method][$uri] ?? null; - if ($handler === null) { + if ($route === null) { http_response_code(404); - echo '404 Not Found'; + require __DIR__ . '/../views/404.php'; 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(); } } \ No newline at end of file