Encase – A Lightweight IOC Container for PHP

My latest project is Encase. Encase is a lightweight IOC Container for PHP. It allows you to inject dependencies into your objects in an intuitive manner. The API is declarative yet small enough that you can easily master it in a couple of hours.

The source code is MIT licensed and available on GitHub. The library is also available on packagist and can be used with Composer by including it in your composer.json.

{
  "require": {
    "dsawardekar/encase-php": "~0.1.2"
  }
}

What is Dependency Injection?

Dependency injection is a somewhat misunderstood design pattern used in Object-Oriented programming. It is often associated with verbose languages like Java. And often accompanied by large XML based configuration requirements.

This background can sometimes needlessly complicate DI. Yet the fundamental ideas behind DI are easy to grasp.

In any object oriented system you have Objects with these characteristics,

  • Objects that depend on other objects to complete there work.
  • Sometimes an Object can create these dependent objects on its own.
  • On other occasions the dependent objects need to be provided to it.

There are a wide variety of design patterns that address how to go about organizing objects in an OO system.

What DI says is that no matter how you organize your objects, when you need any dependent object you fetch it from an external source, called the Container. Also commonly referred to as an IOC Container.

But does that mean that every time an object needs a dependent object it should go look it up from the Container? No, that’s where the Injection part comes in.

Instead of looking up an object’s dependencies from a container manually, you declare that this Object has such-and-such dependencies. Now when you lookup an object from the Container, it will give you an object with all it’s dependencies met.

How Encase Works

Encase separates configuration of a Container from the declaration of dependencies.

Configuring an Encase container is how you declare the objects in your system. Objects can be prexisting, or need to be instantiated on every lookup or even be singletons that are only instantiated once.

For instance, if you are building a URL shortener and expander you would have different objects that do URL shortening and expanding based on a user’s actions. Such an Application would declare it’s dependencies like below,

$this->container = new Container();
$this->container
     ->object('options', new Options())
     ->object('bitly_api_key', getenv('BITLY_API_KEY'))
     ->object('bitly_login', getenv('BITLY_LOGIN'))
     ->factory('url_shortener', 'Urly\BitlyShortener')
     ->factory('url_expander', 'Urly\LongurlExpander');

Here BitlyShortener is a class that will be instantiated when url_shortener is looked up from the Container. The bitly_api_key and bitly_login are strings that are fetched from Environment Variables to avoid hardcoding.

The BitlyShortener object needs the bitly_api_key and bitly_login dependencies to do it’s work. You declare this by adding a needs function in the BitlyShortener class.

class BitlyShortener {

  function needs() {
    return array('bitly_api_key', 'bitly_login');
  }

}

The needs function returns an array of keys corresponding to those declared on the container earlier.

How Encase differs from Pimple

Pimple is another lightweight IOC Container for PHP. Pimple relies on lazy assignment at the time of container configuration to inject dependencies.

The anonymous function is provided a reference to the container. You use this container to lookup the dependencies and return an object with the dependencies.

In the above scenario, you would need to create a constructor that takes the bitly_api_key and bitly_login as arguments and then create a new BitlyShortener with these values.

class BitlyShortener {

  function __construct($bitly_api_key, $bitly_login) { // __|
    $this->bitly_api_key = $bitly_api_key;
    $this->bitly_login = $bitly_login;
  }

}

// and configure with
$container['url_shortener'] = function($container) {
  return new BitlyShortener(
    $container['bitly_api_key'],
    $container['bitly_login']
  );
}

The difference is that with Encase you declare it’s dependencies with the needs function. While Pimple configures it from the outside.

How Encase differs from PHP-DI

PHP-DI is another well known DI library. The PHP-DI Container is similar to Pimple in that both use Constructor Injection. That is, constructor functions list the dependencies as arguments.

Where PHP-DI differs from Pimple is that it takes away the manual injection process. It uses Reflection & Annotations to automatically figure out the dependencies of an object and injects them.

In the example above, PHP-DI can figure out that BitlyShortener needs bitly_api_key and bitly_login by using Reflection to look at it’s class definition.

As seen earlier Encase uses needs in a declarative manner similar to PHP-DI. An advantage of using a function to return the dependencies is that it can then control the result at runtime.

If Objects inherit from others, they can even add new dependencies to the result of a parent’s needs result.

Final Thoughts

Regardless of which DI Container you use, using one can radically improve the design of your OO system. Any system with a large number of classes should use some form of Dependency Injection. You will improve coupling between your objects and have much simpler test suites.

The source code of Urly, the URL shortener example from above is also available on GitHub. It’s a command line application that can shorten or expand URLs using the bit.ly API and the longurl.org API.

$ bin/urly --shorten http://some-long-url.com/
$ bin/urly --expand http://short.url

Note that the app needs the Environment Variables mentioned to work correctly as Bitly doesn’t work without an API key.

Level Up your WordPress-Fu with Weekly Tips in your inbox. Find out just why query_posts() is evil and why you should stop using it. (No Spam)