Code Bright: The Container

← Back to Index

This title was written for Laravel version 4. If you're looking for material on the latest version of Laravel, then please check out Code Smart.

Shauna Gordon crept slow and mouse-like up to the entrance of the Otwell mansion. She kept to the shadows, crouched low and out of sight. Shauna was no stranger to stealth. You see, she wasn't just a developer, she was one of the most renowned cat burglars in the United States of Americaland. This evening however, she wouldn't be stealing cats. No, she had her eyes set on a much bigger prize.

She removed her lock-picking kit and set to work on the door. The Otwell mansion had six locks on its outer door protecting the rare and valuable treasures held within. The locks were no match for Shauna's nimble fingers. One by one they all went click. The door swung open quietly.

Shauna crept along the wooden floorboards of the main landing. Her five-fingered shoes made no noise as they came in contact with the cold varnished floors. All around her were trinkets and treasures. Priceless components from previous versions of Laravel's history. Trophies for development battles and award ceremonies. None of these treasures caught Shauna's eyes. She had her gaze firmly fixed on a greater treasure. There it was, in all of its shiny golden glory!

Actually, I kinda imagine it looking like the Ark of the Covenant from the Indy movies. You know, like a big decorated solid gold box? Sure that will do. Maybe with some kind of light 'aaaaaaaaaaaahhhh' background noise playing when you stand near it.

It was the container. The heart of Laravel! This was the most valuable treasure in the mansion. Shauna slithered up to the container silently and laid her hands upon its cool-to-the-touch exterior. She couldn't contain her excitement. She had to know what was inside.

Slowly, she slid the lid to one side and peered into the depths of the container. With a burst of light and screams of pain a number of entities filled the room. Bound closures and object instances swirled around the landing of the Otwell mansion. The screams and storms were so violent that the Otwell clan had been woken.

Taylor leaned over the balcony of the mansion, holding a sawn-off shotgun in each hand.

"Shauna!? You again? Get out of my house! Just go and clone the Laravel repository if you want your own container so bad!"


Okay, I know what you must be thinking.

Dayle, you wrote about the container in the architecture chapter.

Yes. Yes I did. Do you really think that was all there was to know about the Container? The mysterious beating heart of Laravel has only a single trick to share? No, handsome reader, you are very much mistaken!

The container is the most important part of the framework. It holds everything together. Without the container, there would be no Laravel four. There would be no Code Bright. Everyone would be sad, and developers might not be as beautiful.

In the architecture chapter I previously mentioned how the $app object at the heart of Laravel extends the Illuminate\Container\Container class. I explained how we could store things within it using a syntax similar to arrays. It becomes a magical box, holding anything we wish to keep within it. It's a little mysterious though, and tends to be confusing for many first-timers. A little like the Ark of the Covenant I suppose. Did I mention how much I love those movies? Dun-da-dun-dun... Do-dee-do!

Well, before I get an angry email from Mr Lucas, let's start playing with the container.

Inversion of Control

You may have heard of the container being called an 'IoC' container. Can you guess what the 'IoC' stands for?

Inversion of Control.

Don't be a smart Aleck, you saw it right there in the heading, didn't you?! If you weren't so dang handsome I'd have to be angry with you. Inversion of control is self-explanatory, right? Let's move on.

I'm just kidding! It's a terrible term. I despise when developers invent over-complex terms to make ourselves sound smarter than we are. It truly aggravates me!

Let's simplify the term. IoC essentially means:

Allow the framework to build things for us.

We give the framework a recipe for creating objects or values for us. This means that we are transferring (or inverting) control of the instantiation of these objects to the framework. We're trusting it to build them for us.

Earlier, I told you how we could use the $app instance as an array to hold things within the container. Well, in Laravel we have the 'App' facade which can be used to make the container (and application object) available to all parts of the framework. It's aliased right there in the root namespace.

We can use the facade to store and retrieve things from within the container, sharing them amongst the different classes that make up our application.

