granula/inversion

Inversion of Control Container for PHP

Installs: 61

Dependents: 1

Stars: 22

Watchers: 4

Forks: 2

Language: PHP

v3.0.1 2013-02-28 21:16 UTC

README

Inversion is a simple and powerful dependency injection container for PHP 5.3. Supports service-oriented architecture, links, PSR-0, and Composer. You can install it via packagist.org: granula/inversion or download and add to the PSR-0-compatible loader.

$container = new Inversion\Container();
$container['foo'] = 'My\Class\Foo';
//...
$foo = $container('foo');

In this example shown the basic functionality of the container. Look at what is happening there. The first line creates an instance of the container. In the second creates an association between «foo» and service creates an instance of «My\Class\Foo». That can be written as:

use Inversion\Service\Builder;

$container->addService('foo', new Builder('My\Class\Foo'));

In the third line, we obtain an instance of the object. That can be written as:

$foo = $container('foo');
// or
$foo = $container->get('foo');
// or
$foo = $container['foo']->get();
// or 
$foo = $container->getService('foo')->get();

However, I recommend using a shortened version, but all of them are valid.

Description Dependency

By default, when a string is passed to the container it is understood as the class name, and the puts to the service Inversion\Servise. This service has several features and functions. The first is a lazy loading. As long as you dont use it, the class is not loading. Second, you can specify a dependency from other services and parameters. Let me explain with an example. Suppose we have a class Bar, which depends on two classes One and Two:

namespace My\Space;
class One {}
class Two {}
class Bar
{
    public function __construct(One $one, Two $two) 
    {
    }
}

Describe this dependence in Inversion:

use Inversion\Service\Builder;
//...
$container['one'] = 'My\Space\One';
$container['two'] = 'My\Space\Two';
$container['bar'] = new Builder('My\Space\Bar', array($container['one'], $container['two']));

Now when you call the «bar», they will be created and substituted in the constructor. In fact, you can even easier. If, instead of «one» and «two» specify their class names:

$container['My\Space\One'] = 'My\Space\One';
$container['My\Space\Two'] = 'My\Space\Two';
$container['My\Space\Bar'] = new Builder('My\Space\Bar'); // "new Builder" can be omitted

This is a convenient way to describe the dependence when you are using interfaces:

namespace My\Space;
class One implements OneInterface {}
class Two implements TwoInterface  {}
class Bar implements BarInterface 
{
    public function __construct(OneInterface $one, TwoInterface $two) 
    {
    }
}
$container['My\Space\OneInterface'] = 'My\Space\One';
$container['My\Space\TwoInterface'] = 'My\Space\Two';
$container['My\Space\BarInterface'] = 'My\Space\Bar'; 

Other kinds of services

Inversion library have a few services, but you can create your own by implementing Inversion\ServiceInterface.

Closure

Class: Inversion\Service\Closure Usage:

$container['closure'] = function () use ($container) {
    return new My\Class();
};

You can also specify dependencies:

$container['closure'] = function (One $foo, Two $foo) use ($container) {
    return new My\Class();
};

Just as with Inversion\Service\Builder, you can specify them explicitly:

$container['closure'] = new Closure(function (One $foo, Two $foo) use ($container) {
    return new My\Class();
}, array($container['one'], $container['two']));

Factory

Class: Inversion\Service\Factory Usage:

$container['factory'] = new Factory('My\ClassFactory', 'create');

As with Inversion\Service\Builder, you can specify dependency manually.

Object

Class: Inversion\Service\Object Usage:

$container['object'] = new My\Class();
// or
$container['object'] = new Object(new My\Class());

Store

Class: Inversion\Service\Store Usage:

$container['data'] = new Store('what you want');

By default, all arrays are converted to Store Services.

$container['data'] = array(...);

Equivalent to:

$container['data'] = new Store(array(...));

Links to services

Inversion support references. To get a link, use the container as an array:

$container['foo'] = new Builder(...);

$ref = $container['foo'];  // Reference to the service.

So you can create an alias to any service:

$container['My\Class\FooInterface'] = new Builder('My\Class\Foo');
$container['foo'] = $container['My\Class\FooInterface']; 
//...
$foo = $container('foo');

Now if someone overwrite «My\Class\FooInterface», then «foo» will continue to refer to this service:

//...
$container['My\Class\FooInterface'] = new Builder('Another\FooImpl');
//...
$foo = $container('foo'); // $foo instanceof Another\FooImpl

You can even create links to the links:

$container['foo'] = 'My\Class\Foo';
$container['ref'] = $container['foo']; 
$container['ref2'] = $container['ref']; 
$container['ref3'] = $container['ref2'];
//...
$foo = $container('ref3'); // $foo instanceof My\Class\Foo
$name = $container->getRealName('ref3'); // $name == 'foo'

Extension of services

For example, if you want to extend any service, then this method will not work because it overwrites the first one:

$container['My\Class\FooInterface'] = 'My\Class\Foo';
//...
$container['My\Class\FooInterface'] = function (FooInterface $foo) {
    $foo->extendSome(...);
    return $foo;
};

This will result in an infinite loop. To prevent this, use the following function:

$container['My\Class\FooInterface'] = 'My\Class\Foo';
//...
$container['extended'] = $container->extend('My\Class\FooInterface', function (FooInterface $foo) {
    return new FooDecorator($foo);
});

Share and Protect services

By default all services is sharing the their result. Look at an example:

$container['i'] = function () {
    static $i = 0;
    return ++$i;
};

$a = $container('i'); // $a == 1
$b = $container('i'); // $b == 1 still.

If you want to call service every time use protect method:

$container['i'] = $container->protect(function () {
    static $i = 0;
    return ++$i;
});

$a = $container('i'); // $a == 1
$b = $container('i'); // $b == 2

You can create share service from protect service by "share" method:

$container['share'] = $container->share($someProtectService);

If you want to create your own share service class implement ShareableInterface.

Tests

Inversion library fully tested. Tests are in a separate repository granula/test, along with other tests of granula.

As Singleton

Inversion is designed entirely without use of static methods and singletons, but rarely useful to have a container as a singleton:

$container = Inversion\Container::getInstance();

License

Inversion is licensed under the MIT license.