Inversion of Control

Stubbles provides a very simple-to-use but still powerful inversion of control container, which supports constructor and setter based dependency injection. The IoC container of Stubbles is modeled after Google Guice and makes use of type hinting and Stubbles' annotation features. If you've never heard of type hinting or annotations, you should at first read the sections on these two topics:

The example code

Imagine, you are building a car configurator. To follow the rules of good design, you define interfaces for all components of a car and provide several classes that implement these components.

The interfaces in you application include:

<?php
interface Car {
    public function moveForward($miles);
}
interface Person {
    public function sayHello();
}
interface Tire {
    public function rotate();
}
interface Engine {
    public function start();
}
?>

The implementations are:

<?php
class BMW implements Car {
    protected $driver;
    protected $engine;
    protected $tire;

    public function __construct(Engine $engine, Tire $tire) {
        $this->engine = $engine;
        $this->tire = $tire;
    }
    public function setDriver(Person $driver) {
        $this->driver = $driver;
    }
    public function moveForward($miles) {
        $this->driver->sayHello();
        $this->engine->start();
        $this->tire->rotate();
    }
}

class Schst implements Person {
    public function sayHello() {
        echo "My name is Stephan\n";
    }
}

class Goodyear implements Tire {
    public function rotate() {
        echo "Rotating Goodyear tire\n";
    }
}

class TwoLitresEngine implements Engine {
    public function start() {
        echo "Starting 2l engine\n";
    }
}
?>

Without the dependency injection framework

To create a new instance of an implementation of Car the following code is required:

<?php
$tire   = new Goodyear();
$engine = new TwoLitresEngine();
$schst  = new Schst();

$bmw    = new BMW($engine, $tire);
$bmw->setDriver($schst);

$bmw->moveForward(50);
?>

Creating objects manually like this has several drawbacks:

  • Your application is bound to the concrete implementations instead of the interfaces
  • Changing the implementation means changing existing code, which might break it
  • The creation of objects is scattered throughout your application

Enter 'Inversion of Control'

Stubbles tries to solve these problems, by providing functionality to handle all dependency injections for you. This keeps your application clean of boilerplate code, which is only needed to construct complex objects.

Furthermore, it allows you to centralize and/or modularize the definition of the concrete implementations for your interfaces or abstract types.

A simple example

To define the concrete implementations is done using an instance of stubBinder:

<?php
// make sure, the IoC functionality is loaded
stubClassLoader::load('net::stubbles::ioc::ioc');

$binder = new stubBinder();
$binder->bind('Car')->to('BMW');
$binder->bind('Tire')->to('Goodyear');
$binder->bind('Person')->to('Schst');
$binder->bind('Engine')->to('TwoLitresEngine');
?>

In this short code snippet, you bound the interfaces from the example above to their concrete implementations.

If you now need an instance of the engine, you use the binder to create a stubInjector, which can be used to create the desired Engine:

<?php
$injector = $binder->getInjector();
$engine = $injector->getInstance('Engine');
var_dump($engine);
?>

This code snippet will now display:

object(TwoLitresEngine)#48 (0) {
}

As desired, it created an instance of the concrete implementation, that you bound to the interface.

Adding dependency injection

Next, you probably want to get an instance of Car using the same approach:

<?php
$injector = $binder->getInjector();
$car = $injector->getInstance('Car');
var_dump($engine);
?>

This will result in the following error:

Catchable fatal error: Argument 1 passed to BMW::__construct() must implement interface Engine, none given in path/to/code.php on line 13

This had to happen, because the constructor of the BMW class requires you to pass an instance of Tire and Engine and Stubbles does not know, that it should do this. But it is very easy to tell Stubbles, that it should inject dependencies into the constructor, by just adding an @Inject annotation. You only need to modify the sourcecode for the class BMW a little bit:

<?php
class BMW implements Car {
    protected $driver;
    protected $engine;
    protected $tire;

   /**
    * @Inject
    */
    public function __construct(Engine $engine, Tire $tire) {
        $this->engine = $engine;
        $this->tire = $tire;
    }
    public function setDriver(Person $driver) {
        $this->driver = $driver;
    }
    public function moveForward($miles) {
        $this->driver->sayHello();
        $this->engine->start();
        $this->tire->rotate();
    }
}
?>

