10

I'm trying to get my head around Dependency Injection and I understand it, for the most part.

However, say if, for some reason, one of my classes was dependent on several classes, instead of passing all of these to this one class in the constructor, is there a better, more sensible method?

I've heard about DI Containers, is this how I would go about solving this problem? Where should I start with this solution? Do I pass the dependencies to my DIC, and then pass this to the class that needs these dependencies?

Any help that would point me in the right direction would be fantastic.

1
  • 21
    "Dependency Injection" is a 25-dollar term for a 5-cent concept -- James Shore Commented Apr 8, 2012 at 18:03

4 Answers 4

32

Dependency Injection !== DIC

People should really stop confusing them. Dependency Injection is an idea that comes from Dependency Inversion principle.

The DIC is a "magic cure" that promises to let you use dependency injection, but in PHP is usually implemented by breaking every other principle of object oriented programming. The worst implementations tend to also attach it all to global state, via static Registry or Singleton.

Anyway, if your class depends on too many other classes, then in general, it signifies a design flaw in the class itself. You basically have a class with too many reasons to change, thus, breaking the Single Responsibility principle.

In this case, then dependency injection container will only hide the underlying design issues.

If you want to learn more about Dependency Injection, I would recommend you to watch the "Clean Code Talks" on Youtube.

Sign up to request clarification or add additional context in comments.

1 Comment

dependency injection is a way of implementing Ioc correct?
11

If you have several dependencies to deal with, then yes a DI container can be the solution.

The DI container can be an object or array constructed of the various dependent object you need, which gets passed to the constructor and unpacked.

Suppose you needed a config object, a database connection, and a client info object passed to each of your classes. You can create an array which holds them:

// Assume each is created or accessed as a singleton, however needed...
// This may be created globally at the top of your script, and passed into each newly
// instantiated class
$di_container = array(
  'config' = new Config(),
  'db' = new DB($user, $pass, $db, $whatever),
  'client' = new ClientInfo($clientid)
);

And your class constructors accept the DI container as a parameter:

class SomeClass {
  private $config;
  private $db;
  private $client;
 
  public function __construct(&$di_container) {
    $this->config = $di_container['config'];
    $this->db = $di_container['db'];
    $this->client = $di_container['client'];
  }
}

Instead of an array as I did above (which is simple), you might also create the DI container as an class itself and instantiate it with the component classes injected into it individually. One benefit to using an object instead of an array is that by default it will be passed by reference into the classes using it, while an array is passed by value (though objects inside the array are still references).

Edit

There are some ways in which an object is more flexible than an array, although more complicated to code initially.

The container object may also create/instantiate the contained classes in its constructor as well (rather than creating them outside and passing them in). This can save you some coding on each script that uses it, as you only need to instantiate one object (which itself instantiates several others).

Class DIContainer {
  public $config;
  public $db;
  public $client;

  // The DI container can build its own member objects
  public function __construct($params....) {
    $this->config = new Config();

    // These vars might be passed in the constructor, or could be constants, or something else
    $this->db = new DB($user, $pass, $db, $whatever);

    // Same here -  the var may come from the constructor, $_SESSION, or somewhere else
    $this->client = new ClientInfo($clientid);
  }
}

9 Comments

@Azirius I added an example above where the object is more flexible.
That is quite a horrid implementation of DIC. Also , what's the point in passing array as reference , when you are not writing to it?
Would you really write to a referenced array in the constructor ?! Thats insane. Or is it just because you don't understand copy-on-write in php5 ..
NO , you shouldn't. Especially not something as horrible as your Diesel.
This is the service locator anti-pattern. Google it.
|
0

I've wrote an article about this problem. The idea is using a combination of abstract factory and dependency injection to achieve transparent dependency resolving of (possible nested) dependencies. I will copy/paste here the main code snippets.

namespace Gica\Interfaces\Dependency;
    
interface AbstractFactory
{
    public function createObject($objectClass, $constructorArguments = []);
}

The abstract factory implementation is the following.

namespace Gica\Dependency;
 
class AbstractFactory implements \Gica\Interfaces\Dependency\AbstractFactory, \Gica\Interfaces\Dependency\WithDependencyInjector
{
    use WithDependencyInjector;
   
    /**
     * @param string $objectClass
     * @param array $constructorArguments
     * @return object instanceof $class
     */
    public function createObject($objectClass, $constructorArguments =     [])
    {
        $instance = new $objectClass(...$constructorArguments);
  
        $this->getDependencyInjector()->resolveDependencies($instance);
    
        return $instance;
    }
}

The dependency injector is this.

namespace Gica\Dependency;

class DependencyInjector implements \Gica\Interfaces\Dependency\DependencyInjector
{
    use \Gica\Traits\WithDependencyContainer;
    
    public function resolveDependencies($instance)
    {
        $sm = $this->getDependencyInjectionContainer();
    
        if ($instance instanceof \Gica\Interfaces\WithAuthenticator) {
            $instance->setAuthenticator($sm->get(\Gica\Interfaces\Authentication\Authenticator::class));
        }
        if ($instance instanceof \Gica\Interfaces\WithPdo) {
            $instance->setPdo($sm->get(\Gica\SqlQuery\Connection::class));
        }
        
        if ($instance instanceof \Gica\Interfaces\Dependency\WithAbstractFactory) {
            $instance->setAbstractFactory($sm->get(\Gica\Interfaces\Dependency\AbstractFactory::class));
        }
        // All the dependency declaring interfaces go here.
    }
}

The dependency container is the standard one.

The client code could look like this.

$abstractFactory = $container->get(\Gica\Interfaces\Dependency\AbstractFactory::class);
    
$someHelper = $abstractFactory->createObject(\Web\Helper\SomeHelper::class);
    
echo $someHelper->helpAction();

Notice that dependencies are hidden, and we can focus on the main business. My client code doesn't care or know that $someHelper need an Authenticator or that helpAction() need an SomeObject to do its work.

In the background, a lot of things happens, a lot of dependencies are detected, resolved, and injected. Notice that I don't use the new operator to create $someObject. The responsibility of actual creation of the object is passed to the AbstractFactory.

Comments

-6

I recommend you to use Singltones or Mutlitones. In these cases you will be always able to get objects via static class' methods.

The other way (couldn't find a correct pattern name, but it could be Registry) is to use one global static object to store multiple objects' instances. E.g. (simplified code, without any checks):

class Registry {
    private static $instances = array();

    public static function add($k, $v) {
        $this->instances[$k] = $v;
    }

    public static function get($k) {
        return $this->instances[$k];
    }
}

class MyClass {
    public function __construct() {
        Registry::add('myclass', $this);
    }
}

4 Comments

I have considered Singletons, however, I've read that DI is the way forward.
DI is a good solution however, but IMO it has much more better realization in Java (i.e. Automatically injected dependency), not in PHP itself
@RepWhoringPeeHaa Explain your opinion.
@Botanick I believe I already did. Globals are bad and what you have in your code are fancy globals. I suggest you watch the videos in teresko's answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.