Page 1 of 1
inverted dependency injection
Posted: Wed Oct 08, 2008 1:22 pm
by koen.h
I hope someone can comment on the following style of dependency injection:
Code: Select all
interface IObjectLoader {
public function load($name);
public function into($object);
public function using($method);
public function addObject($objectOrClass, $alias);
public function addInstanceArgs($name, array $args);
}
// usage:
$loader->addObject('MyFramework_User', 'user');
$loader->addInstanceArgs('user', array($session, $auth));
class LoginController {
private $loader;
public function __construct(IObjectLoader $loader) {
$this->loader = $loader;
}
public function setUser(User $user) {
$this->user = $user;
}
public function loginAction() {
$this->loader->load('user')->into($this)->using('setUser');
$this->user->authenticate($username, $password);
// etc
}
}
Re: inverted dependency injection
Posted: Wed Oct 08, 2008 2:06 pm
by Christopher
Well I think it brilliant, but that is almost exactly how Skeleton Framework does it.

The Skeleton 'Locator' is a Registry + Loader that has DI functionality like you show. And since it uses itself to load dependencies you can chain them, so a Model can load a dependency on an ActiveRecord class, will in turn load a dependency on a DB Connection class.
In addition, the Skeleton Front Controller also has a plug-in you can use to inject dependencies in to Action Controllers.
Re: inverted dependency injection
Posted: Wed Oct 08, 2008 3:06 pm
by koen.h
arborint wrote:Well I think it brilliant, but that is almost exactly how Skeleton Framework does it.

The Skeleton 'Locator' is a Registry + Loader that has DI functionality like you show. And since it uses itself to load dependencies you can chain them, so a Model can load a dependency on an ActiveRecord class, will in turn load a dependency on a DB Connection class.
In addition, the Skeleton Front Controller also has a plug-in you can use to inject dependencies in to Action Controllers.
Funny thing is, in developing ideas I looked at skeleton and thought you were doing it all wrong

Maybe I haven't looked good enough. The lack of type hinting was probably what made me stop looking further. I'm going to look further. I now remember too I saw the term "registry" used so I probably came to conclusions too quick.
Re: inverted dependency injection
Posted: Wed Oct 08, 2008 3:25 pm
by koen.h
Looked at your class again. The big difference for me is that you set an object, ask it back and get something. There's no way an object knows what class or interface it is. That's why I disallowed using "get($name)" as a public method (though I use it privately) and only allow it to be set using a method. I see that a user can simply do this without any typehint in the setter. And with simply getting the object like you have it you could still pass it through the setter manually. So maybe I'm too optimistic about what I tried to accomplish.
Re: inverted dependency injection
Posted: Wed Oct 08, 2008 4:06 pm
by Christopher
koen.h wrote:Looked at your class again. The big difference for me is that you set an object, ask it back and get something. There's no way an object knows what class or interface it is. That's why I disallowed using "get($name)" as a public method (though I use it privately) and only allow it to be set using a method. I see that a user can simply do this without any typehint in the setter. And with simply getting the object like you have it you could still pass it through the setter manually. So maybe I'm too optimistic about what I tried to accomplish.
See the register() method:
Code: Select all
$inject = array(
// register class MyModel with constructor and setter injection calling setSomething(true, 42)
'MyModel' => array(
'__construct' => array('MysqlConnection'), // create and inject 'MysqlConnection' object
'setSomething' => array(true, 42), // call ->setSomething(true, 42)
),
// register class MysqlConnection with constructor
'MysqlConnection' => array(
'__construct' => array($config['DB']),
),
);
$locator->register($inject);
// then later
$model = $locator->get('model', 'MyModel');
Re: inverted dependency injection
Posted: Wed Oct 08, 2008 4:23 pm
by koen.h
arborint wrote:
See the register() method:
Code: Select all
$inject = array(
// register class MyModel with constructor and setter injection calling setSomething(true, 42)
'MyModel' => array(
'__construct' => array('MysqlConnection'), // create and inject 'MysqlConnection' object
'setSomething' => array(true, 42), // call ->setSomething(true, 42)
),
// register class MysqlConnection with constructor
'MysqlConnection' => array(
'__construct' => array($config['DB']),
),
);
$locator->register($inject);
// then later
$model = $locator->get('model', 'MyModel');
What happens if another coder on the other side of the skeleton galaxy does something like this:
Code: Select all
$locator->set('model', new TotallyDifferentModel());
You start coding again not knowing what happened and use your model:
Code: Select all
$model = $locator->get('model', 'MyModel');
$model->expectedMethod();
// PHP complains about $model::expectedMethod() not being there in class TotallyDifferentModel
Re: inverted dependency injection
Posted: Wed Oct 08, 2008 5:21 pm
by Christopher
First of all, that is a problem with Registries and nothing to do with DI. And that is a problem with many other types of objects as well. I suppose that not allowing overwrite would be one solution.
Second, you can also do this if you don't want to check first for an object in the Registry:
Code: Select all
$model = $locator->get('', 'MyModel');
That will do all the DI stuff, loading classes as necessary (or not loading on subsequent calls).