After you modified the class, just run the example again and you get the following output:

object(BMW)#33 (3) {
  ["driver:protected"]=>
  NULL
  ["engine:protected"]=>
  object(TwoLitresEngine)#37 (0) {
  }
  ["tire:protected"]=>
  object(Goodyear)#40 (0) {
  }
}

Stubbles created a new instance of BMW, as you bound it to Car, and as the constructor of BMW requires a Tire and an Engine instance, it created these instances as well. To determine the concrete classes to use, Stubbles used the bindings you defined in the stubBinder instance.

What you also can see is, that Stubbles did not inject an object into the $driver property, although you specified a binding for Person. Stubbles will never inject any dependencies, unless you annotate the constructor or setter method with @Inject. All that you need to do to inject a driver is to annotate the setDriver() method:

<?php
class BMW implements Car {
    protected $driver;
    protected $engine;
    protected $tire;

   /**
    * @Inject
    */
    public function __construct(Engine $engine, Tire $tire) {
        $this->engine = $engine;
        $this->tire = $tire;
    }
   /**
    * @Inject
    */
    public function setDriver(Person $driver) {
        $this->driver = $driver;
    }
    public function moveForward($miles) {
        $this->driver->sayHello();
        $this->engine->start();
        $this->tire->rotate();
    }
}
?>

If you re-run this example, all properties will be set according to your bindings.

Optional injection

Probably you do not want to inject an object every time, because the class will work fine without the dependency. It is possible to mark an injection as optional:

<?php
class BMWWithCoDriver extends BMW {
    protected $codriver;

   /**
    * @Inject(optional=true)
    */
    public function setCoDriver(CoDriver $codriver) {
        $this->codriver = $codriver;
    }
    
    public function moveForward($miles) {
        if (null !== $this->codriver) {
            $this->codriver->sayHello();
        }
        
        parent::moveForward($miles);
    }
}
?>

If the injection would not be marked as optional and no binding would be defined for CoDriver retrieving the instance of BMWWithCoDriver would result in a stubBindingException. But by marking the injection as optional there will be no exception thrown and the instance of BMWWithCoDriver will be created without setting the codriver property.

Implicit bindings

Stubbles does not force you to use interfaces in your type hints. If you are already using concrete classes, there is no need to bind them, as Stubbles will implicitly bind the concrete class to itself:

<?php
class Window {}

class BMW implements Car {
    protected $driver;
    protected $engine;
    protected $tire;
    protected $window;

   // same constructor and methods as in previous examples

   /**
    * @Inject
    */
    public function setWindow(Window $win) {
        $this->window = $win;
    }
}
?>

When creating an instance of BMW, it will automatically have a reference to an instance of Window although no special binding has been added.

Default implementations

Very often, you only use one concrete implementation of an interface in your application and only added the interface or abstract class to make it testable. To avoid having to bind all of your interfaces to the concrete implementations, you may specify a default implementation, which will be used, if no explicit binding has been set. To achieve this, add the @ImplementedBy annotation to your interface.

<?php
/**
 * All Persons should be bound to the class Schst
 * unless Person is bound
 *
 * @ImplementedBy(Schst.class)
 */
interface Person {
    public function sayHello();
}
?>

You may also specify the fully qualified class name, Stubbles will then load the class, if it's not already loaded.

Working with Singletons

In the above example, multiple calls to $injector->getInstance('Car'); will return different objects. In most cases, this is probably what you want, as the IoC framework behaves like the new operator. If you want to create only one instance of the BMW class, you can easily convert the BMW class to a singleton.

<?php
$binder = new stubBinder();
$binder->bind('Car')->to('BMW')->in(stubBindingScopes::$SINGLETON);
// other bindings

$injector = $binder->getInjector();
$bmw1 = $injector->getInstance('Car');
$bmw2 = $injector->getInstance('Car');

if ($bmw1 === $bmw2) {
    echo "Same object.\n";
}
?>

You may specify a scope for each binding using the in() method. In the above example, the $SINGLETON scope has been used. This makes sure, that the instance is created only once and subsequent calls to getInstance() will return the same instance.

