Data Manager as an IoC Container
On top of being a powerful data-manager, Manager
can be used as a flexible IoC container.
For more information about IoC and dependency injection see Php The Right Way
Manager can:
* Resolve dependencies from classnames, closures, already instantiated objects, and instances of Manager itself,
* Create and Resolve Singletons
* Configure dependencies for dependencies. This can be done a number of ways and to any depth.
* Allow for fallbacks, if you get()
a dependency that doesn't exist.
* Send any object through a closure so you can prepare objects globally.
Please note that this overrides some of the features of ManagesItemsTrait
. While you an use this to store regular values,
you MUST use getRaw($alias, $fallback)
to retrieve a value. Otherwise it will try to become a dependency and break.
Also, the previous versions used di()
and fetch()
. These are still available, but deprecated.
Setup
The easiest way is to $manager = new \Michaels\Manager\IocManager
.
If you want use the traits, you must resolve some conflicts.
class IocManager
{
use ManagesItemsTrait, ManagesIocTrait {
ManagesIocTrait::add insteadof ManagesItemsTrait;
ManagesIocTrait::get insteadof ManagesItemsTrait;
}
NOTE THAT ManagesIocTrait
depend on ManagesItemsTrait.
If you to use ManagesIocTrait
without ManagesItemsTrait
, you will get all sorts of errors.
The IoC container is inspired, mostly, by Pimple
Basic Usage
Now you can setup dependencies.
/* Setup a dependency using a classname */
$manager->add('event_dispatcher', 'Full\Class\Here');
/* Setup a dependency using a factory (closure) for lazy loading */
$manager->add('event_dispatcher', function ($di) {
// you have access to the container through $di
return new WhateverObject($di->get('another_dependency'));
});
/* Setup a dependency using an object for eager loading */
$manager->add('event_dispatcher', new WhateverObject($manager->get('another_dependency'));
/* Setup a dependency using an instance of the IoC container itself */
$manager->add('cool_eventer', $myCoolEventer);
$manager->add('event_dispatcher', $manager);
// Which will call `get('event_dispatcher') on the manager you passed and return what it returns.
When you're ready to call dependencies:
$manager->get('event_dispatcher');
You may also register multiple aliases to a single dependency
$manager->add(['one', 'two', 'three'], $factory);
$manager->get('one');
$manager->get('two');
$manager->get('three'); // All the same
And, just ask for a class. If it exists (and nothing by that name was registered), it will be loaded.
$manager->get('Some/Class')
Dependencies that need Dependencies
The easiest way to setup a dependency that needs a dependency is to use a closure.
$manager->add('email', 'Some\Email\Class');
$manager->add('logger', function ($di) {
return new Logger($di->get('email'));
});
$manager->add('application', function ($di) {
return new Application($di->get('logger'));
});
Now an Application
will have a Logger
and a Logger
will have an Email
Fallbacks
By default, if you get an item that does not exist, you will get an ItemNotFoundException
.
If you want a fallback, simply $manager->get('doesnt_exist', $fallback);
Using Singletons
By default, every time you get()
an item, it will return a new instance of that item (unless the item is an object).
If you want a singleton (that is return the same one each time):
$events->share('event_dispatcher');
$singleton = $events->get('event_dispatcher');
Prepare an object after created but before returned
It is also possible to pass a dependency through some sort of pipeline that will alter the object before returned.
$manager->setup('event_dispatcher', function ($dispatcher, $manager) {
// Do whatever you want to $dispatcher and then return it
});
Note that fallbacks are not sent through the pipeline.
Also note that you must setup pipelines BEFORE you get()
the first instance of a share()
d dependency.
Setting Dependencies Implicitly
You can tell Manager how to configure a certain dependency when you register it. For the example, let's assume:
$manager->add('one', new One());
$manager->add('two', new Two());
Setting up using a classname
$manager->add('event_dispatcher', 'My\Event\Dispatcher', ['one', 'two']);
Will pass a new One()
and new Two()
into Dispatcher($one, $two)
Setting up using a closure
$manager->add('event_dispatcher', function ($di, $one, $two) {
}, ['one', 'two']);
Will pass a new One()
and new Two()
into the closure.
Setting up using an object
$manager->add('event_dispatcher', $object, ['one', 'two']);
Will pass a new One()
and new Two()
into the needs()
method of $object
if it exists.
If you pass a value that is not a registered dependency, then the value itself is passed.
NOTE: For the moment, you cannot prepare dependencies that are instances of containers.