Close

Download the code from GitHub: Current version 1.4.2

Dependency Injection

PHP development really feels like it’s grown up in recent years, and with all the great new features landing in 5.3 we’ve never had it so good. Dependency injection is a well known concept, and there are currently a plethora of DI containers available from one of the interweb’s many pipes. These include Symfony and Crafty, while the Zend Framework also has its own DI container.

So why another?

One of my personal goals as a developer is to make things so simple, even I can understand them. But most of the DI containers I’ve come across require too much configuration, and usually via XML – yuck! So how is ours different?

This article assumes you’re familiar with the concept of dependency injection, if you’re not already using this technique then you should read some docs and come right back!

Creating an injector

The one requirement the library has is that Addendum is loaded.

include '../boxuk-di/lib/bootstrap.php';
$helper = new BoxUKInjectHelper();
$injector = $helper->getInjector(); 

The easiest way to get your injector set up and ready to go is to use the Helper class. This will create you a standard injector with all the defaults. You’re then good to go…

$class = $injector->getClass( 'MyClass' );

Type hinting dependencies

The injector will use type hinting to determine your class dependencies, so make sure to specify them in your constructor. If you don’t, no injection for you sorry…

public function __construct( SomeClass $classOne ) { ... }

Scope, and annotations

Using what we’ve seen so far, every time we ask for a class we’re going to get a brand new one back. If, for example, you have objects that you only want one of you need to specify their scope using an annotation.

/**
 * @ScopeSingleton
 */
class MyClass { ... }

This tells the injector that there should only be one of these at a time, so the second call, or the second class to specify this as their dependency will get the same one.

Another scope available is session scope, which stores objects in the session and returns them on subsequent requests.

Creating more objects...scope, and annotations

So – you might be thinking – do I need to pass this injector around now, to every object when I want to create something? The answer is – no. When the injector is created it is then available as a dependency like other classes, so if you need to use it in one of your classes…

public function __construct( BoxUKInjectInjector $injector ) { ... }

Just ask for it!

Primitives

What about primitives? Your objects may currently take in arrays and strings to their constructors, what about these? Primitives aren’t really dependencies, they’re usually configuration. So if your class takes both object dependencies and primitives to its constructor then the primitives will need to be refactored to a later initialisation phase. For example…

class MyClass {
   public function __construct( AnotherClass $class ) { ... }
   public function init( $port, $params ) { ... }
}

There is nothing special about the init() function, it is just an arbitrary method name. Also settings like this can often be abstracted to classes (maybe a single Settings object) which can then be injected.

The rule should be objects are dependencies, everything else is configuration.

You can also annotate methods and properties to be injected…

/**
 * @InjectProperty
 * @var AnotherClass
 */
private $anotherClass;
 
/**
 * @InjectMethod
 */
public function setSomeClass( SomeClass $someClass ) {
   $this->someClass = $someClass;
}

Property injection even handles setting private members so you don’t have to open your class up, and method injection has no constraint over the name of the method or the number of dependencies you ask for.

Isn't reflection slooooooooow?

If you’ve ever looked into reflection before you’ll probably know that it’s not the fastest part of PHP. It’s not sloooooooow as such… but for high performance web apps it just doesn’t cut the mustard. Which is why the reflection and annotation part of the library is split out into its own BoxUKReflect namespace – and includes a whole bunch of caching support to make it nice and fast.

This means you can get all the benefits of using these PHP features, and none of the headaches of waiting for them to happen. Bonza!

The easiest way to create your own reflector is to use the same Helper object we used to create our injector earlier, just call getReflector() instead. You’ll need to pass in a Config object if you want to set up caching, full info is in the main documentation.

Testing

The injector works great when it comes to unit testing. At Box UK we often sub-class the injector to provide an application specific test injector which will know how to provide useful defaults for unit tests (where customized canned objects are not provided).

$object = $testInjector->getClass( 'SomeClass', array(
   'DependencyClass' => $dependency
));

More stuff

So that’s a brief overview of what the library provides, but there’s heaps more stuff like how to tweak method injection, binding to interfaces, creating your own session handler for session scoped objects, etc. For full details check the main documentation.

Conclusion

This library provides a simple, lightweight, and fast way to architect and test your PHP application. It’s designed to be easily extensible and configurable, so hopefully you’ll find it useful in freeing yourself from dependency/singleton hell.

Grab the code from https://github.com/boxuk/boxuk-di and enjoy!

About the author

Box UK

Box UK

Box UK's team of simply brilliant thinkers, consultants and application developers mastermind simply brilliant solutions to the world's toughest, performance-critical web and software assignments.

Related content

Hacktoberfest 2015

By Ian Jenkins

We're hiring. Let's talk. View available roles