It is also possible to create your own scope. You only need to write a new class that implementes the stubBindingScope interface. For instance, this would allow you to create a scope so that a class is only instantiated once per session.

Another way to treat a class as a singleton is using the @Singleton annotation, which is used to annotate the class. The following example makes sure, that the application uses only one instance of the class Schst:

<?php
/**
 * @Singleton
 */
class Schst implements Person {
    public function sayHello() {
        echo "My name is Stephan\n";
    }
}
?>

The following code will now create two instances of the class BMW, but both should have a reference to the same Schst instance:

<?php
$binder = new stubBinder();
$binder->bind('Car')->to('BMW');
// other bindings

$injector = $binder->getInjector();
$bmw1 = $injector->getInstance('Car');
$bmw2 = $injector->getInstance('Car');

var_dump($bmw1);
var_dump($bmw1);
?>

If you run the code snippet, you get the following output:

object(BMW)#34 (3) {
  ["driver:protected"]=>
  object(Schst)#50 (0) {
  }
  ["engine:protected"]=>
  object(TwoLitresEngine)#38 (0) {
  }
  ["tire:protected"]=>
  object(Goodyear)#41 (0) {
  }
}
object(BMW)#30 (3) {
  ["driver:protected"]=>
  object(Schst)#50 (0) {
  }
  ["engine:protected"]=>
  object(TwoLitresEngine)#44 (0) {
  }
  ["tire:protected"]=>
  object(Goodyear)#39 (0) {
  }
}

As you can see, the two BMW instances have different object handles (#30 and #34), but the $driver properties point to the same Schst instance (object handle #50).

Implementing the singleton pattern never has been this easy.

The session scope

Similarly to the singleton scope there exists a session scope. Contrary to the singleton scope which keeps one instance of a class but recreates this instance on every request, the session scope keeps one instance of a class per session - the instance is only created on its first instantiation and then stored in the session. On the next request the same instance will be taken from the session.

<?php
$binder = new stubBinder();
$binder->bind('Car')->to('BMW')->in(stubBindingScopes::$SESSION);
// other bindings

$injector = $binder->getInjector();
$bmw1 = $injector->getInstance('Car');
$bmw2 = $injector->getInstance('Car');
?>

Now, $bwm1 and $bmw2 reference the same instance which is stored in the session. If the above code snippet is executed on the next request, both variables would contain the same instance as in the first request.

Please bear in mind that the session scope must know the session. This can be done in two ways: first by explicitly setting the session instance:

stubBindingScopes::$SESSION->setSession($mySession);

The other (and preferred) way is to use the net::stubbles::ioc::stubIOCPreInterceptor as explained below, which takes care of injecting the session into the scope.

Naming instances

For the next example, you have to modify the used classes a little bit:

  1. Add a new class Mikey which implements the Person interface
  2. Add a new property $coDriver and the matching setter-method to the BMW class
<?php
class BMW implements Car {
    protected $driver;
    protected $coDriver;
    protected $engine;
    protected $tire;
    // existing methods left out...
    /**
     * @Inject
     */
    public function setCoDriver(Person $coDriver) {
        $this->coDriver = $coDriver;
    }
}

class Mikey implements Person {
    public function sayHello() {
        echo "My name is Frank\n";
    }
}
// existing classes left out
?>

If you now create an instance of BMW, you will get the following object structure:

object(BMW)#35 (4) {
  ["driver:protected"]=>
  object(Schst)#51 (0) {
  }
  ["coDriver:protected"]=>
  object(Schst)#51 (0) {
  }
  ["engine:protected"]=>
  object(TwoLitresEngine)#39 (0) {
  }
  ["tire:protected"]=>
  object(Goodyear)#42 (0) {
  }
}

The properties $driver and $coDriver both contain references to the instance of Schst as both setter methods require an instance that implements the Person interface. In real-life, you would probably be able to inject a different co-driver, but until now, an interface could only be bound to one implementation. This can be changed using the @Named annotation.

Again, you need to modify the BMW class a little bit:

