What’s the difference between a Dependency Injection Container and the Service Locator Pattern?

Dependency injection is important in modern coding practices. It allows us to help maintain our code by making it easier to test, and by making it less likely that dependent code will need to be changed if the dependency changes.

There are two different tools we use to make dependency injection easier, but they are easily confused and slightly different. So what is the difference between a Dependency Injection Container and the Service Locator pattern?

DICs and SLs have slightly different roles:

An implementation of the Service Locator pattern is an object that gets injected into classes that have dependencies.  From inside those classes, the Service Locator object can then be used to find and locate those dependencies when they’re needed to be used.

class Moo
{
    private $dependencyA;
    private $dependencyB;

    public function __construct($container)
    {
        $this->dependencyA = $container->get('DependencyA::class');
        $this->dependencyB = $container->get('DependencyB::class');
    }
}

Dependency Injection Containers are quite similar, although they have a subtle but important difference.  Dependency Injection Containers don’t get injected into classes themselves, they locate the required objects and inject those objects and instead inject those into the class.

class MooFactory
{
    public function create($container)
    {
        $dependencyA = $container->get('DependencyA::class');
        $dependencyB = $container->get('DependencyB::class');

        return new Moo($dependencyA, $dependencyB);
    }
}

class Moo
{
    private $dependencyA;
    private $dependencyB;

    public function __construct($dependencyA, $dependencyB)
    {
        $this->dependencyA = $dependencyA;
        $this->dependencyB = $dependencyB;
    }
}

In the example above we use a factory to fetch the dependencies and inject them into our class, which creates a looser coupling between your code and the container. If the container ever needs to be switched out, classes themselves never have to be changed, only the factories in this example. The responsibility of fetching dependencies are separated from the class’s responsibility so it’s a lot easier to test for these specific changes when you come to change your container.

There is another maintenance advantage to using a Dependency Injection Container over a Service Locator. If you have a look at the constructors of both examples, we can quickly see what our class requires to work in the second example. In the first example, the only dependency we can see in the constructor is the container itself, which hides the true dependencies somewhere else in the class’s code.

Interestingly, Zend Framework’s Service Manager has recently gone from being described on the Zend Framework website as an implementation of a Service Locator to being called a Dependency Injection Container. This is probably to discourage people from directly injecting the container into controllers, and instead using a factory to injection actual dependencies into controllers.

Initialising the Service Manager in Zend Framework 2

You may have a need for initialising the Service Manager in Zend Framework 2 as a separate instance to the included framework service manager.

Similarly, if you’ve decided to use the Zend\ServiceManager module for your project, you’ll need to instantiate it in the following way:

Get the service manager configuration. In this case I’m getting it from the ZF2 application.config.php file:

$configuration = require realpath(__DIR__ . '/path/to/application') . '/config/application.config.php’;
$smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : [];
$serviceManager = new ServiceManager(new ServiceManagerConfig($smConfig));

If you’re doing this inside a ZF project, you’ll need to set the application config

$serviceManager->setService('ApplicationConfig', $configuration);

Depending which version of the Zend\ServiceManager you have available, you may find that certain parts of the service manager don’t get set with the code above. This can include delegators, initializers and abstract factories. If you find this is the case, loading the module manager will fire off the listener that merges this extra config with your standards service manager config:

$serviceManager->get('ModuleManager')->loadModules();

MySQL INFORMATION_SCHEMA subquery bug

I found a bug today in MySQL 5.6, I hope this saves someone some time:

I’ve been trying to select columns names from a table, for a PHP class to check they exist and use elsewhere. To do this I built a sub query that selects results from the INFORMATION_SCHEMA database to return a subset of the total results. Initially, I was not getting any results back from the query.

I ran both the inner and outer queries separately, and both returned the expected results, it was only when they were ran as a single query that no results came back.

Eventually, I stumbled across this MySQL bug report:

https://bugs.mysql.com/bug.php?id=77191

The bug is in MySQL’s optimiser, and it prevents the use of the INFORMATION_SCHEMA database in a subquery.

The fix is to turn off semi-join transformations in the optimiser:

set optimizer_switch='semijoin=off';

Joining the a table in the INFORMATION_SCHEMA database still works as expected, but unfortunately the database isn’t indexed, so any queries that join it will take much longer to run.