First, here's an example without the facade.

<?php

// app/routes.php

// Get hold of the app object.
$app = app();

// Bind the integer 3 into the container.
$app['three'] = 3;

// Output the response.
echo $app['three'];

This works absolutely fine. We get the value 3 as we might expect. It's no different to working with any other array.

The truth is, the container is doing something very clever under the hood. Let's take a look at the instantiation of an imaginary 'sms' library. Normally you'd want to hold the parameters within a configuration file, but let's keep this simple for the sake of this example.

<?php

// app/routes.php

// Get hold of the app object.
$app = app();

// Create a new SMS component.
$sms = new SMSClient;
$sms->setUsername('shauna');
$sms->setPassword('1_5t341_c4t5!');

// Bind the component.
$app['sms'] = $sms;

// Send an SMS.
$app['sms']->send('+55121212153', 'Yo dawg.');

It looks like we're just setting the sms index of the $app array (really, we know it's a container) to an instance of our SMS client, but under the hood, the Laravel container is doing clever things for us.

You see, the Laravel container implements the ArrayAccess interface, which allows it to simulate some properties of an array. For one, direct access to its values through indexes.

When you implement ArrayAccess you must provide two methods, offsetGet() and offsetSet(), to let the language know what should happen when the object is used in a similar manner to an array.

Let's take a look at the source of the Illuminate container. Yes, I know it's a complicated class, but I promise this will be useful. We're going to be looking at the two methods that I mentioned a second ago, offsetGet() and offsetSet().

<?php

// Container.php

/**
 * Get the value at a given offset.
 *
 * @param  string  $key
 * @return mixed
 */
public function offsetGet($key)
{
    return $this->make($key);
}

/**
 * Set the value at a given offset.
 *
 * @param  string  $key
 * @param  mixed   $value
 * @return void
 */
public function offsetSet($key, $value)
{
    // If the value is not a Closure, we will make it one. This simply gives
    // more "drop-in" replacement functionality for the Pimple which this
    // container's simplest functions are base modeled and built after.
    if ( ! $value instanceof Closure)
    {
        $value = function() use ($value)
        {
            return $value;
        };
    }

    $this->bind($key, $value);
}

Kind of confusing? Not a problem. Let me explain. Let's look at the content of the offsetGet() method.

<?php

// Container.php

return $this->make($key);

Here the container is saying, when you try to access anything in an array like fashion, use the make() method with the key to get our value.

The make method can be used to build values and objects from the storage of the container. It does some other clever stuff, but we'll be taking a look at that in the next section.

The offsetSet() method looks a little more confusing. Here's the bit we're interested in.

<?php

// Container.php

if ( ! $value instanceof Closure)
{
    $value = function() use ($value)
    {
        return $value;
    };
}

$this->bind($key, $value);

When we try to set a value onto the container using an array-like syntax, we first check to see if the provided value is not a Closure. If it's not a Closure, then we set the stored value to a Closure that returns the original value.

Why do we do that? Well, just like how the make() method is used to internally retrieve values from within the container, the bind() method is used to store them. The bind() method takes a Closure as a parameter, so we need to wrap it first.

Once it's wrapped, we use bind() and store it within the internal storage of the container.

Looking at our previous example step by step, we see that...

<?php

// app/routes.php

// Bind the component.
$app['sms'] = $sms;

... effectively becomes...

<?php

// app/routes.php

$app['sms'] = function () use ($sms) {
    return $sms;
};

... when bound within the container.

Hopefully you're still with me. We know that everything bound within the container is now wrapped within a Closure. So when we try to retrieve our SMS component from the container...

<?php

// app/routes.php

$sms = $app['sms'];

...we know that a Closure is being executed internally, and is returning the $sms instance held within.

Now that's enough of the $app object. That's messy. Let's use the facade instead. We can't use the ArrayAccess syntax on the facade itself, since it is simply masking the $app instance behind it. Instead, what we can do is use the bind() and make() methods directly.