<?php
class BMW implements Car {
    protected $driver;
    protected $coDriver;
    protected $engine;
    protected $tire;
    // existing methods left out...
    /**
     * @Inject
     * @Named('Co-Driver')
     */
    public function setCoDriver(Person $coDriver) {
        $this->coDriver = $coDriver;
    }
}
?>

By adding the @Named('Co-Driver') annotation, you gave Stubbles the possibility to distinguish the Person instance passed to setCoDriver from all other Person instances. You may now specify a separate binding for this instance:

<?php
$binder->bind('Person')->to('Schst');
$binder->bind('Person')->named('Co-Driver')->to('Mikey');
// other bindings

$injector = $binder->getInjector();
$bmw = $injector->getInstance('Car');
var_dump($bmw);
?>

Now, the $injector will return the following object structure:

object(BMW)#34 (4) {
  ["driver:protected"]=>
  object(Schst)#50 (0) {
  }
  ["coDriver:protected"]=>
  object(Mikey)#57 (0) {
  }
  ["engine:protected"]=>
  object(TwoLitresEngine)#38 (0) {
  }
  ["tire:protected"]=>
  object(Goodyear)#41 (0) {
  }
}

As desired, Stubbles created an instance of the new class Mikey and injected it using the stoCoDriver() method. You may use as many named bindings for one type as you like and combine it with all other features like scoping.

WARNING: Naming instances currently only works with setter-injection.

Injecting constant values

Until now, you used the Stubbles IoC framework only to inject objects. However, it is also possible to inject constant values into objects. The following example shows, how this can be done. Imagine, you have a class, that requires the connection parameters for a database:

<?php
class MyApplication {
    protected $dbParams;

    /**
     * @Inject
     * @Named('dbParams')
     */
    public function setDbParams($params) {
        $this->dbParams = $params;
    }
    // other methods of the class
}
?>

As in the examples before, you annotated the method that should be used to inject the database connection parameters with the @Inject and the @Named annotation. When injecting non-objects, the @Named annotation is required, as there is no typehint to identify the binding.

Now, all that's left to do is specify a binding for the contstant dbParams:

<?php
// make sure, the IoC functionality is loaded
stubClassLoader::load('net::stubbles::ioc::ioc');

$binder = new stubBinder();
$binder->bindConstant()->named('dbParams')->to('mysql:host=localhost;dbname=test');

$injector = $binder->getInjector();
$app = $injector->getInstance('MyApplication');
?>

The $injector will now return a configured instance of MyApplication with the $dbParams property set.

The Stubbles IoC framework is not meant to replace your own configuration framework, but in some cases, injecting constant values can be helpful or might even be a requirement for your application to work:

WARNING: Injecting constant values currently only works with setter-injection.

Injecting instances

In some cases you might need to inject a dependency, that is not managed by the IoC framework of Stubbles, but created by your own code. The last two features help you solve this problem.

Instead of binding a type to a concrete implementation, you can always bind it to an existing instance. In the following example, you already have an instance of the class Schst created and you want to inject this into the BMW instance, instead of letting the IoC framework create a new instance:

<?php
$schst = new Schst();

$binder->bind('Person')->toInstance($schst);
// other bindings

$injector = $binder->getInjector();
$bmw = $injector->getInstance('Car');

var_dump($schst);
var_dump($bmw);
?>

Instead of using the to() method to specify the binding, you only need to call toInstance() and pass the object to use for the binding. The result of this script is:

object(Schst)#14 (0) {
}
object(BMW)#38 (4) {
  ["driver:protected"]=>
  object(Schst)#14 (0) {
  }
  ["coDriver:protected"]=>
  object(Mikey)#55 (0) {
  }
  ["engine:protected"]=>
  object(TwoLitresEngine)#42 (0) {
  }
  ["tire:protected"]=>
  object(Goodyear)#45 (0) {
  }
}

As you can see, the BMW instance contains a reference to the Schst instance you created, there object handle is #14.

Using custom providers

In some rare cases, there are dependencies, that should not or could not be created by the Stubbles IoC framework, as they are complex to create. One example could be database connection objects, like PDO instances. In some cases you might already have a factory, which creates and configures objects for you and you do not want to completely switch to the IoC container but integrate your factory so it can provide the objects for the container.

