laminas / laminas-servicemanager

Factory-Driven Dependency Injection Container
https://docs.laminas.dev/laminas-servicemanager/
BSD 3-Clause "New" or "Revised" License
152 stars 59 forks source link

Lazy services should not have their desctructor proxied #202

Open func0der opened 1 year ago

func0der commented 1 year ago

Feature Request

When you proxy a class that has a destructor in it, the proxy manager by default creates a proxy for the __destruct method. Then without actually needing or using the class the destructor is called at the end of execution which loads the lazy loaded class.

There is a possibility to configure this behavior in the proxy manager by passing $proxyOptions['skipDestructor'], but there is currently no way to pass any proxy options to the proxy creation process from the service manager config.

There could be basically two routes here:

Q A
New Feature yes
RFC yes
BC Break yes&no

Summary

Have destructor of lazy services dealt with in a way that does not call them without the class being ever used in that run of PHP.

boesing commented 1 year ago

Not sure if this is actually related to laminas-servicemanager or to laminas-code or ocramius/proxy-manager? @Ocramius do you have an idea on how to handle this? Is this intended?

boesing commented 1 year ago

@func0der would you mind creating a PoC to get glympse? Most preferably with a failing unit test to understand the actual problem. If I understand correct, a __destruct is proxied to the instance even tho the service was never initialized in the first place? So on __destruct, the service will be initialized even tho we could early return in case the service was never loaded?

func0der commented 1 year ago

@boesing Sorry, I currently do not have time for a PoC.

Easiest way for me to reproduce this was: Have class in the service manager that initializes a connection to Redis in the factory. For example \Redis::class => \RedisFactory::class. Make \Redis::class lazy loadable. Build the service manager in an environment where you do not have a Redis server instance running, for example unit tests in CI or just stop it in your environment. Run the tests. Boom.

The __destruct of the proxy is called, the instance is initialized and it tries to connect to the redis server in RedisFactory.

boesing commented 1 year ago

You can provide that PoC whenever you want.