Dhii / containers

A selection of PSR-11 containers for utility, simplicity, and ease
MIT License
8 stars 3 forks source link

CachingContainer cannot return caches of dependencies #8

Closed Biont closed 4 years ago

Biont commented 4 years ago

The CachingContainer decorates a child container. So if an entry is not yet cached, it is fetched from the child. But that means that any uncached entry is not able to pull cached dependencies, because the CachingContainer is not passed down.

$factories = [
    'foo' => function (ContainerInterface $container) {
        return new Foo();
    },
    'bar' => function (ContainerInterface $container) {
        return new Bar($container->get('foo'));
    },
    'baz' => function (ContainerInterface $container) {
        return new Baz($container->get('foo'), $container->get('bar'));
    },
];
$container = new CachingContainer(new DelegatingContainer(new ServiceProvider($factories, [])));

$container->get('foo'); // Calls factory function for foo because it's not yet cached
$container->get('foo'); // Returns cached Foo
$container->get('bar'); // Creates a new instance of Foo
$container->get('baz'); // Another instance of Foo
XedinUnknown commented 4 years ago

This is definitely a bug. To fix this bug, the delegating container must have a reference to the caching container. However, it cannot obtain that reference, because the caching container cannot exist before the delegating one, since it can only receive the inner container at construction time.

XedinUnknown commented 4 years ago

This is ultimately the chicken-egg problem that I have already encountered before with lookup delegation: the top-most container requires a reference to the inner container to lookup, and the inner container requires a reference to the top-most container to pass it down to service definitions.

Suggested Solution

Create a 3rd container with a very explicit setter, e.g. ProxyContainer::setContainer(ContainerInterface $inner). This proxy container will do nothing but forward calls to its inner container. The advantage is that you can use the setter to set the inner container at any time after creation of the proxy. In this particular case, you would use it like so:

$proxy = new ProxyContainer();
$container = new CachingContainer(new DelegatingContainer(new ServiceProvider($factories, []), $proxy));
$proxy->setContainer($container);
Biont commented 4 years ago

Thank you for the quick response. This looks good