Now that we know that bind() receives a Closure, we can use it to store values and objects using the facade. Let's store an integer again.

<?php

// app/routes.php

// Bind an integer into the container.
App::bind('three', function () {
    return 3;
});

// Retrieve the integer from the container.
echo App::make('three');

We aren't really doing anything new here. We're simply masking our old ugly code with the new and beautiful facade. The bind method takes a container 'key' (used to later retrieve the value) as the first parameter, and as a second parameter we give our Closure that returns an integer.

We can then use the make() method, supplying the container key that we used to set the value, and are given the result of the bound Closure.

Let's look at a similar example using our SMS component.

<?php

// app/routes.php

// Create a new SMS component.
$sms = new SMSClient;
$sms->setUsername('shauna');
$sms->setPassword('1_5t341_c4t5!');

// Bind the component.
App::bind('sms', function () use ($sms) {
    return $sms;
});

// Send an SMS.
App::make('sms')->send('+55121212153', 'Yo dawg.');

First we create the component. Next we bind it within the container using a key string and Closure. Finally, we resolve the value from the container by key and make use of it.

There's a problem here, though. Have you managed to spot it? It can be a little difficult to spot, you need to think about the flow of execution of the application.

We're creating that new SMS instance and binding it to the container. What happens if we don't wish to use it in every request? The likelyhood is that we only want certain routes to send SMS notifications. In all other requests we are wasting time and resource by creating that SMS component upfront.

This is where the Closure becomes useful. You see, our Closure within the container is only ever executed when you attempt to resolve it from the container. You can think of it as a form of blueprint for creating your stored value.

Why don't we move the whole creation of the SMS component into the Closure? Let's take a look at how that might be implemented in code.

<?php

// app/routes.php

// Bind the component.
App::bind('sms', function () {

    // Create a new SMS component.
    $sms = new SMSClient;
    $sms->setUsername('shauna');
    $sms->setPassword('1_5t341_c4t5!');

    // Return the created SMS component.
    return $sms;
});

// Send an SMS.
App::make('sms')->send('+55121212153', 'Yo dawg.');

It's not a big change is it? We simply move the creation of the SMS instance to within the Closure. We're inverting control right here. We're providing the framework with the power to create our SMS component only when we need it. We know that the $sms instance will only ever be instantiated when we call App::make('sms') in the requests that intend to use it.

You see, Inversion of Control isn't as scary as it sounds, is it? Perhaps it should have been named 'Allowing the framework to create things for you only when you need them container.', or the AtFtCTFYOWYNT container for short. That's much better!

There's one final flaw with the binding of our SMS component, though. If you've managed to spot it, then go ahead and help yourself to some of Dayle's gold stars. They taste like plastic and glitter.

Take a look at the following example, maybe you'll be able to spot the problem?

<?php

// app/routes.php

// Bind the component.
App::bind('sms', function () {

    // Create a new SMS component.
    $sms = new SMSClient;
    $sms->setUsername('shauna');
    $sms->setPassword('1_5t341_c4t5!');

    // Return the created SMS component.
    return $sms;
});

// Send an SMS.
App::make('sms')->send('+55121212153', 'Yo doge.');

// Send another SMS.
App::make('sms')->send('+55121223432', 'SUCH TEXT. WOW');

Here, all we've added is a second line to send an SMS message. It shouldn't be a problem, right? Well, it's true that the code will function just fine. The problem is that it's not very efficient. To spot the flaw, we need to think about the flow of execution once more.

We create the blueprint for building an object. We resolve the component to send an SMS message, then we resolve the component and send another.

We resolve the component twice. We resolve the Closure twice. Uh oh! That means that we are instantiating the SMS component twice. We're setting the credentials twice. Are you getting tired of hearing twice yet? I'm getting tired of typing it.

Let me get it out of my system. TWICE TWICE TWICE TWICE TWICE.

Right, we're good. This double resolution is a problem. It's wasteful, isn't it? The component is being used the exact same way the second time. It's using the same credentials. Why should we bother to instantiate it again? It's a waste of memory and CPU cycles.

