What is Dependency Injection?

Fabien Potencier

March 26, 2009

This article is part of a series on Dependency Injection in general and on a lightweight implementation of a Container in PHP in particular:

This article is the first of a series on Dependency Injection in general and the implementation of a Dependency Injection Container in PHP.

Today, I won't talk about the container yet as I first want to introduce the concept of Dependency Injection with some concrete examples that will hopefully demonstrate the problems it tries to solve and the benefits it gives to the developer. If you already knows the concept of Dependency Injection, you can safely skip this article and instead wait for the next one.

Dependency Injection is probably one of the most dead simple design pattern I know. And odds are you have probably already used Dependency Injection. But it is also one of the most difficult one to explain well. I think it is partly due to the nonsense examples used in most introductions to Dependency Injection. I have tried to come up with examples that fits the PHP world better. As PHP is a language mainly used for web development, let's take a simple Web example.

To overcome the statelessness of the HTTP protocol, web applications need a way to store user information between web requests. This is of course quite simple to achieve by using a cookie, or even better, by using the built-in PHP session mechanism:

$_SESSION['language'] = 'fr';
 

The above code stores the user language in the language session variable. So, for all subsequent requests of the same user, the language will be available in the global $_SESSION array:

$user_language = $_SESSION['language'];
 

As Dependency Injection only makes sense in an Object-Oriented world, let's pretend we have a SessionStorage class that wraps the PHP session mechanism:

class SessionStorage
{
  function __construct($cookieName = 'PHP_SESS_ID')
  {
    session_name($cookieName);
    session_start();
  }
 
  function set($key, $value)
  {
    $_SESSION[$key] = $value;
  }
 
  function get($key)
  {
    return $_SESSION[$key];
  }
 
  // ...
}
 

... and a User class that provides a nice high-level interface:

class User
{
  protected $storage;
 
  function __construct()
  {
    $this->storage = new SessionStorage();
  }
 
  function setLanguage($language)
  {
    $this->storage->set('language', $language);
  }
 
  function getLanguage()
  {
    return $this->storage->get('language');
  }
 
  // ...
}
 

Those classes are simple enough and using the User class is also rather easy:

$user = new User();
$user->setLanguage('fr');
$user_language = $user->getLanguage();
 

All is good and well... until you want more flexibility. What if you want to change the session cookie name for instance? Here are some random possibilities:

  • Hardcode the name in the User class in the SessionStorage constructor:

    class User
    {
      function __construct()
      {
        $this->storage = new SessionStorage('SESSION_ID');
      }
     
      // ...
    }
     
  • Define a constant outside of the User class:

    class User
    {
      function __construct()
      {
        $this->storage = new SessionStorage(STORAGE_SESSION_NAME);
      }
     
      // ...
    }
     
    define('STORAGE_SESSION_NAME', 'SESSION_ID');
     
  • Add the session name as a User constructor argument:

    class User
    {
      function __construct($sessionName)
      {
        $this->storage = new SessionStorage($sessionName);
      }
     
      // ...
    }
     
    $user = new User('SESSION_ID');
     
  • Add an array of options for the storage class:

    class User
    {
      function __construct($storageOptions)
      {
        $this->storage = new SessionStorage($storageOptions['session_name']);
      }
     
      // ...
    }
     
    $user = new User(array('session_name' => 'SESSION_ID'));
     

All these alternatives are quite bad. Hardcoding the session name in the User class does not really solve the problem as you cannot easily change your mind later on without changing the User class again. Using a constant is also a bad idea as the User class now depends on a constant to be set. Passing the session name as an argument or as an array of options is probably the best solution, but it still smells bad. It clutters the User constructor arguments with things that are not relevant to the object itself.

But there is yet another problem that cannot be solved easily: How can I change the SessionStorage class? For instance, to replace it with a mock object to ease testing. Or perhaps because you want to store the sessions in a database table or in memory. That's impossible with the current implementation, except if you change the User class.

Enter Dependency Injection. Instead of creating the SessionStorage object inside the User class, let's inject the SessionStorage object in the User object by passing it as a constructor argument:

class User
{
  function __construct($storage)
  {
    $this->storage = $storage;
  }
 
  // ...
}
 

That's Dependency Injection. Nothing more! Using the User class is now a bit more involving as you first need to create the SessionStorage object:

$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
 

Now, configuring the session storage object is dead simple, and replacing the session storage class is also very easy. And everything is possible without changing the User class thanks to the better separation of concerns.

The Pico Container website describes Dependency Injection like this:

"Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields."

As any other design pattern, Dependency Injection also has some anti-patterns. The Pico Container website describes some of them.

Dependency Injection is not restricted to constructor injection:

  • Constructor Injection:

    class User
    {
      function __construct($storage)
      {
        $this->storage = $storage;
      }
     
      // ...
    }
     
  • Setter Injection:

    class User
    {
      function setSessionStorage($storage)
      {
        $this->storage = $storage;
      }
     
      // ...
    }
     
  • Property Injection:

    class User
    {
      public $sessionStorage;
    }
     
    $user->sessionStorage = $storage;
     

As a rule of thumb, constructor injection is best for required dependencies, like in our example, and setter injection is best for optional dependencies, like a cache object for instance.

Nowadays, most modern PHP frameworks heavily use Dependency Injection to provide a set of decoupled but cohesive components:

// symfony: A constructor injection example
$dispatcher = new sfEventDispatcher();
$storage = new sfMySQLSessionStorage(array('database' => 'session', 'db_table' => 'session'));
$user = new sfUser($dispatcher, $storage, array('default_culture' => 'en'));
 
