yiisoft / injector

PSR-11 compatible injector
https://www.yiiframework.com/
BSD 3-Clause "New" or "Revised" License
43 stars 18 forks source link

Add contextual functional. #31

Closed mj4444ru closed 3 years ago

mj4444ru commented 3 years ago
Q A
Is bugfix?
New feature? ✔️
Breaks BC?
Fixed issues

I suggest adding the ability to receive objects with a binding to the name in injector.

Usage example:

class SiteController
{
    ...
    public function index(TestInterface $test, TestInterface $testA, TestInterface $testB): ResponseInterface
    ...
    public function action1(ConnectionInterface $db): ResponseInterface
    ...
    public function action2(ConnectionInterface $dbA): ResponseInterface
    ...
    public function action3(ConnectionInterface $dbB): ResponseInterface
    ...
    public function action4(ConnectionInterface $dbA, ConnectionInterface $dbB): ResponseInterface
    ...
}

DI configuration example (config/common):

return [
    TestInterface::class => TestA::class,
    TestInterface::class . '$testA' => TestA::class,
    TestInterface::class . '$testB' => TestB::class,

    ConnectionInterface::class => function() {
        return new Connection(['dbName'=>'Def']);
    },
    ConnectionInterface::class . '$dbA' => function() {
        return new Connection(['dbName'=>'dbA']);
    },
    ConnectionInterface::class . '$dbB' => function() {
        return new Connection(['dbName'=>'dbB']);
    },
];
samdark commented 3 years ago

That likely won't work for a situation with same-named parameters of two different classes.

xepozz commented 3 years ago

That likely won't work for a situation with same-named parameters of two different classes.

What do you mean?

samdark commented 3 years ago

Also it will make it impossible to use injector with PSR container. Will require specific implementation.

samdark commented 3 years ago

Likely the situation could be solved with explicit controller config in container.

roxblnfk commented 3 years ago

@mj4444ru You could use this approach

$injector->invoke($action, [
    'db' => new Connection(['dbName'=>'Def']),
    'dbA' => new Connection(['dbName'=>'dbA']),
    'dbB' => new Connection(['dbName'=>'dbB']),
]);

However, there is a problem in making the initialization of parameters deferred (when needed).

I already thought about this functionality, but did not suggest it, so as not to overcomplicate Injector It might look like this:

$injector->invoke($action, [
    'db' => new Injector\Deferred(fn () => new Connection(['dbName'=>'Def'])),
    'dbA' => new Injector\Deferred(fn () => new Connection(['dbName'=>'dbA'])),
    'dbB' => new Injector\Deferred(fn () => new Connection(['dbName'=>'dbB'])),
    'someValue' => new Injector\Deferred(fn (ContainerInterface $container) => $container->get('someValue')),
]);
mj4444ru commented 3 years ago

@mj4444ru You could use this approach

$injector->invoke($action, [
    'db' => new Connection(['dbName'=>'Def']),
    'dbA' => new Connection(['dbName'=>'dbA']),
    'dbB' => new Connection(['dbName'=>'dbB']),
]);

However, there is a problem in making the initialization of parameters deferred (when needed).

I already thought about this functionality, but did not suggest it, so as not to overcomplicate Injector It might look like this:

$injector->invoke($action, [
    'db' => new Injector\Deferred(fn () => new Connection(['dbName'=>'Def'])),
    'dbA' => new Injector\Deferred(fn () => new Connection(['dbName'=>'dbA'])),
    'dbB' => new Injector\Deferred(fn () => new Connection(['dbName'=>'dbB'])),
    'someValue' => new Injector\Deferred(fn (ContainerInterface $container) => $container->get('someValue')),
]);

Why would the calling code know anything about the action?

roxblnfk commented 3 years ago

Why would the calling code know anything about the action?

At least this code knows what it runs.

The purpose of your proposal is clear, but it cannot be realized in this form. In your proposal, the injector dictates the format for storing IDs in the container. This is not desirable.

mj4444ru commented 3 years ago

Why would the calling code know anything about the action?

At least this code knows what it runs.

The purpose of your proposal is clear, but it cannot be realized in this form. In your proposal, the injector dictates the format for storing IDs in the container. This is not desirable.

What if he doesn't know?

Why exactly it cannot be implemented in such a form?

In my proposal, DI is just a repository that does not know what is stored in it. It is undesirable for a simple user to work with a container and an injector.

mj4444ru commented 3 years ago

Also it will make it impossible to use injector with PSR container. Will require specific implementation.

What is the assertion based on?

xepozz commented 3 years ago

@samdark Isn't $container->get('my-service') an PSR-compatible call?

samdark commented 3 years ago

@xepozz, @mj4444ru currently the calls are:

$container->get(Connection::class);

new calls will be:

$container->get(Connection::class . '$methodName');

It is, indeed, PSR-compatible but in order for it to work container must be aware of special $method syntax.

samdark commented 3 years ago

OK, I was wrong and it's fully compatible with previous container. In the "context" mode it will double the number of calls though but that's usually a single isset so should not matter that much.

roxblnfk commented 3 years ago

Why exactly it cannot be implemented in such a form?

@mj4444ru I suggest another draft that looks more flexible -- #32 It seems to me that it completely covers your case. What do you think about it?

samdark commented 3 years ago

Closing this one since @roxblnfk, indeed covers the case. @mj4444ru thanks for interesting idea.