Surely the smart and mighty Otwell must have thought of a way around this problem? Of course he did, and as ever, Laravel has got your back!

We can use the magical share() method of the container. It will allow the result of a Closure to be cached internally for the current request, so that subsequent container resolutions return the same value without repeatedly executing the Closure.

Let's take a look at how it can be used.

<?php

// app/routes.php

// Bind the component as a singleton.
App::singleton('sms', function () {

    // Create a new SMS component.
    $sms = new SMSClient;
    $sms->setUsername('shauna');
    $sms->setPassword('1_5t341_c4t5!');

    // Return the created SMS component.
    return $sms;
});

// Send an SMS.
App::make('sms')->send('+55121212153', 'Yo doge.');

// Send another SMS.
App::make('sms')->send('+55121223432', 'SUCH TEXT. WOW');

All we need to do, is swap the bind() method for the singleton() method which has the same signature. You may have heard of the Singleton design pattern. It ensures that there is only ever one instance of a class, and does not allow it to be duplicated. This isn't a true singleton, but it's a similar process, and a fitting name for it. The Closure will only ever be resolved once. You'll get the same instance of the SMS component back every time.

Why should I believe you?

Now then, you're a feisty one today aren't you reader? You think I'd lie to you? Well... erm, again? I suppose I don't have a great track record here, do I? Fine, let me prove it to you.

Let's go back to numbers! Take a look at the following snippet.

<?php

// app/routes.php

App::bind('rand', function () {
    return rand();
});

var_dump(App::make('rand'));
var_dump(App::make('rand'));
var_dump(App::make('rand'));

We know that the rand() function for PHP can be used to generate a 'pseudo' random number. Let's execute this piece of code and see what we get.

int 1742870120
int 385800925
int 1573429337

Wow. Such random. So number. It's exactly as we expected. Using the bind() method, the Closure is executed each and every time that it is resolved using the make() method. We receive a new 'random' number each time.

Let's try the singleton() method instead, shall we?

<?php

// app/routes.php

App::singleton('rand', function () {
    return rand();
});

var_dump(App::make('rand'));
var_dump(App::make('rand'));
var_dump(App::make('rand'));

It's a slight change, but I have a feeling that it will have a very dramatic result. Let's take a look at the output.

int 903162162
int 903162162
int 903162162

You see, the Closure is executed only once. The integer 903162162 that we get back from the rand() function the first time the Closure is executed, is cached, and given on all subsequent calls to make().

When we use the container, we need to decide whether our resource can be shared or not. Sometimes we need a 'fresh' resource every time, and sometimes it's simply a waste of resources. As always, we need to use our best judgement as developers to decide which method is appropriate.

Well, do you think that we've explored all the mysteries of the container now? Of course not! In fact I've got a whole extra section dedicated to my favourite container trick. Prepare to be amazed!

Dependency Injection

Oh no! It's another scary term, isn't it? If you're just starting out with object-oriented coding then you may have heard of this technique being thrown around the internet in the PHP community recently. You might be thinking "Oh no, it's that thing related to testing!". I know that testing code can be intimidating, but even without writing a single test, I promise that dependency injection can be useful to you. It's not going to be scary. I promise!

I once saw a quote once about dependency injection that I absolutely loved. Unfortunately, I can't for the life of me remember who it was that said it. I like to think that the semi-anonymous author would be happy for me to use it anyway, so here goes.

Dependency injection... or as I like to call it.. 'setting things'. - Anon Code Philosopher.

It's fantastic because it's completely true. Here we have another prime example of developers using a complicated name, to make a technique sound much more complicated and advanced than it needs to be.

Let's start small. Before we get started on dependency injection, let's take a look at reflection-based class resolution using the container. Oh dear, that sounded fairly complicated didn't it? Maybe that's where these names come from? Let's take a look at a code example that will help serve to clarify that horrible wording!

