Dependency Injection with Pimple

Tweet

In application development, we try to create independent modules so that we can reuse code in future projects. But, it’s difficult to create completely independent modules which provide useful functionality; their dependencies can cause maintenance nightmares unless they are managed properly. This is where Dependency Injection comes in handy, as it gives us the ability to inject the dependencies our code needs to function properly without hard coding them into the modules.

Pimple is a simple dependency injection container which takes advantage PHP’s closures to define dependencies in manageable way. In this article we’ll look at the problems with hard-coding dependencies, how dependency injection solves them, and how to use Pimple to keep code that takes advantage of dependency injection more maintainable.

The Issues with Concrete Dependencies

We use a number of PHP classes when writing applications. One class may need to call methods of one or more other classes to provide the intended functionality, and so we say the first class depends on the other classes. For example:

<?php
class A
{
    public function a1() {
        $b = new B();
        $b->b1();
    }
}

Class A depends on class B. If class B is not available then the above code will not work.

Moreover, every time we hard code the creation of an object inside a class, we are making a concrete dependency to that class. Concrete dependencies are a barrier to writing testable code. A better way is to provide an object of class B to class A. These objects can be provided through A’s constructor or a setter method.

Let’s take a look at a more realistic scenario before we go any further.

Sharing content on social networking websites is very common these days, and most sites display their social profile feeds right on their site itself. Suppose we have a class called SocialFeeds which generates feeds from social sites like Twitter, Facebook, Google+, etc. Separate classes are created to work with each of these services. Here we’ll look at the class that interfaces with Twitter, TwitterService.

The SocialFeeds class requests a Twitter feed using TwitterService. TwitterService interacts with the database to retrieve the specific user token to access the API. The token is passed to the OAuth class which retrieves the feeds using the provided token and returns it to the SocialFeeds class.

<?php
class SocialFeeds
{
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService->getTweets();
    }
}
<?php
class TwitterService
{
    public function getTweets() {
        $db = new DB();
        $query = "Query to get user token from database";
        $token = $db->getQueryResults($query);

        $oauth = new OAuth();
        return $oauth->requestTwitterFeed($token);
    }
}
<?php
class OAuth
{
    public function requestTwitterFeed($token) {
        // Retrieve and return twitter feed using the token    		
    }
}
<?php
class DB
{
    public function getQueryResults($query) {
        // Get results from database and return token
    }
}

It’s clear that SocialFeeds depends on TwitterService. But TwitterService depends on DB and OAuth, and so SocialFeeds depends on both DB and OAuth indirectly.

So what are the issues? SocialFeeds depends on concrete implementations of three classes, so it is impossible to test SocialFeeds as a separate unit without having real implementations of the other classes. Or, let’s say we want to use a different database or a different OAuth provider. In this case we would have to replace the existing classes with new class in each occurrence throughout our code.

Fixing Concrete Dependencies

The solution to these dependency issues is as simple as providing the objects dynamically when necessary without using concrete implementations. There are two types of techniques to inject dependencies: constructor-based dependency injection and setter-based injection.

Constructor-Based Injection

With constructor-based dependency injection, dependant objects are created externally and passed to the class’s constructor as parameters. We can assign these objects to class variables and use anywhere inside the class. Constructor-based injection for the SocialFeeds class looks like this:

<?php
class SocialFeeds
{
    public $twService;

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

    public function getSocialFeeds() {
        echo $this->twService->getTweets();
    }
}

An instance of TwitterService is passed as an object to the constructor. SocialFeeds still depends on TwitterService, but now we can have the freedom to provide different versions of the Twitter service provider or even a mock object for testing purposes. The DB and OAuth classes are defined similarly with regard to TwitterService.

<?php
$db = new DB();
$oauth = new OAuth();
$twService = new TwitterService($db, $oauth);
$socialFeeds = new SocialFeeds($twService);
$socialFeeds->getSocialFeeds();

Setter-Based Injection

With setter-based injection, objects are provided through setter methods instead of the constructor. Here’s a setter-based implementation of dependency injection for the SocialFeeds class:

<?php
class SocialFeeds
{
    public $twService;

    public function getSocialFeeds() {
        echo $this->twService->getTweets();
    }

    public function setTwitterService($twService) {
        $this->twService = $twService;
    }
}

The initialization code including DB and OAuth for now looks like this:

<?php
$db = new DB();
$oauth = new OAuth();
$twService = new TwitterService();
$twService->setDB($db);
$twService->setOAuth($oauth);

