Symfony2: Dependency Injection Types

EDIT: Changes have been made to Dependency Injection in Symfony2 since I wrote this post, please read my follow up post after this one.

Symfony2 makes good use of Dependency Injection throughout and makes the Dependency Injection container available for code built on top of the framework. In this post I will look at the various ways of injecting dependencies into an object and how this is done in Symfony2. For more information on Dependency Injection itself, there is the wikipedia page and Martin Fowler's introduction along with Fabien Potencier's series of articles.

Symfony2 supports using XML, YAML and PHP to configure Dependency Injection, the recommended method for configuring Dependency Injection is XML so that is what I will look at here. There are several ways of injecting dependencies into a class that are supported in the XML configuration.

I am reusing the controller from my previous post, (StaticController refers to its use for static page content and not Static methods).

Constructor Injection

The dependencies are to be passed into the constructor, our class will look like this:

<?php

namespace LimeThinking\SpringBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class StaticController
{


    protected $response;
    protected $request;

    public function __construct(Response $response, Request $request)
    {
        $this->response = $response;
        $this->request  = $request;
    }


    public function indexAction()
    {
        //do stuff to create response
        return $this->response;
    }


}

The relevant section of the XML config file looks like this:

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        
        <service id="lime_thinking_spring.response" 
                 class="Symfony\Component\HttpFoundation\Response"/>      
        <service id="lime_thinking_spring.request" 
                 class="Symfony\Component\HttpFoundation\Request"/> 

        <service id="lime_thinking_spring.static" 
                 class="LimeThinking\SpringBundle\Controller\StaticController">
            <argument type="service" id="lime_thinking_spring.response" />
            <argument type="service" id="lime_thinking_spring.request" />
        </service>        
        
    </services>

</container>

Some advantages to this are that the dependencies must be provided to the object on instantiation so they must have been provided if accessed later. If the dependencies are necessary then this is an ideal way of ensuring they are provided.

There is a disadvantage that any runtime parameters cannot be passed to the object though as the Dependency Injection container is taking care of construction and must be passed in another way. Also the dependencies cannot be changed in the lifetime of the object which may be desired, if they do need to be changed than a setter method for this would need to be provided as well.

Setter/Method Injection

This is injection by calling a method of our class:

<?php

namespace LimeThinking\SpringBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class StaticController
{


    protected $response;
    protected $request;

    public function setResponse(Response $response)
    {
        $this->response = $response;
    }


    public function setRequest(Request $request)
    {
        $this->request  = $request;
    }


    public function indexAction()
    {
        //do stuff to create response
        return $this->response;
    }


}

The XML would now be like this:

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        
        <service id="lime_thinking_spring.response" 
                 class="Symfony\Component\HttpFoundation\Response"/>      
        <service id="lime_thinking_spring.request" 
                 class="Symfony\Component\HttpFoundation\Request"/> 

        <service id="lime_thinking_spring.static" 
                 class="LimeThinking\SpringBundle\Controller\StaticController">
            <call method="setResponse">
                <argument type="service" id="lime_thinking_spring.response" />
            </call>
            <call method="setRequest">
                <argument type="service" id="lime_thinking_spring.request" />
            </call>
        </service>        
        
    </services>

</container>

It is more difficult though to ensure that required dependencies have been injected before any methods of the object are called. It is of course possible to check that the dependencies have been provided before using them but constructor injection would do this for us without additional code.

If there are quite a few dependencies then it avoids a long constructor parameter list at the expense of a a lot of methods. If the list of dependencies is that long that perhaps some refactoring is called for.

One case where this is useful and which constructor injection is not a possible solution is where the dependencies are held in a collection within the object and there is a variable number, for example if a set of filters is to be run against some output. In this case there maybe no filters at all or a variable number. The setter method can be configured to be called the appropriate number of times in the XML file, this would not be possible with constructor injection except by passing the collection itself as the dependency.

Interface Injection

EDIT: Interface Injection has been removed from Symfony2 since I wrote this post, please read my follow up post for more information.

Symfony2 also allows for interface injection, in this case our class does not look much different from the setter method class except that it implements an interface containing those methods.

<?php

namespace LimeThinking\SpringBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

interface ControllerInterface
{

    public function setResponse(Response $response);
    

    public function setRequest(Request $request);
    

}
<?php

namespace LimeThinking\SpringBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class StaticController implements ControllerInterface
{


    protected $response;
    protected $request;

    public function setResponse(Response $response)
    {
        $this->response = $response;
    }


    public function setRequest(Request $request)
    {
        $this->request = $request;
    }


    public function indexAction()
    {
        //do stuff to create response
        return $this->response;
    }


}

The advantage to this though comes in the XML file, passing the dependency to the object can be done by specifying the interface. This way all classes implementing the interface will have the setter method called with the dependency, this allows changing the dependency to be done in one place.

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        
        <service id="lime_thinking_spring.response" 
                 class="Symfony\Component\HttpFoundation\Response"/>      
        <service id="lime_thinking_spring.request" 
                 class="Symfony\Component\HttpFoundation\Request"/> 

        
        <service id="lime_thinking_spring.static" 
                 class="LimeThinking\SpringBundle\Controller\StaticController"/>
       
    </services>

    <interfaces>
        <interface class="LimeThinking\SpringBundle\Controller\TestInterface">
            <call method="setTemplating">
                <argument type="service" id="templating" />
            </call>
        </interface>
    </interfaces>

</container>

This is of course not always useful as we may want control over which particular dependency is passed into various classes, if you find though that the same one is required by multiple classes this is one way of simplifying the maintenance of the XML file.

Container Injection

This is where the container itself is injected, any objects can then be requested directly from it. Our class now looks like this:

<?php

namespace LimeThinking\SpringBundle\Controller;

class StaticController
{

    protected $container;
    protected $response;
    protected $request;

    public function __construct($container)
    {
        $this->container = $container;
        $this->response  = $container->get('lime_thinking_spring.response');
        $this->request   = $container->get('lime_thinking_spring.request');
    }


    public function indexAction()
    {
        //do stuff to create response
        return $this->response;
    }


}

and the XML:

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        
        <service id="lime_thinking_spring.response" 
                 class="Symfony\Component\HttpFoundation\Response"/>      
        <service id="lime_thinking_spring.request" 
                 class="Symfony\Component\HttpFoundation\Request"/> 

        <service id="lime_thinking_spring.static" 
                 class="LimeThinking\SpringBundle\Controller\StaticController">
            <argument type="service" id="service_container" />
        </service>        
        
    </services>

</container>

The actual injection is easier as only the container is injected. This does however mean that there is an additional dependency on the container when testing. We either need to use a container to provide mock objects or mock the container as well.

Whereas with all the above methods the mocks can be directly injected without need for the container to be used in the unit tests. It also makes it more difficult to quickly see what dependencies an object has since they are not being clearly injected and any number could be being requested from the container throughout the class. Whilst it makes matters simpler when there a lot of dependencies that probably means refactoring is needed. I guess there may be a case for doing this during development whilst the dependencies of an object is an unknown and then once it has settled down moving to one of the other methods, however I would personally avoid this method of injection.

Summary

Apart from container injection which I think should be avoided, the other methods of injection each have their places depending on the nature of the dependencies. They are all well supported by Symfony2's Dependency Injection container allowing the most appropriate to be chosen rather than being tied to a particular style.