On Fri, Jun 14, 2024, at 12:13 PM, Arnaud Le Blanc wrote: > Hi Tim, > > We have updated the RFC to address your feedback. Please find > additional answers below.
The updated RFC looks much better, thank you. Though I still have some thoughts, in no particular order. > The actual instance is allowed to escape the proxy and to create direct > references to itself. How? Is this a "return $this" type of situation? This could use more fleshing out and examples. The terms "virtual" and "proxy" seem to be used interchangeably in different places, including in the API. Please just use one, and purge the other. It's confusing as is. :-) (I'd favor "proxy", as it seems more accurate to what is happening.) For that matter, I'd be very tempted to remove the word "lazy" from the API calls. `newGhostInstance()` and `newProxyInstance()` are plenty understandable, and shorter/easier to read. (Similarly, `makeGhostInstance()` and `makeProxyInstance()`. Although since those are more about modifying a provided object to be lazy, perhaps "make" isn't the right verb to use as that often means "create".) Under Common Behavior, you have an example of calling the constructor directly, using the reflection API, but not of binding the callable, which the text says is also available. Please include an example of that so we can evaluate how clumsy (or not) it would be. > After calling newLazyGhostInstance(), the behavior of the object is the same > as an object created by newLazyGhostInstance(). I think the first is supposed be a make* call? > When making an existing object lazy, the makeInstanceLazy*() methods call the > destructor unless the SKIP_DESTRUCTOR flag is given. I don't quite get why this is. Admittedly destructors are rarely used, but why does it need to call the destructor? I find it interesting that your examples list DICs as a use case for proxies, when I would have expected that to fit ghosts better. The common pattern, I would think, would be: class Service { public function __construct(private ServiceA $a, private ServiceB $b) {} } $c = some_container(); $init = fn() => $this->__construct($c->get(ServiceA::class), $c->get(ServiceB::class)); $service = new ReflectionLazyObjectFactory(Service::class, $init); (Most likely in generated code that can dynamically sort out the container calls to inline.) Am I missing something? ReflectionLazyObjectFactory is a terrible name. Sorry, it is. :-) Especially if it's subclassing ReflectionClass. If it were its own thing, maybe, but it's still too verbose. I know you don't want to put more on the "dumping ground" fo ReflectionClass, but honestly, that feels more ergonomic to me. That way the following are all siblings: newInstance(...$args) newInstanceWithoutConstructor(...$args) newGhostInstance($init) newProxyInstance($init) That feels a lot more sensible and ergonomic to me. isInitialized(), initialized(), etc. also feel like they make more sense as methods on ReflectionObject, not as static methods on a random new class. As I said, definitely improved, and I like where it's going. I think it can improve further, though. --Larry Garfield