I'm trying to implement a router in my application using DI container.
So I have an Application class which loads a config and has a DI container instance. Application has Router as a service class instaniated by DI. Router has an array of Route classes, Route has Action and Action has Target. Target is Closure or controller class name + controller method name. I want to instaniate controller by DI Container because I want to inject some services.
Here is example structure.
class Application { private $container; private $router; public function __construct() { $this->container = new DIContainer(); $this->container->add(SomeService::class, function(){ return new SomeService($someConfigParams); }) $this->router = $this->container->get(Router::class); } public function handle($uri){ return $this->router->handle($uri); } } class Router { private $container; private $routes; //Array of Route class filled by addRoute public function __construct (Container $container){ $this->container = $container; } public function addRoute($uri){ $routes[] = $this->container->make(Route::class, ['uri' => $uri]); } public function handle ($uri){ $route = $this->findRoute($uri)->getAction()->run() } } class Route { private $container; private $action; //instance of Action class filled by setAction public function __construct(Container $container){ $this->container = $container; } public function setAction($params){ $this->action = $this->container->make(Action::class, $params); } public function getAction(){ return $this->action(); } } class Action { private $container; private $target; //Instance of Target class filled by setTarget public function __construct(Container $container){ $this->container = $container; } public function setTarget(){ $this->target = $this->container->make(Targer::class) } public function run(){ return $this->target->run(); } } class Target { private $container; private $controllerClass; private $controllerMethod; public function __construct(Container $container, $class, $method){ $this->container = $container; $this->controllerClass = $class; $this->controllerMethod = $method; } public function run(){ return $this->container->make($this->controllerClass)->{$this->controllerMethod}(); } } //And finally! class Controller { private $someService; public function __construct(SomeService $someService) { $this->someService = $someService; } public function test(){ return $this->someService->sayHello(); } } new Application()->handle('/controller/test/');
Excuse me for such large code example. But I really wonder is it OK that all the classes in chain depend on $container only because controller needs a service. Or should I just make my Container class global (e.g. singleton) and call it at the last step? Like this:
... // Target class public function run() { return Container::getInstance()->make($this->controllerClass)->{$this->controllerMethod}() } ...