Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Dependency Injection and Reflection Library

branch: master
README.markdown

Box UK - Dependency Injection, Reflection and Annotations

The BoxUK-DI library enables you to easily handle dependency injection between components using annotations and type-hinting, similar to Guice and Spring.

Dependencies

  • PHP 5.3+
  • Addendum 0.4.0+

Including the Library

To include the library just include the bootstrap file in your application.

include '../path/to/boxuk-di/lib/bootstrap.php';

The Standard Injector

The main part of the library is the DI container, a simple example to fetch a class...

$libraryLoader = $injector->getClass( 'LibraryLoader' );

By default, the injector will create a new class each time it's asked for it. Its constructor parameters will be analysed to check types so that any dependencies can be injected into the new object (if these dependencies don't exist they will be created). It's methods will also be checked on creation for any that have been annotated for method injection (see below)

Scopes

By default, new objects will be instantiated for each requested class. To use objects in a different scope, singleton for example, just annotate them as so:

/**
 * @ScopeSingleton
 */
class LibraryLoader {
}

Available Scopes

Singleton

The singleton scope is defined by the annotation @ScopeSingleton, and lasts for the lifetime of a request. Objects annotated as singletons will only be created once by the injector, and then the same object is returned on each subsequent request.

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

Session

The session scope will store objects in the users session, and these will be available for the lifetime of the session. This can be used for things like a logged in user, a shopping cart, etc... To give a class session scope just annotate it as so.

/**
 * @ScopeSession
 */
class MyShoppingCart {}

To use this scope you will first need to define a class that implements the SessionHandler interface and bind it to this name (SessionHandler).

Interface Binding

If your class is implementing an interface which it is type hinted for then you can specify this by the above annotation:

/**
 * @ScopeSingleton(implements="SomeInterface")
 */
class MyClass implements SomeInterface, AnotherInterface {}

Then requests for that interface will return this singleton:

$oInjector->getClass( 'SomeInterface' );

3rd Party Singletons

To add 3rd party singletons to the injector just go through the getScope() method.

$this->injector->getScope( 'singleton' )->set( $doctrineManager );

Method Injection

You can also annotate methods to be injected:

/**
 * @InjectMethod
 */
public function setClassLoader( ClassLoader $oClassLoader ) {}

NB: When doing method injection there is no constraint on the name of the method, or the number of parameters injected.

Parameter Types

If your method requires tweaking the injected parameter types then you can specify these with another annotation:

/**
 * @InjectMethod
 * @InjectParam(variable="class", class="ModuleRegistry")
 */
public function setSomething( SomeInterface $class ) {
    // will receive a ModuleRegistry
}

This can also be used for constructors.

Property Injection

The final type of injection available is property injection. This can be used for public and private properties.

/**
 * @InjectProperty
 * @var SomeClass
 */
private $someClass;

The type of object injected is specified by the @var PHPDoc.

Inheritance

Methods

When doing method injection, the injector will ascend up the inheritance chain to also inject methods in parent classes. If you override a method in your child class though this method will only be injected (if annotated) in the child class.

Scopes

When checking a class for scope, the injector will ascend up the inheritance chain and stop at the first scope annotation it encounters.

Fetching New Classes

To ignore any scope annotations you can force fetching a new instance of the class you want:

$oInjector->getNewClass( 'SomeClass' );

Constructor Patterns

The one requirement of the injector is that type hinting or @InjectParam annotations need to be used to identify dependencies, so only classes can be dependencies. This makes a clean seperation between class dependencies and class configuration. For classes created with the injector you will not be able to pass in strings or arrays to the constructor. You can think of this as...

  1. Objects are dependencies
  2. Anything else is configuration

So you will need to remove any configuration from your constructors and injected methods, this will be moved to initialisation time for your class:

$class = $injector->getClass( 'MyClass' );
$class->initialise( $port, array( 'some', 'values' ) );

NB: initialise() here is just an arbitrary method on the class being created.

Using the Injector

So, your class has been injected with all it's dependencies, but what if you want to create more objects inside your class? Well just ask for the injector as one of your dependencies:


private $injector;

public function __construct( BoxUK\Inject\Injector $injector ) {
    $this->injector = $injector;
}

private function myMethod() {
    $class = $this->injector->getClass( 'SomethingElse' );
}

Don't use the injector as a service locator though inside your class, always specify your dependencies to be injected at construct time.

Inject Arbitrary Objects

The injector also provides an inject() method which can be used to do method injection and property injection on arbitrary objects. These objects can have been created elsewhere but the injector will scan them for dependencies to inject.

$injector->inject( $someObject );

The Helper

The easiest way to create an injector is to use the Helper class.

$helper = new BoxUK\Inject\Helper();
$injector = $helper->getInjector();

Configuration

When you create the helper you can pass in a Config object. This example shows a config object generated from an .ini file.

$config = new BoxUK\Inject\Config\IniFile();
$config->initFromFile( 'path/to/file.ini' );
$helper = new BoxUK\Inject\Helper( $config );

The injector will be all set up and ready to go. There are also methods to create reflectors and caches.

Reflection and Annotations

The second part of the library, which the injector is built on is the reflector. You can use this class to access reflection and annotation information on classes.

$reflector = $helper->getReflector();

Caching

Reflection can be slow, so for your applications production mode it's reccomended to use the BoxUK\Reflect\Caching reflector instead. You can get this through configuration.

boxuk.reflector = caching

The complete list of configuration options is as follows:

Setting Values Default
boxuk.reflector standard, caching standard
boxuk.reflector.cache file, memcache, apc file
boxuk.reflector.filecache.dir (path to cache directory) (sys_get_temp_dir())
boxuk.reflector.filecache.filename (name of cache file) $CLASS.cache
boxuk.reflector.memcache.host (memcache host) localhost
boxuk.reflector.memcache.port (memcache port) 11211
boxuk.reflector.memcache.key (memcache key) $CLASS
boxuk.reflector.apc.key (APC key) $CLASS

($CLASS means the fully qualified name of the class concerned)

Unit Testing

You can unit test these classes using:

phing test
Something went wrong with that request. Please try again.