thephpleague / container

Small but powerful dependency injection container

Home Page:http://container.thephpleague.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Setting concrete does not reset arguments.

Messere opened this issue · comments

When changing definition via extend() and setConcrete(), previously set constructor arguments are left intact.

Consider the following code:

require_once __DIR__ . '/vendor/autoload.php';

interface X {}

class ADependency {}

class AConcrete implements X {
    public function __construct(ADependency $arg) {}
}

class BDependency {}

class BConcrete implements X {
    public function __construct(BDependency $arg) {}
}

$container = new \League\Container\Container();

$container->add(ADependency::class);
$container->add(BDependency::class);

$container->add(X::class, AConcrete::class)->addArgument(ADependency::class);

$container->extend(X::class)->setConcrete(BConcrete::class)->addArgument(BDependency::class);

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

which ends with PHP Fatal error: Uncaught TypeError: Argument 1 passed to BConcrete::__construct() must be an instance of BDependency , instance of ADependency given, because container tries to initialize BConcrete with the first argument it has ever seen which is ADependency instead of BDependency.

I'd say that either setConcrete() should reset registered arguments and method calls, or separate method(s) should be provided to do so.

Basically I want some of the implementations for interfaces in application to be configurable by user. Application provides all the defaults, so it can run out of the box, but user can prepare yaml file which is parsed at startup and turned into appropriate definitions in the container.

I don't think I can have separate definitions. The rest of the application depends on autowiring so I need to use interface names as ids.

I can think of workarounds (split container initialization into multiple phases and provide default only if it wasn't configured by user, or provide defaults with new in constructors), but since the container already allows (at least partially) to modify definitions via setConcrete i thought I could use that. Unfortunately setConcrete leads to unexpected behaviour like described above.

Hi, it's not really designed to do that, the behaviour is expected as it's dealing with an existing definition, you'd be better having separate definitions and creating code to decide what alias it pulls from the container rather than messing with the container at runtime.

I'm gonna close this issue as what you're describing isn't really something that I'd plan to change, but happy to continue discussions.

Hi, that's ok.

I guess setConcrete was added for the purpose of providing test doubles rather than actually editing the container, and changing that would add unnecessary complexity to the library.