简体   繁体   中英

PHP How to prevent pimple DIC causing infinite loop on a circular dependency

In this example I have classA and classB that I am using with pimple container.

They both have a dependency on each other. However when setting this up with pimple DIC, the below code causes an infinite loop...

There must be a way of doing this in pimple but I can't see it in the docs... Any ideas how to prevent the infinite loop?

// PIMPLE CONTAINER
use Pimple\Container;
$container = new Container();

use Classes\ClassA;
$container['ClassA'] = function ($c) {
  return new ClassA($c['ClassB']);
};

use Classes\ClassB;
$container['ClassB'] = function ($c) {
  return new ClassB($c['ClassA']);
};

Strictly speaking, your problem is not really pimple related.

You simply have a circular constructor dependency , there is no way to fix those. A needs B needs A needs B...

The thing is, usually, two classes that depend on each other do not make much sense. But in the rare case where that do happen, one of the two should be a lighter dependency, where you inject the object through a setter or some mechanism like that after instantiation .

By passing the container around and instantiating inside ClassB , you are hiding your dependencies, which beats the purpose of having a dependency injection container. Now ClassB depends on Container , not on ClassA , which is what you wanted in the first place.

Instead of doing that, just add a method setA(Class A $a) to your ClassB .

Then, you inject your dependency by calling $b->setA($container['ClassA']); when you actually need it, not on the DI container. As Adam points out , you can even do the setter injection in your container by extending your services and using the setters on the service definition.

But again, to reiterate, your main problem is to have a circular dependency . Re-think that. It's very likely a sign your design could do with some improvements.

@yivi makes a valid observation in their answer about the validity of circular references in the first place. So I really think you ought to assess your design here. You might be treating the symptom, not the underlying problem. It's impossible for us to comment on this though given your generic sample code (which is good code for the issue at hand, that said). Maybe raise a new question about your design if you think it's worth while? Either here or on Code Review , maybe?

If you are in control of the design of ClassA and ClassB , then the prescribed approach to this sort of thing is to use setter injection, not constructor injection.

This should work:

// PIMPLE CONTAINER
use Pimple\Container;
$container = new Container();

use Classes\ClassA;
$container['ClassA'] = function ($c) {
  return new ClassA();
};

use Classes\ClassB;
$container['ClassB'] = function ($c) {
  return new ClassB();
};


$container->extend('ClassA', function ($instanceOfA, $container) {
    $instanceOfA->setB($container['ClassB']);

    return $instanceOfA;
});

$container->extend('ClassB', function ($instanceOfB, $container) {
    $instanceOfB->setA($container['ClassA']);

    return $instanceOfB;
});

(untested, might contain typos, but that's the general gist)

Note how the original constructors do not take the dependency any more, the insertion of that is left to a specific setter. This allows you to create the services in the container, then use extend to then call the relevant setter to insert the dependency after this.

This is in the docs: Modifying Services after Definition .

// PIMPLE CONTAINER
use Pimple\Container;
$container = new Container();

use Classes\ClassA;
$container['ClassA'] = function ($c) {
  return new ClassA($c['ClassB']);
};

use Classes\ClassB;
$container['ClassB'] = function ($c) {
  return new ClassB($c);
};

Instead of instantiating ClassA from classB, pass the container to ClassB. Then when you need ClassA within ClassB, you can use the container you have passed to it to initiate / get instance ot ClassA.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM