On Wed, Jun 5, 2024, at 2:50 PM, Arnaud Le Blanc wrote: > Good point. The Mutex constructor is called during "new Mutex()", but > the object is made lazy after that, and the destructor is never > called. > > We have made the following changes to the RFC: > > - makeLazyGhost / makeLazyProxy will call the object destructor > - A new option flag is added, `ReflectionLazyObject::SKIP_DESTRUCTOR`, > that disables this behavior > > This is not ideal since the intended use of these methods is to call > them on objects created with newInstanceWithoutConstructor(), or > directly in a constructor, and both of these will need this flag, but > at least it's safe by default. > > Thanks again for the feedback. > > Best Regards, > Arnaud
Let me make sure I am following, since I'm still having a hard time with the explanation in the RFC (as I discussed with Nicolas off list). The two use cases intended here are, essentially, "delay invoking the constructor until first use" (ghost) and "sneak a factory object in the way that is called on first use" (virtual). Right? The ghost initializer callback is basically an alternate constructor (which will probably just call the real constructor in the typical case), and the virtual initializer callback is basically the body of the factory object. So the most common ghost implementation for a service would be something like: $c = the_container(); $object = new ReflectionClass(Foo::class)->newInstanceWithoutConstructor(); $initializer = static function(Foo $foo) => $foo->__construct($c->dep1, $c->dep2, $c->dep3); $lazyReflector = ReflectionLazyObject::makeLazyGhost($object, $initializer, ReflectionLazyObject::SKIP_DESTRUCTOR); Am I following? Because if so, the proposed API is extremely clunky. For one thing, using an input/output parameter ($object) is a code smell 99% of the time. It's changing an un-constructed object into a ghost object, and returning... um, I'm not sure what. It also means I need to use both reflection classes in different ways to achieve the result. It seems a much cleaner API would be something like: $object = new ReflectionClass(Foo::class)->newInstanceWithLazyConstructor($initializer); In which case it becomes a lot more obvious that we are, essentially, "swapping out" the constructor for a lazy one. It also suggests that perhaps the function should be using $this, not $foo, as it's running within the context of the object (I presume? Can it call private methods? I assume so since it can set private properties.) That would also suggest this API for the other approach: $initializer = static function() => new Foo($c->dep1, $c->dep2, $c->dep3); $object = new ReflectionClass(Foo::class)->newInstanceWithLazyFactory($initializer); In which case $object is the proxy, and gets "swapped out" for the return value of the $initializer on first use. Am I understanding all this correctly? Because if so, I think the above simplified API would make it much more obvious what is going on, much easier to work with, easier to document/explain, and simple enough that it could conceivably be used in cases outside of DI or ORMs, too. If I'm way off and don't understand what you're doing, then please explain as I'm clearly very confused. :-) --Larry Garfield