<?php

// app/routes.php

$collection = App::make('Illuminate\Support\Collection');

This code isn't very useful. It just gives is an empty collection. Why does it do that though? I don't remember binding that key into the container, do you?

The container is awfully clever. If we try to resolve a key that doesn't exist, but instead a class with that namespace and name exists, then that's obviously what we want, right?

The container says to itself "Hey, I see this Illuminate\Support\Collection class definition over here, why don't I create a new one?".

The talking to itself isn't actually implemented in the code, unfortunately. Perhaps someone could send a pull request for that?

Let's quickly take a look at the signature for the constructor of the Collection class.

<?php

// Collection.php

public function __construct(array $items = array())

You'll notice that you can pass an array of items to the constructor to set the initial content of the collection. However, it has a default value of an empty array.

When resolving classes from the container by name, the container will use the PHP reflection API to examine the parameters that it needs to be constructed. Since this parameter has a default value, the class can be constructed without passing any additional values.

Let's make our own class to see what happens when our constructor parameters don't have a default value.

<?php

// app/routes.php

// An example class, injecting a 'something' instance.
class Example
{
    protected $something;

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

// Attempt to resolve the example class by name from the container.
$example = App::make('Example');

Here we have an Example class that takes a $something instance within its constructor. We're injecting that dependency into the class. We're 'setting it'. That's all dependency injection is.

Unfortunately, the container has no idea what our $something is. So, what happens when we execute this code? Well, we get an error. An Illuminate\Container\BindingResolutionException is thrown with the message 'Unresolvable dependency resolving [Parameter #0 [ $something ]].'.

Well at least it tried, right? So how can we help the container to tackle this problem? Well, the best thing that we could do is somehow describe the instance that we are injecting. We need to let the Container know what we intend for it to be.

We can do this by type-hinting the parameter in the constructor. Let's implement the Something class, and then type-hint its injection within the Example class. That sounded fairly jargon filled, didn't it? It's always much more simple to explain with code!

<?php

// app/routes.php

// A Something class.
class Something
{
    // ...
}

// An example class, injecting a 'something' instance.
class Example
{
    protected $something;

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

// Attempt to resolve the example class by name from the container.
$example = App::make('Example');

This time, the Example class is instantiated correctly. So how does it work?

Well, the container will take a look at all the parameters required in the constructor of the Example class. It will notice that the Example class requires an instance of a Something class as a parameter. Since the container is aware that there is a class definition for a Something class, and is also aware that it can be instantiated, it will simply created a new instance and pass it to the new Example. Laravel is managing our dependency hierarchy for us.

What's even more impressive about this process, is that it happens all the way down the dependency tree. Let's take a look at an overcomplicated example.

<?php

// app/routes.php

class First
{
    public function __construct(Second $second) {}
}

class Second
{
    public function __construct(Third $third, Fourth $fourth) {}
}

class Third
{
    public function __construct(
        Fourth $fourth,
        Illuminate\Container\Container $container
    ) {}
}

class Fourth
{
    public function __construct(array $myArray = array()) {}
}

$first = App::make('First');

Here we have a four level dependency hierarchy, which the container is happy to instantiate for us using make(). I'm not going to cover each step of the example, but for a little homework, why not try to identify the following?

  • Injection of the four levels of classes.
  • Injection of a default value.
  • Injection of multiple parameters.
  • Injection of the Laravel container.

Why not try drawing a diagram of the dependency tree? Tweet it to @daylerees and I'll give you a grade! :)

The best part about the reflection-based injection, is that most of the classes that are used by the framework are also resolved through the container.

Anywhere where you specify a class/action pair in the following format...

Some\Namespaced\Class@andMethod

... the class will be resolved from the container. This means that any dependencies, and dependencies of dependencies, will be resolved automatically. Your controllers, filters, composers, listeners and other classes can all take advantage of this automatic injection mechanism!

In the next chapter we will build on what we've learned about the controller, to create service providers for third party libraries.

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!