$socialFeeds = new SocialFeed();
$socialFeeds->setTwitterService($twService);
$socialFeeds->getSocialFeeds();

Constructor vs Setter Injection

It’s up to you to choose between constructor or setter-based injection. Constructor-based injections are suitable when all the dependencies are required in order to instantiate the class. Setter-based injections are suitable when dependencies are not required in each occasion.

Advantages

  • Constructor – all the dependencies of a class are identifiable simply by looking at the class’s constructor
  • Setter – adding a new dependency is as easy as adding a new setter method, which does not break existing code

Disadvantages

  • Constructor – adding a new dependency increases the constructor’s parameters; existing code needs to be updated throughout our application to provide the new dependency
  • Setter – we have to manually search for the necessary dependencies as they are not specified anywhere

With knowledge of dependency injection and various injection techniques, it’s time to look at Pimple and see how it fits in.

The Role of Pimple in DI

You might be wondering why Pimple is necessary when we can inject the dependencies already using the techniques previously mentioned. To answer this question, we need to look to the DRY principle.

Don’t Repeat Yourself (DRY) is a principle of software development aimed at reducing repetition of information of all kinds, especially useful in multi-tier architectures. The DRY principle is stated as “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system – Wikipedia

Consider the constructor-based injection example. Each time we want an object of the SocialFeed class, we have to repeat the whole setup process of instantiating and passing its dependencies. According to DRY, such code should be avoided to prevent maintenance headaches. Pimple acts as a container for defining such dependencies to avoid repetition.

Let’s look at a simple example to see how Pimple works.

<?php
$container = new Pimple();
$container['class_name'] = 'Test';
$container['object_name'] = function ($c) {
    return new $c['class_name']();
};

$testClass = $container['object_name'];

An instance of Pimple is created to act as the container for storing dependencies. It implements the SPL ArrayAccess interface so working with it is very similar to working with an array. First we’ve defined a key which holds the name of some arbitrary class we want. Then we’ve defined a closure to return the instance of the specified class which acts as a service. Note that $c will be passed an instance of the container, so we can reference other defined keys as we please; each defined parameter or object is available in the closure through the $c variable. Now whenever we want an instance of the class, we can reference the key to retrieve the object.

Let’s convert the SocialFeeds example to Pimple. The examples on the official Pimple site show constructor-based injection, so here we’ll illustrate setter-based injection. Keep in mind that any of the setter methods or code we defined in earlier do not need to be modified for us to use Pimple – we simply encapsulate the logic.

<?php
$container = new Pimple();
$container['oauth'] = function($c) {
	return new OAuth();
};
$container['db'] = function($c) {
	return new DB();
};
$container['tweet_service'] = function($c) {
	$twService = new TwitterService();
	$twService->setDB($c['db']);
	$twService->setOauth($c['oauth']);
	return $twService;
};
$container['social_feeds'] = function($c) {
	$socialFeeds = new SocialFeeds();
	$socialFeeds->setTwitterService($c['tweet_service']);
	return $socialFeeds;
};

$socialFeeds = $container['social_feeds'];
$socialFeeds->getSocialFeeds();

Both DB and OAuth classes are independent modules, so we directly return a new instance of them inside closures. Then we add dependencies to the TwitterService class using setter-based injections. We added DB and OAuth classes into the container already, so we can directly access them inside the function using $c['db'] and $c['oauth'].

Now the dependencies are encapsulated inside the container as services. Whenever we want to use different DB class or different OAuth service, we can just replace the class inside the container statement and everything will work perfectly. With Pimple you need to add new dependencies in just one place.

Advanced Pimple Usage

In the above scenario, Pimple will return new instances of each class from the closure whenever one is requested. There are certain scenarios where we need to use the same object without initializing new instances each time, for example connecting to database is a perfect example.

Pimple provides the ability to return the same instance using sharing objects, doing so requires us to specify the closure through the share() method as shown below:

<?php
$container['db'] = $container->share(function ($c) {
    return new DB();
});

Also, so far we’ve defined all our dependencies in a single location inside the Pimple container. But think of a situation where we need the services with its dependencies but configured in a slightly different way than the original. For example, let’s say we need to access an ORM for certain functionality of the TwitterService class. We can’t change the existing closure since it will force all of the existing functionality to use the ORM.

Pimple provides the method extend() to modify the existing closure dynamically without affecting the original implementation. Consider the following code:

<?php
$container['tweet_service'] = $container->extend('tweet_service', function($twSservice, $c) {
	$twService->setDB(new ORM());
	return $twService;
});