By default, an instance of stubDefaultInjectionProvider is created for every binding you add to the binder. This provider creates depending objects using the same injector that you called getInstance() on. To hook into the creation of the dependencies, you may write you own custom provider by implementing the stubInjectionProvider interface. This interface requires you only to implement one method:

<?php
interface stubInjectionProvider extends stubObject {
    public function get($type, $name = null);
}
?>

The following example shows, how to use this feature to inject a PDO instance:

<?php
class MyApplication {
    protected $pdo;
    /**
     * @Inject
     */
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }
}
?>

The class MyApplication requires an instance of PDO. If you take a look at the PDO documentation, you will see, that the constructor requires parameters for the connection and the username and password. As PDO is an internal class, you cannot add the @Inject and @Named annotation and thus, the Stubbles IoC container is not able to create this object.

The solution is to implement a provider, which creates the PDO instance:

<?php
/**
 * Provider to create PDO instances
 */ 
class PDOProvider extends stubBaseObject implements stubInjectionProvider {

    public function get($type, $name = null) {
        // get the connection parameters from any source
        $connection = MyRegistry::get('pdoConnection');
        $user       = MyRegistry::get('pdoUser');
        $pass       = MyRegistry::get('pdoPassword');
        // create the object
        return new PDO($connection, $user, $pass);
    }
}
?>

This provider can now be bound to the type PDO and will be used to create all PDO instances:

<?php
$binder = new stubBinder();

// create an instance of the provider and use it for the bindings
$provider = new PDOProvider();
$binder->bind('PDO')->toProvider($provider);

$injector = $binder->getInjector();
$app = $injector->getInstance('MyApplication');
?>

When creating the MyApplication instance, the injector requires to create an instance of the class PDO. This type has been bound to a provider (PDOProvider) and thus, the injector delegates the creation of the PDO instance to this provider object. In this object, you can do whatever is needed to create the actual object. In the example, a registry is used to fetch the connection parameters for the PDO connection object.

IoC in a Stubbles MVC application

Stubbles provides an interceptor, that you can use in your Stubbles MVC application, which will create a stubBinder, create some default bindings and adds the binder to the registry. To use this interceptor, add the following code to the interceptors.xml file:

<?xml version="1.0" encoding="iso-8859-1"?>
<xj:configuration
    xmlns:xj="http://xjconf.net/XJConf"
    xmlns="http://stubbles.net/ipo/interceptors">
  <preInterceptors>
    <preInterceptor type="net::stubbles::ioc::stubIOCPreInterceptor" />
    ...
  </preInterceptors>
  <postInterceptors>
    ..
  </postInterceptors>
</xj:configuration>

This interceptor currently creates bindings for the following types:

  • stubRequest
  • stubResponse
  • stubSession

To make use of this binder in you application, you can easily access it using the registry:

<?php
$binder = stubRegistry::get('net.stubbles.ioc.stubBinder'); // alternatively use $binder = stubRegistry::get(stubBinder::REGISTRY_KEY);
?>

The binder is already used in various places, e.g. the instances of the service objects in the JsonRpcProcessor are created using the IoC framework and thus can easily access the session instance.

To add some bindings, we recommend writing your own pre-interceptor, which extends the stubAbstractIOCPreInterceptor and implements the configure() method:

<?php
/**
 * Pre-interceptor for application specific bindings
 *
 * @author      Your Name
 */
stubClassLoader::load('net::stubbles::ioc::stubAbstractIOCPreInterceptor');

/**
 * Pre-interceptor for application specific bindings
 */
class myIOCPreInterceptor extends stubAbstractIOCPreInterceptor
{
    /**
     * Configure the bindings
     *
     * @param  stubBinder    $binder    the binder to configure
     */
    public function configure(stubBinder $binder)
    {
        $binder->bind('Foo')->to('FooImpl');
        // more bindings should follow here
    }
}
?>

Drawbacks

The Stubbles IoC container is very useful until you need to create various instances of the same class that have different dependencies. In this case you had to create the bindings from scratch for each of the objects which is not how the container is intended to be used.

For these cases, we recommend the use of XJConf for PHP which is tightly integrated into Stubbles.