Re: [RFC] Lazy Objects
| From: | Tim Düsterhus | Date: | Sun, 30 Jun 2024 13:54:26 +0000 |
| Subject: | Re: [RFC] Lazy Objects | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-124094@lists.php.net to get a copy of this message | ||
Hi
On 6/27/24 16:27, Arnaud Le Blanc wrote:
I'm curious, how did the implementation look like? Is there a proof of concept commit or patch available somewhere? As the author of the first internal enum (Random\IntervalBoundary) I had the pleasure of finding out that there was no trivial way to efficiently match the various enum cases. See the PR review here: https://github.com/php/php-src/pull/9679#discussion_r1045943444 I was able to find a hacky work-around, but if we add additional enums, perhaps we should add the proper infrastructure to the arginfo files or so to match enum cases to switch-case in C, even without making them an integer-backed enum.* flags should be aUnfortunately this leads to a 30% slowdown in newLazyGhost() when switching to an array of enums, in a micro benchmark. I'm not sure how this would impact a real application, but given this is a performance criticallist<SomeEnumAroundProxies>instead. A bitmask for a new API feels unsafe and anachronistic, given the tiny performance hit.
I believe the sub-class part is not spelled out in the RFC text. I'm also not sure if allowing sub-classes here is sound, given the reasoning of my previous emails.* what happens ifThe object must be an instance of the class represented by ReflectionClass (including of a sub-class)ReflectionClass#reset*()methods are used on a different class instance?* considered?
See my previous email.Cloning an initialized proxy is the same as cloning the real instance (this clones the real instance and returns it).* what about the object that a lazy proxy forwards state access to? Isit cloned too?
The dynamic property bit is good, I had the same question. To rephrase: Any access to a non-existant (i.e. dynamic) property will trigger initialization and this is not preventable using 'skipLazyInitialization()' and 'setRawValueWithoutLazyInitialization()' because these only work with known properties? While dynamic properties are deprecated, this should be clearly spelled out in the RFC for voters to make an informed decision.The linkage can not be broken. unset() and dynamic properties are not special, in that these are just property accesses. All property accesses on the proxy are forwarded to the real instance.After initialization, property accesses on the proxy are forwarded tothe actual instance.Observing properties of the proxy has the same result as observingproperties of the actual instance. * This is some sort of "quantum locking" of both objects? * How hard is it to break this linkage?* Can properties bewould be OK to just document the expected behaviorunset(), for example? * what happens to dynamic properties? * I don't use them myself, and I discourage their usage, but it
Relatedly: In the 'resetAsLazyGhost()' explanation we have this sentence:Returning a lazy object (including an initialized proxy) is not allowed and will throw. I've clarified this in the RFC (the RFC specified that returning a lazy object was not allowed, but whether this included initialized proxies was not clear).* what happens if the user mis-implements the factory as `function (object $proxy): object { return $proxy; }`?ReflectionClass::newLazyProxy()The factory should return a new object: the actual instance.* this is obviously a mistake on their end, but is it somehowpreventable?
If the object is already lazy, a ReflectionException is thrown with the message “Object is already lazy”.What happens when calling the method on a *initialized* proxy object? i.e. the following:
class Obj { public function __construct(public string $name) {} }
$obj1 = new Obj('obj1');
$r->resetAsLazyProxy($obj, ...);
$r->initialize($obj);
$r->resetAsLazyProxy($obj, ...);
What happens when calling it for the actual object of an initialized proxy object? It's probably not possible to prevent this, but will this allow for proxy chains? Example:
class Obj { public function __construct(public string $name) {} }
$obj1 = new Obj('obj1');
$r->resetAsLazyProxy($obj1, function () use (&$obj2) {
$obj2 = new Obj('obj2');
return $obj2;
});
$r->resetAsLazyProxy($obj2, function () {
return new Obj('obj3');
});
var_dump($obj1->name); // what will this print?
Best regards
Tim Düsterhus