Now we’re able to use different extended versions of tweet_service in special scenarios. The first parameter is the name of the service, the second is a function that gets access to the object instance and the container.

Indeed, extend() is a powerful way of adding dependencies dynamically to suit different situations, but make sure to limit the extended versions of services to a minimum as it increases the amount of duplicate code.

Summary

Managing dependencies is one of the most essential and difficult tasks in web application development. We can use Dependency Injection using constructors and setter methods to manage them effectively. Dependency injection comes with its own hassles though, which Pimple solves by providing a lightweight container for creating and storing object dependencies in a DRY manner.

Feel free to share your experiences of managing dependencies in your projects and what you think about Pimple as a dependency injection container in the comments below.

Image via Fotolia

Get your free chapter of Level Up Your Web Apps with Go

Get a free chapter of Level Up Your Web Apps with Go, plus updates and exclusive offers from SitePoint.

  • Alexander Cogneau

    I don’t think that a discussion is necessary about Dependency Injection by using getter and setters vs. using constructor injection. When a dependency is really necessary, I put it in my constructor, that way I order myself and other developers to also define the dependency. I also however use getter and setter methods for changing dependencies at runtime, this would be a bit like the strategy pattern I suppose?

    • http://www.innovativephp.com Rakhitha Nimesh

      Hello Alexander
      Thanks for your suggestions. I just wanted to explain the difference between setter vs constructor injections. So according to what I understood, you pass the dependencies to the constructor. Then at a latter stage if a new dependency comes in, you use setter method too add it dynamically allowing both constructor for mandatory dependencies and setter for optional dependencies?

      Please correct me if I am wrong and explain you process so that readers can see different views from different developers.

      Thanks.

      • Armando

        I think that what Alexander means is that his constructor requires some initial objects, but then he has some setters to re-declare, when needed, those initial objects that the constructor used.

        Something like this:
        public function __construct($twService) {}
        public function setTwitterService($twService) {}

        • http://www.innovativephp.com Rakhitha Nimesh

          Hello Armando

          Thanks for the clarifications.
          Can you let us know why you prefer construtor based injection over setter based injection?

          Looking forward to your suggestions.

          • Alexander Cogneau

            Yes, indeed, while in PHP it is less necessary to alter dependencies at runtime, it could be on other applications. Take a server which uses a logger for instance, you could bind a command line action to a setter method and so you can change the logger dependency without having to restart your server.

            Nice article by the way!

          • http://www.innovativephp.com Rakhitha Nimesh

            Hi Alexander

            Thank you very much for the detailed explanations. Your example is way better than my example in understanding why setter injection is needed.

  • http://www.psinas.com Martin Psinas

    I’m really glad you included practical examples of dependency injection rather than focusing on Pimple entirely. Very well organized and written. Thank you.

    • http://www.innovativephp.com Rakhitha Nimesh

      Hello Martin

      Thank you very much for your interest in my article.
      I would like to know how I could have improved the article,contents or explanation process in your opinion.

      Also what are the topics do you like to seee more on phpmaster?

      Looking forward to your suggestions.

  • Boabramah Ernest

    I realise that my controllers needed the same dependencies which I have to pass to them. What if my controllers becomes more. I will be writing the same constructor signature for all of them. At the same time each controller requires it own unique dependencies. This is bad. My solution, I write an abstract class which the controllers extend move my setter injection methods into the abstract class. Now I write a factory class that will create my controllers. Here is my trick, I pass controllers unique dependencies through the constructor, the factory class just calls the DI for the controllers which is already filled with their unique dependencies, since all the controllers extend the abstract class, the factory class uses the setter method to inject dependencies which are common to all controllers. Now if the is a library My controllers will need, I just go to my factory class and add it. It Works. My point is just use the method that will solve the problem at that particular time.
    Good job mate. Keep the good work

    • http://www.innovativephp.com Rakhitha Nimesh

      Hello Boabramah
      Thank you very much for the detailed explanations. Its good to know how different developers do the same thing in different ways.

      It would have been much easier for readers to understand your process, if you included the necessary codes with the explanations.

      Thanks for sharing.

  • http://blog.mindplay.dk Rasmus Schultz

    Once again, I’m impressed and pleased that there is a website for PHP developers that isn’t afraid to teach real programming techniques – this is so sorely needed in the PHP community, and you’re doing a great job!

    On a related note, check out my strongly-typed configuration/service-container:

    https://github.com/mindplay-dk/stockpile

    This uses dependency injection in a similar way to Pimple, but adds type-checking and relies on type-arguments (to anonymous closures) and property-annotations to achieve better IDE support.

    This is not a general-purpose DI container, but rather specifically designed to host service-components and configuration-values – and to that end, it also adds the ability to seal the container against further changes once it is configured, which also serves to perform a check to make sure that your site’s local configuration-file is complete and correct…

    • http://www.innovativephp.com Rakhitha Nimesh

      Hello Rasmus

      Thank you very much for your interest in my article. I really appreciate that you took time to provide such a detailed comment. Its great to have PHP developers coming in an sharing their own experiences.

      I’ll definitely check your dependency injection container.

      Thanks again for sharing.

  • http://blog.budiirawan.com budi irawan

    Hi Rakhitha,

    Your article is really detail in explanation about indepedency injection. Simple to understand. :D

    • http://www.innovativephp.com Rakhitha Nimesh

      Hello Budi

      Thank you very much for your interest. Let me know what you like to see more on phpmaster and how I could have improved my explanations.

  • Justin Braithwaite

    What are the advantages to using this method over the autoload* function built into PHP? I’m having a really tough time seeing it.
    * http://php.net/manual/en/language.oop5.autoload.php

    • http://www.innovativephp.com Rakhitha Nimesh

      Hi Justin

      Thanks for the comment and asking your questions.

      Autoload function is used to load classes only. It does not consider about any dependencies between classes. Whenever new Class() is called, you can use it to load the class dynamically without using include_once ‘class.php’.

      Here we are talking about the dependencies between classes. I’ll ask a simple question to help you understand this.

      Think that you have a class called Email and it is used in 10 other classes as new Email(). Now assume new version of same library comes as Emailer. How do you make your exiting code work with new class?

      Looking forward to your answer and I’ll explain more depending on the answer.

      • Justin Braithwaite

        Thanks for the reply!

        I understand now what you mean by dependency. I always use a framework[1] and that’s why I’ve never stumbled across this issue. Going back to your hypothetical… so a new version of a library has a different name? And you can’t simply rename the library to the old name? I would delete the old class file(Email.php) and autoload the new one (Emailer) whenever the old one is called[2].

        [1] http://ellislab.com/codeigniter
        [2] http://pastebin.com/cB3KzPxY

        Unrelated: The line-height of this textarea is insane (32px) something like 20px works a lot better. http://imgur.com/qKAEuIM

        • http://www.innovativephp.com Rakhitha Nimesh

          Hi Justin
          It is not a good practice to find and replace your class names inside the codes. We cannot guarantee that new version is similar to the previous version. What if email sending method is also changed to a new one. So you have to replace the method everywhere as well. Thats is unneccessary additional work.

          But when you use a librray like pimple for managing dependencies, you only have to change one place and all your functions will use the new code.

          Personally I use Codeigniter. Framework classes will manage its dependencies.But even with a framework you need to manage dependencies for your own classes.

          Let me know if this helps and you have more questions.

          Thanks for taking time to comment.

  • http://www.j7mbo.co.uk James Mallison

    I’ve been using pimple for a while, but as a service locator – i.e I pass $container through to the constructor of my controller, using my routing mechanism (so, it’s created in the bootstrap), and then the controller can request objects using the $container[‘object’].

    This is a service locator, as the controller’s dependencies are hidden until called throughout the code.

    From other containers I’ve researched, many use things like reflection / caching to inject dependencies depending on what the class signature asks for in the constructor. So I’m not using these others as a service locator, I’m using them as DiCs.

    My question – the pimple documentation shows using pimple as a service locator – how do I not use pimple as a service locator (anti-pattern) – can it even be used as a true DiC?

    • http://www.innovativephp.com Rakhitha Nimesh

      Hi James

      Thank you very much for taking time to provide your suggestions.

      As you mentioned, passing the container into controller and requesting objects works like the service locator and the documentation explains it.

      I think it works as both DI as well as service locator. All the dependencies are injected into the container before passing it to the service locator. But certainly need to think about ways of using it as pure DI contaainer.

      Pimple is a very simple library and hence we cannot expect true DI capabilitiees. I have worked with Java Spring framework and this is no way near the capabilities of DI provided by that framework.

      Can you let me know the other DI containers you have tried, so that I can have a look at them too.

      thanks

  • Gabriel Vítor

    Thanks Rakhitha, your article is very clear and well organized.
    Now I can understand all the picture of DI. :)