// Zend Framework: A setter injection example
$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
  'auth'     => 'login',
  'username' => 'foo',
  'password' => 'bar',
  'ssl'      => 'ssl',
  'port'     => 465,
));
 
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);
 

If you are interested in learning more about Dependency Injection, I highly recommend you read the Martin Fowler introduction or the excellent Jeff More presentation. You can also have a look at a presentation I gave last year on Dependency Injection, where I talk in more detail on the example I have talked about in this article.

That's all for today. I hope you now have a better understanding of the Dependency Injection concept. In the next installment of this series, I will talk about Dependency Injection Containers.

Discussion

gravatar Ryan Weaver  — March 26, 2009 14:23   #1
Perfect- thanks for the simple introduction - I got lost going through your presentation on Symfony 2 where you mentioned this concept. Now the concept makes sense.

Always looking forward to reading more...
gravatar Scott Meves  — March 26, 2009 15:06   #2
Thanks for this article. It was a simple and clear explanation. I look forward to more of your posts!
gravatar denderello  — March 26, 2009 15:12   #3
Really nice read! Made it easy to understand to concept and to fill some gaps after viewing your slides.
gravatar Tom  — March 26, 2009 15:46   #4
Great simple introduction. I allways get lost when I hear pattern. You're explantion is right on the money
gravatar Malaney J. Hill  — March 26, 2009 17:20   #5
Excellent article! I especially liked the time you spent to build the case for Dependency Injection. Also, making the distinction between constructor, setter and property injection was enlightening as well. Keep up the good work!
gravatar fqqdk  — March 26, 2009 18:07   #6
I would recommend the articles about dependency injection over at google testing blog (http://googletesting.blogspot.com/), and this link in particular: http://googletesting.blogspot.com/2008/11/clean-code-talks-dependency-injection.html
gravatar opensas  — March 27, 2009 14:37   #7
Excellent article in deed!

the chosen php example is very good, it helps you understand what is dependency injection's real world use.

gravatar idont  — March 27, 2009 18:27   #8
Making a choice of how to inject the dependency is hard. You could also make your choice depending of the life cycle of the objects:

- if sessionStorage is created before and will "die" after user => constructor injection

- if sessionStorage is created after user => setter or attribute
gravatar kbsali  — March 27, 2009 18:58   #9
Nice one! Thanks.
and like "fqqdk" mentioned, Miško Hevery from google who likes to write about testable code wrote really nice posts about dependency injections too : http://misko.hevery.com/2009/02/19/constructor-injection-vs-setter-injection/ for instance.
gravatar Yuriy Voziy  — March 28, 2009 09:56   #10
I think that many programmers used this pattern without understanding this fact. That's why they are called simple patterns.

Nice explanation. Great examples.
gravatar Branko Ajzele  — March 28, 2009 10:45   #11
Great article, very educational. Thank you.
gravatar mbischof  — March 28, 2009 13:58   #12
As far as I know, this is called "Law of Demeter"
gravatar Chris Hartjes  — March 28, 2009 22:25   #13
Fabien, awesomely simple explanation of dependency injection! To me, it always sounded more complicated than it really is.

It also seems obvious now that the only way to really test loosely-coupled modules is to use this pattern as much as possible.
gravatar Oncle Tom  — March 30, 2009 00:50   #14
As you wrote, it's a dead simple example ... but it's perfect to point out the concept.

Indeed, we already did Dependency Injection without knowing it.
gravatar gentlemike2  — March 31, 2009 00:18   #15
Thanks for this article. I have just learned to write php using the object oriented methods, after writing procedural code for more than two years. I was wondering what was the best method to pass the attributes and methods of one object to another. This really helped!
gravatar Anna  — March 31, 2009 02:43   #16
Thanks. Very nicely explained. Great examples.
gravatar Justin  — April 01, 2009 16:53   #17
L O L - i was using it and i had no clue that it was called dependency injection :)))))


Tnks man
gravatar Don Bosco  — April 02, 2009 18:42   #18
Shouldnt you also be aware of call by value and call by reference when injecting objects?
gravatar Fabien  — April 02, 2009 18:46   #19
@Don Bosco: Objects are always passed by reference in PHP5.
gravatar Nicolas BUI  — April 06, 2009 00:49   #20
I completly agree that most articles about DI are difficult to understand due to nonsenses examples.
The most efficien way is to explain step by step how to do it. So it needs a lot of times to do so, that why only few articles can achieve this goal.

Actually, I've read a really good article (maybe the best I've ever seen about DI) that help me fully understand the use and why to use it can be found at dotnetstacker as a serie of 4 articles :

http://dotnetslackers.com/articles/designpatterns/InversionOfControlAndDependencyInjectionWithCastleWindsorContainerPart1.aspx

Enjoy !

gravatar Bogdan  — April 17, 2009 17:05   #21
Very nice example. Cheers!
gravatar Timothy  — April 17, 2009 18:31   #22
Pure excellence. Thank you, Fabien.
gravatar g-rant  — May 11, 2009 02:25   #23
"Passing the session name as an argument...It clutters the User constructor arguments with things that are not relevant to the object itself."

So how is passing an instance of the storage class not the EXACT same. I have a very nice set of classes that are not lending themselves to UT, and I find this DI stuff makes me sacrifice usability for testability. There has to be something better.
gravatar g-rant  — May 12, 2009 05:33   #24
Now that I have gotten to the container, I'm seeing how I could implement my classes keeping the convenience they give now, but allowing me to do the DI I need for UT.

Apologies...