Implement dependency injection and middleware support in the router
This commit is contained in:
+1
-1
@@ -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
@@ -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']);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace src\Middleware;
|
||||||
|
|
||||||
|
interface MiddlewareInterface
|
||||||
|
{
|
||||||
|
public function handle(callable $next): void;
|
||||||
|
}
|
||||||
@@ -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
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user