Not My Idea

Carnets Web d'Alexis Métaireau

Dependency Injection – Using SpiralDi Container

2 comments

Please note that this article is also available in French. A big thanks to Frédéric Sureau for his translation work on the English version.

Since my previous article (FR), I have kept on working on the dependencies injector of Spiral, wich I just published in a standalone version.

Here is an overview of SpiralDi functionnalities, and some examples on the use.

Table des matières

Overview

SpiralDi comes with a lot of new features, some of them inspired by the excellent Java framework : Spring. It is now possible to :

  • Use factories to set the parameters values (useful for configuration)
  • Use an inheritance system to make configuration file more clear
  • Use factories for the construction of the services
  • Directly inject the container in ContainerAware services

But, maybe, it doesn’t mean anything for you ? I will explain the details further later.

Principles

If you don’t know much about the inversion of control, I advise you to read my introduction on the dependencies injection (FR).

The use of a lightweight container is done in two steps :

  • The description of the schema of all the services that will be used later
  • The call (so, the construction) of these services via the container

The SpiralDi library let you describe the Schema in different ways. The sexiest is probably the XML description file, but it is also possible to define this Schema using the PHP language if you think it’s more convenient. The following examples are in XML. Here is a quite simple definition file, that describe two services :

< ?xml version="1.0" encoding="UTF-8"?>
<container>
    <service name="db" class="Database">
        <constructor>
            <argument value="localhost"/>
            <argument value="root"/>
            <argument value="password"/>
        </constructor>
    </service>
    <service name="user" class="User">
        <method name="setDb">
            <argument type="service" value="db"/>
        </method>
    </service>
</container>

Once this file is written, you have to transform it in a SpiralDi understandable format : the Schema.

$builder = new SpiralDi_SchemaBuilder_Xml();
$builder->setFileName("schema.xml");
$schema = $builder->buildSchema();

You can now exploit the built Schema. The Schema is first used to resolve objects. We will see later that it has much more aims. Now, pass this Schema to the Container :

// construction of the container
$container = new SpiralDi_Container_Default($schema);

// getting the desired service
$myUser = $container->user;

This looks sexy, isn’t it ? The container has done everything ! Resolution of the object’s dependencies and injection of these dependencies when needed.

Services scope

All services constructed by SpiralDi have a controlled life time.
In the default behavior, the container will return the same instance of the object, each time the service is needed. This is the singleton scope.
All services are by default configured to have the singleton scope.

You can also use the prototype scope to make the container create a new instance each time the service is needed.

To change the scope of the service, you can use the property

scope="prototype"

in the XML file.

Advanced use

Now, you know how to describe « basic»  services and their dependencies. Let’s see now, more complex features.

Using references and MethodFactory

Sometimes, you will need to inject parameters in a dynamic manner. Imagine that you don’t know the value of a parameter that you want to inject because it is stored in a configuration object for example.

So, we want the container to construct our services using information from a Config object. The container is able to generate automatically the following code for us :

$config = new Config();
$service = new Db($config->host, $config->user, $config->password);

For this, we have to use the system of references :

<service name="db" class="Database">
    <constructor>
        <argument ref="config" value="host"/>
        <argument ref="config" value="user"/>
        <argument ref="config" value="password"/>
    </constructor>
</service>

It is also possible to specify the method that will be used to get arguments. For example

getParam()

will produce something like this :

$config = new Config();
$service = new Db(
    $config->getParam("host"),
    $config->getParam("user"),
    $config->getParam("password")
);

The corresponding XML code will be :

<argument ref="config" value="host" factoryMethod="getParam"/>

Inheriting and overloading services

Quite often, some services are similar. It is possible to use the inheritance system to make the services description lighter.
This is not an actual inheritance between classes, but only the inheritance principle. Thus, you can extend a service description, which make the description file more readable and easier to understand.

<service name="extendedService" extends="config">
    <method name="method2">
        <argument value="value"/>
    </method>
</service>

Container Aware

In some cases, services have to know the existence of the container, and need to access to it. It is easy to inject the container in a service by using the « container»  type or the facility « containerAware»  :

<service name="containerAwareService" class="Service" containerAware="true"/>
<!-- is exactly the same as -->
<service name="containerAwareService" class="Service">
    <method name="setDiContainer">
        <argument type="container" />
    </method>
</service>

From the class’ point of view, you only have to implement the SpiralDi_ContainerAware interface. In this way, during the construction of the service, the setDiContainer method of your class will be automatically called.

As well, in this case, there is no need to explicitly name your service as « ContainerAware»  in the description file, the container will automatically detect it

Factories

SpiralDi also provides a convenient way to use Factories from the DI. This helps generating behaviors like :

$service = MyServiceFactory::createService();

The corresponding XML code is :

<service name="serviceFactory" type="factory" class="MyServiceFactory">
    <method name="createService" />
</service>

This should be nice if you want to reuse your old Singleton implementations, among other things…

<service name="singleton" type="factory" class="Mysingleton">
    <method name="getInstance" />
</service>

Download it !

Convinced ? To get the latest version of SpiralDi, you have many solutions :

Via the mercurial repository

hg clone https://ametaireau@bitbucket.org/ametaireau/spiraldi/

Via the archives

The code is available in the following formats : zip, gz ou bz2

You can also directly check the repository.

That’s all ! You can now have fun using it ! For functionnality requests, bug report, etc. Please !

Interesting readings

If you want to know more about dependencies injection, here are some interesting readings on the subject :

Going further ?

SpiralDi is of course not the only PHP lightweight container. Recently, a lot of container are flowering on the web. Frameworks like Symfony and Flow31 are giving a good place to DI. So much the better !

  1. It is new, beautiful, written for PHP 5.3, using AOP, Domain Model persistence… and of course dependencies injection ! Keep an eye on it : Flow3

Written by Alexis Metaireau

juin 23rd, 2009 at 11:10

2 Responses to 'Dependency Injection – Using SpiralDi Container'

Subscribe to comments with RSS or TrackBack to 'Dependency Injection – Using SpiralDi Container'.

  1. [...] Please note that this article is also available in French . A big thanks to Frédéric Sureau for his translation work on the English version. Since my previous article (FR) , I have kept on working on the dependencies injector of Spiral , wich I just published in a standalone version Original post: Dependency Injection – Using SpiralDi Container [...]

  2. Moi j’aime pas Frédéric Sureau. En plus il a un nom d’arbuste.

    Quentin

    3 déc 09 at 1:01

Leave a Reply