auraphp / Aura.Di

Dependency Injection System
MIT License
349 stars 63 forks source link

newInstance throws a MissingParam exception for an interface that is known to the container #120

Closed glen-84 closed 8 years ago

glen-84 commented 8 years ago

See https://github.com/auraphp/Aura.Di/issues/116#issuecomment-185114347

If you do:

$di->set(MyInterface::class, $di->lazyNew(Something::class));

And class Test requires a MyInterface, then $di->newInstance(Test::class) doesn't use the registered implementation, and throws a MissingParam exception.

Note: Auto resolver enabled.

/cc @harikt

pmjones commented 8 years ago

In the example above, you're creating a shared service named 'MyInterface'. Service names are not interchangeable with class names, and are not honored as such. Likewise, newInstance() needs a class name to create an instance of, not an interface name, since interfaces cannot be instantiated themselves.

Does that help explain what's going on?

glen-84 commented 8 years ago

This is very intertwined with #116. The thing is, if you just ask the container for a service with the name of the type-hint (in this case an interface), things "just work".

This is in fact a reason in favour of option 1 here. For both classes and interfaces, you can simply request services from the container.

This is how PHP-DI works, but I'm not suggesting that it's right or wrong. I'm just trying to find the best solution.

I'm very confused right now. :confused: Need to think about it some more probably.

pmjones commented 8 years ago

Yes, very intertwined. I suggest that the confusion originates from unclear thinking about what a "service" is (or ought to be) and the differences between a Container, a Factory, and a Registry. To modify comments I have made elsewhere on that subject:

I'll go further and say that it's ok for a Container to expose its underlying Factory and Registry behaviors, along with configuration behaviors. (Although I have been thinking lately about how to separate the configuration behaviors. :-)

glen-84 commented 8 years ago

Right, but this is sort-of a mix of the two – a factory that may utilize the services registered in a container.

If you want to use the factory to create an Object with a single IService, you either:

  1. Request IService from the container by name
    • Imagine that this parameter could have an annotation specifying a different service name, and that the interface type-hint is just used as a default name.
  2. Configure a type-to-service map ahead of time (like $container->types), that defines what to inject.
    • Note that in my test case, I already had an IService defined via set, but I had to again define that mapping with $container->types.

I guess I was suggesting that if:

  1. There is no params config.
  2. There is no types config.

... it requests the service from the container, defaulting the service name to the name of the type hint.

But I'm not sure of anything anymore. :laughing:

pmjones commented 8 years ago

I hate to say it, but I think there are enough conflicts in how Aura.Di is intended to work, and how you think it ought to work, that we're at an impasse. Let me know if you revise your expectations on this.

glen-84 commented 8 years ago

It's your project, so if you don't think that type names should be used as default/fallback service names, I will respect your decision.

pmjones commented 8 years ago

(/me bows) I appreciate that; thanks.