auraphp / Aura.Di

Dependency Injection System
MIT License
349 stars 63 forks source link

Limitations about object creation #92

Closed mivanov93 closed 9 years ago

mivanov93 commented 9 years ago

I've read the documentation and played with the library, seems nice, but so far I didn't get the following.

[1]. How do we get two instances of the same class but with different param values in their constructors?

Example for question 1: Say we have class DbConn and two classes DbCfgA and DbCfgB.

So we'd like to have two DbConn instances - one with DbCfgA and DbCfgB.

But if we do $di->set('something', $di->lazyNew('DbCfgA')); $di->params['DbConn']['dbCfg'] = 'something'; and $db->set('dbConn1', $di->lazyNew('DbConn')); $db->set('dbConn2', $di->lazyNew('DbConn'));

Making a base dbConn class and extending it twice is fine too, but seems a bit like overhead. Creating empty classes and killing performance too.

[2]. How to use libraries which do not provide public constructors but static functions, like Doctrine?

I used $di->lazy and a wrapping class, but it seems without lazyNew I can't pass parameters. So it's the same as just using a random singleton.. Maybe I should do use ($di) but that will not resolve the dependencies by itself either.

And with lazyNew I'd have to create a wrapping class too, but then I can't directly use it, because you can't do a simple $this=somethin else; in a PHP constructor.

Example for question 2

$di->set('orm_em', $di->lazy(function() {
  return OrmWrapper::getInstance();  
}));

class OrmWrapper {

    public static function getInstance() {
        $isDevMode = true;
        $config = Setup::createYAMLMetadataConfiguration(array(__DIR__ . "/../orm/mappings"), $isDevMode);

        $conn = array(
            'driver' => 'pdo_sqlite',
            'path' => __DIR__ . '/../db/db.sqlite',
        );

        $entityManager = EntityManager::create($conn, $config);
        return $entityManager;
    }

}
pmjones commented 9 years ago

1: Easy!

$di->set('service1', $di->lazyNew('ClassName', array(
    'foo' => 'value1',
)));

$di->set('service2', $di->lazyNew('ClassName', array(
    'foo' => 'value2',
)));

2: Not so easy. Statics being statics, all you get are global public methods to work with, so there's no opportunity for the DI system to intercede. It seems like creating a Factory class is the right thing to do in that case (what you have called a "wrapper" class above), which then becomes easier to invoke:

$di->set('orm_em', $di->lazy(function() use ($di) {
    $factory = $di->newInstance('OrmFactory');
    return $factory();
}));

class OrmFactory
{
    public function __construct(
        $isDevMode,
        array $conn
    ) {
        $this->conn = $conn;
        $this->isDevMode = $isDevMode;
    }

    public function __invoke()
    {
        return EntityManager::create($this->conn);
    }
}

Then you have a real factory to work with and set $di->params on, and are not dependent on any particular DI system.

Does that help? (This is yet another reason I don't like statics.)

p.s.: After working through this, it might be useful to have some sort of method to indicate "instantiate this Factory class and invoke it to get the object" for services.

mivanov93 commented 9 years ago

Thanks for 1), as for 2), I thought about it too, perhaps - $di->lazyNewInstanceFromFactory('OrmFactory'); (or something shorter, that's way too long). As for statics, I guess the main problem is PHP doesn't allow constructor overloading.

By the way I've been using Pimple 1.x recently and saw that in version 3, they are evading the $app->share which is kinda like $di->lazy, in favor of just $app['stuff']=function() {}; but I guess that's ok for them since Pimple is more of a service locator and requires $app to be injected everywhere, (so we get a flat array like $app) basically resolving the lazy loading itself instead of creating Lazy objects like Aura.Di which can be stacked. So you can't do a simple $stuff=$app->share() in Pimple either. Is my understanding of the matter correct?

mivanov93 commented 9 years ago

Another thing that sparked my interest here is - what about the auto-resolution? Why is it hard to debug? And wouldn't it better to do something simpler instead of trying to remove it -> generate an auto-resolve cache? That way we will get better performance plus a comprehension about what's happening and the debug issue will be solved.

harikt commented 9 years ago

Hard to debug is when you expect the same instance of the object. But sometimes it was not. Basically the problem wiring things.

mivanov93 commented 9 years ago

I understand, probably in some cases that won't work completely unmanaged either, like in case 1 i've mentioned. But, if instead=> we generate a config about our DI, we can debug that config and still save the initial time investment of writing it from scratch.

pmjones commented 9 years ago

Regarding (2) I realized last night that you can in fact use the static method, and pass arguments to the method call:

$di->set('orm_em', $di->lazy(array('EntityManager', 'create'), $conn, $config));

That's because the array() notation for a callable lets you make a static call. Individual arguments after the callable get passed as arguments. You can't build a set of of params overrides or anything, but it is more straightforward.

pmjones commented 9 years ago

@mivanov93 ^^

pmjones commented 9 years ago

@pmjones: update the "lazy" docs in 3.x to point out how statics can be used in lazies

harikt commented 9 years ago

@mivanov93 This was a package to build config of v2 https://github.com/friendsofaura/FOA.DiConfig rather than creating from scratch ;) .

mivanov93 commented 9 years ago

@pmjones Thanks, that should help make it more concise.

@harikt Thank you, indeed that's a nice addition, even if it only works for constructors, if it saves time, it's useful.