phpoopdependency-injectionphp-di

How to get instance of PHP-DI container from any class I want?


I am using PHP-DI 6 Container in my PHP Project. At the very begging of my program I just initialize the container and get Application class with all dependencies injected.

    $container   = new Container();
    $application = $container->get(Application::class);

    $application->initialize();
    $application->run();

On the image below you can see classes that I use in my project.

class diagram

Asterisk Dispatcher gets injected into Application class.

private $asteriskDispatcher;

public function __construct(AsteriskDispatcher $asteriskDispatcher)
{
    $this->asteriskDispatcher = $asteriskDispatcher;
}

Then, inside AsteriskDispatcher class I need to create a list of Asterisk Manager instances, which is going to contain some dependencies as well in near future.

I don't want to inherit container through the all classes. Is there a way to initialize PHP-DI container as singleton, so I can use it any moment I want to create some objects?

This is how I am doing this right now, I just create a new instance of PHP-DI container inside my AsteriskDispatcher class and this looks so damn awful.

class AsteriskDispatcher implements AsteriskDispatcherInterface
{
    private $listOfAsteriskManagers;

    public function __construct()
    {
        $configurations = AsteriskConnectionsConfiguration::$connectionsConfiguration;

        $this->listOfAsteriskManagers = new \SplDoublyLinkedList();

        $container = new Container();

        foreach ($configurations as $configuration)
        {
            $this->listOfAsteriskManagers->push($container->make(AsteriskManager::class,
                array('configuration' => $configuration)));
        }
    }
}

I really want to understand how can I use PHP-DI container without breaking SOLID principles.


Solution

  • From the documentation:

    If you need to use the make() method inside a service, or a controller, or whatever, it is recommended that you type-hint against FactoryInterface *. That avoids coupling your code to the container. DI\FactoryInterface is automatically bound to DI\Container so you can inject it without any configuration.

    *emphasis mine

    So you should change your AsteriskDispatcher constructor to be like:

    public function __construct(FactoryInterface $factory) {
      // your code ...
      // more of your code ...
    
      $factory->make(AsteriskManager::class, ['configuration' => $configuration]);
    
      // the rest of your code.
    
    }
    

    PS: Singletons are evil (mostly).