Code Happy: The IoC Container

← Back to Index

Please note that this chapter was written for VERSION 3 of the Laravel PHP Framework.

The IoC container is a tricky subject, many people are confused by its description in the documentation, and for a short time I was included in those people. A great deal of research, and the support of the fantastic Laravel community (join us in #laravel on freenode IRC) has cleared up the topic nicely. Hopefully I will be able to shed some light on this mysterious topic.

IoC stands for Inversion of Control, I don't want to complicate things with a full description, there are many online articles which will cover the nerdier side of this topic. Instead think of the container as 'Inverting the Control' or 'Handing control back to Laravel' to resolve our objects.

That's what the container is all about, resolving objects. Other than its use for injecting dependencies for use in unit tests (we will cover this later) you can simply think of the IoC container as a 'shortcut' for resolving complex objects, or following a singleton pattern, without the usual class associated with the pattern. More on singletons later, let's have a look at registering a objects with the container.

Registering Objects

Let's use our imaginations, like the big purple dinosaur on the TV taught us. We will be imagining a class called 'Discoball' which will be used all over our application for various groovy purposes.

Unfortunately, our Discoball class requires a lot of configuration before it can be used, let's have a look at that.

<?php
$db = new Discoball(Discoball::SHINY);
$db->configure_shinyness('max');
$db->spin_speed('8900rpm');

Woah! That's a lot of settings. Now it would soon get boring to have to instantiate and setup our discoball every time we want to use it. Let's let the IoC container instantiate it for us, and jump right in with a code sample.

I like to put this code into start.php, but you can put it anywhere you like, as long as your objects are registered before you try to resolve them.

<?php

// application/start.php

IoC::register('discoball', function() {

    // instantiate our object as before
    $db = new Discoball(Discoball::SHINY);
    $db->configure_shinyness('max');
    $db->spin_speed('8900rpm');

    // hand the object as the result of the closure
    return $db;
});

We use the IoC::register() method to register our object with the controller. The first parameter is a string that will be used to resolve the object later, I used the word 'discoball' as it made the most sense to me. The second parameter is a closure that we can use to instantiate our object.

Inside the closure you will see the familiar discoball configuration code, and we will return the configured discoball object from the closure.

Great! Our object is registered, and that's all there is to the Io... just kidding. Let's have a look at how we can use our registered object.

Resolving Objects

Now we have our disco ball registered, let's see how we can get it back. Let's make a call to resolve.

<?php
$db = IoC::resolve('discoball');

And that's it! Instead of creating and configuring a new instance of our discoball each time, we make a call to the resolve() method, passing the string that identifies the object and the IoC container will execute the closure we created in the first section, and return our instantiated and configured discoball object.

Handy, and saves many lines of code!

You can register and resolve as many objects as you want, go ahead and try it. For now lets move on to singletons.

Singletons

Resolving our discoball is useful, but what if our discoball was expensive on resources to instantiate, or should only be instantiated once? The register method will not be useful in this case, since the closure is executed with every call to resolve() and a new instance of the object is returned each time. This is where the singleton design pattern comes in.

The singleton design pattern involves writing your classes in a certain way, so that they can be called using a static method, and will always return the same instance of itself. This way the class is instantiated only once.

For more information on the Singleton design pattern, I would suggest a quick Google search, or check out the PHP API which has an article on the subject.

Singletons can be useful, but they require a certain class structure to be able to use them. The IoC container has a singleton() method which makes the process a lot more simple, and does not require any special kind of class. Let's register our discoball as a singleton instead..

<?php
// application/start.php

IoC::singleton('discoball', function() {

    // instantiate our object as before
    $db = new Discoball(Discoball::SHINY);
    $db->configure_shinyness('max');
    $db->spin_speed('8900rpm');

    // hand the object as the result of the closure
    return $db;
});

As you can see, the process is almost identical to registering an object, except that we use the method singleton() which accepts the same parameters.

When we resolve our discoball, the closure will only be run the first time resolve() is called, the resulting object will be stored, and any future calls to the resolve() method will return the same object instance. For example..

<?php

// closure is executed, and a discoball
// instance is returned
$db = IoC::resolve('discoball');

// the same instance of the discoball
// in the above statement is returned
$another = IoC::resolve('discoball');

Great! It's also worth noting that you can pass an already instantiated object as a second parameter to the singleton() method, and it will be returned by all future requests to resolve() for example..

<?php

$db = new Discoball(Discoball::SHINY);
IoC::singleton('discoball', $db);

// get hold of our discoball
$ball = IoC::resolve('discoball');

In a future chapter we will discuss using the IoC container and dependency injection in combination with unit testing.

My books are available online for free to encourage learning. However, if you'd like for me to keep writing, then please consider buying a digital copy over at Leanpub.com.

It's available in PDF, ePub, and Kindle format, and contains a bunch of extras that you won't find on the site. I have a full-time job, and I write my books in my spare time. Please consider buying a copy so that I can continue to write new books from the comfort of my sofa!