Dependency Injection & Reflection

Pete Withers-Jones

on 29-11-10

Download the code from GitHub: Current version 1.4.2

Dependency Injection configuration

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!

Talk to one of our digital experts

Tom Houdmont

Head of Business Solutions

Do you have an idea or a project you need support with?

Tom leads Box UK’s Business Solutions team and has over 15 years experience in the web industry.  Tom is passionate about creating impactful solutions that solve real problems and deliver the outcomes our clients need.

Or call us on 020 8098 2093

1. 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' );

2. 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 ) { ... }

3. 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.

4. 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!

5. 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.

6. Reflection

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.

7. 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!

Pete Withers-Jones

Head of Development

Pete Withers-Jones leads Box UK’s Development Practice, and has over 20 years of experience in software development, working across various industries and technologies.

Subscribe now and get our expert articles straight to your inbox!

"*" indicates required fields

Privacy*
This field is for validation purposes and should be left unchanged.

Have a project you’d like to discuss?

Give us a call on 020 8098 2093 or fill in the form and we will get back to you.

This field is for validation purposes and should be left unchanged.