Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124463 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id BCCD81A00B7 for ; Thu, 18 Jul 2024 07:08:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1721286585; bh=jZux9gw73XxGIsVYB1V9C2O6upCxJ2lbeRU5JmT0U78=; h=In-Reply-To:References:Date:From:To:Cc:Subject:From; b=McyjPonngTg+t/SvkIqv/0aRLpwRrjIibarBDshqxGJPLtorlpwE/tUP2goVhZFpo EMUiZFXNap0gUiyIvZ6n98KSdhkKeVLZQZBzfxjA3WcRhbmGkPWuqJBaB8RezusTE4 hYl2/HgUFd9/RTrmE4KmRpcOE8rwghavLrJ1YBDfr6TFWjtNsnXUnNLbdcb9dKkC6r qB5owe7DT/7g+GIRE5mzhquwY5DRuSOn+aMSYIti2BCPyo39rsn40iKKsjmojhWrPS k8l2qAmQxgM88+i6f7Vk+t/74AArXrfgQIy1W1JdqSAnBwsxorCxf80/LX7ZQ2QIAQ s+S6dp1/Ul5TQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id D689F18004A for ; Thu, 18 Jul 2024 07:09:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fout2-smtp.messagingengine.com (fout2-smtp.messagingengine.com [103.168.172.145]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 18 Jul 2024 07:09:43 +0000 (UTC) Received: from compute9.internal (compute9.nyi.internal [10.202.2.228]) by mailfout.nyi.internal (Postfix) with ESMTP id 47F01138037E; Thu, 18 Jul 2024 03:08:12 -0400 (EDT) Received: from wimap25 ([10.202.2.85]) by compute9.internal (MEProxy); Thu, 18 Jul 2024 03:08:12 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:cc:content-type:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm3; t=1721286492; x= 1721372892; bh=jZux9gw73XxGIsVYB1V9C2O6upCxJ2lbeRU5JmT0U78=; b=e C7Frm7kP3IN0vH5zLMKXgrCwylda2oP8/veENwFCyTxqYn/ZfDsl8MsEqzCVvqk5 MhYkxE+aWh3EwcVsLk5ruOprWzdbYgM4wAmTQK1WPzXUKSDQW90SdDISHxyK70aq EbD8WarKwt+hRi5J3/BcyZZtxokD6HmOyAOanjAYtY5UWVV8XzBNh4vBSWuWgMxM LW3B7NFGIKVjRivFNqyVSNldAkfBsWUenf2Hq7w6hYgRoWlF7GZPWZWltKGFEM1S Qzfmphl1mdPxnqJa3+nGtQGEDk8hRldSCR6Seu3j9yIYfehtPMQDyJdB0PN3gn4r xXwuwgas/4EU7z5382APQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; t=1721286492; x=1721372892; bh=jZux9gw73XxGIsVYB1V9C2O6upCx J2lbeRU5JmT0U78=; b=FnwuyYGtBPUlpQYiV5ZLDu7vy8nqmiQP7TpGHxu8Nl8O s3uUSTsmHACXBNJ9QKT3Vlwx0iE5Ta/ArvCWPagNC6gmr5rJIKo/UJEp7TuY7j/l 6xgl/QX5/GKFeSna54t8TxqmLK5e+dzhoKSD7/MAGZzZxx4N8/Keu6nFX3ij7HIe D7zIq93Is0V0jTQScBlQF5Q7jXqhb4yalGZmflfoJg+LJ1ZMchYl/ZEWVhUTip90 bDesYhKiJy8X09xN4ZUKR8Jwn+jogqR2nyDheBHODb4pEgYOBXy9Z6D+sJOZMtJT yzpwNetsb0sG2RjqxP8AhClhnqr0bBpC1qavcWuonQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeftddrgeekgdduudelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepofgfggfkjghffffhvfevufgtsegrtderreerreejnecuhfhrohhmpedftfho sgcunfgrnhguvghrshdfuceorhhosgessghothhtlhgvugdrtghouggvsheqnecuggftrf grthhtvghrnheptdduhffhtdejieefgeetleefveegveejieelfefhtddtgfektdegleff leehjefhnecuffhomhgrihhnpehshihmfhhonhihrdgtohhmpdhgihhthhhusgdrtghomh enucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehrohgs segsohhtthhlvggurdgtohguvghs X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.nyi.internal (Postfix, from userid 501) id 749281040060; Thu, 18 Jul 2024 03:08:10 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.11.0-alpha0-568-g843fbadbe-fm-20240701.003-g843fbadb Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Message-ID: <3e637648-dcfa-4264-9d17-e7129936f44b@app.fastmail.com> In-Reply-To: References: <1118bbcd-a7b4-47bf-bf35-1a36ab4628e1@bastelstu.be> <45847b93-02bf-459f-bcd2-81ba35a12c24@bastelstu.be> <46bd4098-2936-4e46-98e9-fe55118325c2@bastelstu.be> <61ab36bc-b045-452a-84e0-87367d4c680e@bastelstu.be> <07e065f2-8f64-4bad-9a98-51f4eaf63ddb@app.fastmail.com> <2a0a4650-c2c5-4c6d-ad3a-25365b3391b2@bastelstu.be> Date: Thu, 18 Jul 2024 09:07:44 +0200 To: "Nicolas Grekas" , =?UTF-8?Q?Tim_D=C3=BCsterhus?= Cc: =?UTF-8?Q?Benjamin_Au=C3=9Fenhofer?= , "PHP Internals List" Subject: Re: [PHP-DEV] [RFC] Lazy Objects Content-Type: multipart/alternative; boundary=15e8d820c798462e9651617aee827851 From: rob@bottled.codes ("Rob Landers") --15e8d820c798462e9651617aee827851 Content-Type: text/plain;charset=utf-8 Content-Transfer-Encoding: quoted-printable On Wed, Jul 17, 2024, at 20:31, Nicolas Grekas wrote: > Dear all, >=20 > Le mar. 16 juil. 2024 =C3=A0 17:51, Nicolas Grekas > a =C3=A9crit : >> Hi there, >>=20 >> Le mar. 16 juil. 2024 =C3=A0 10:13, Nicolas Grekas > a =C3=A9crit : >>>=20 >>>=20 >>> Le lun. 15 juil. 2024 =C3=A0 21:42, Tim D=C3=BCsterhus a =C3=A9crit : >>>> Hi >>>>=20 >>>> On 7/15/24 09:25, Nicolas Grekas wrote: >>>> > Testing is actually a good domain where resetting lazy objects mi= ght open >>>> > interesting use cases. >>>> > This reminded me about zenstruck/foundry, which leverages the >>>> > LazyProxyTrait to provide refreshable fixture objects >>>> > >>>> > and provides nice DX thanks to this capability. >>>> >=20 >>>>=20 >>>> I have not used this library before, but I have taken a (very) brie= f=20 >>>> look at the code and documentation. >>>>=20 >>>> My understanding is that all the fixture objects are generated by a=20 >>>> corresponding Factory class. This factory clearly has the capabilit= y of=20 >>>> constructing objects by itself, so it could just create a lazy prox= y=20 >>>> instead? >>>>=20 >>>> I'm seeing the `instantiateWith()` example in the documentation whe= re=20 >>>> the user can return a constructed object themselves, but I'm not se= eing=20 >>>> how that can safely be combined with the `reset*()` methods: Anythi= ng=20 >>>> special the user did to construct the object would be reverted, so = the=20 >>>> user might as well rely on the default construction logic of the fa= ctory=20 >>>> then. >>>>=20 >>>> What am I missing? >>>=20 >>> Finding the spot where the reset method would be useful is not easy.= Here it is: >>> https://github.com/zenstruck/foundry/blob/v2.0.7/src/Persistence/IsP= roxy.php#L66-L76 >>>=20 >>> Basically, the reset method is not needed when creating the lazy pro= xy. But it's needed to refresh it when calling $object->_refresh(). The = implementation I just linked swaps the real object bound to the proxy fo= r another one (the line "Configuration::instance()->persistence()->refre= sh($object);" swaps by reference). >>=20 >>=20 >> After chatting a bit with Benjamin on Slack, I realized that the sent= ence "The indented use-case is for an object to manage its own laziness = by calling the method in its constructor" was a bit restrictive and that= there are more use cases for reset methods. >>=20 >> Here is the revised part about resetAsLazyGhost in the RFC: >>=20 >> This method allows an object to manage its own laziness by calling th= e method in its constructor, as demonstrated here . In such cases, the propo= sed lazy-object API can be used to achieve lazy initialization at the im= plementation detail level.=20 >>=20 >> Another use case for this method is to achieve resettable services. I= n these scenarios, a service object already inserted into a complex depe= ndency graph can be reset to its initial state using the lazy object inf= rastructure, without its implementation being aware of this concern. A c= oncrete example of this use case is the Doctrine EntityManager, which ca= n end up in a hard to recover "closed" state, preventing its use in long-running processes. Howeve= r, thanks to the lazy-loading code infrastructure , recovering from such a state i= s possible. This method would be instrumental in achieving this capabili= ty without resorting to the current complex code used in userland. >>=20 >> I hope this helps. >=20 > A bit unrelated to the above topic: we've further clarified the RFC by= addition restrictions to what can be done with lazy proxies. Namely, wh= en the factory returns an object from a parent class, we describe that a= dding more on the proxy class would throw, and we also explain why. We a= lso added a restriction to prevent a proxy from having an overridden __c= lone or __destruct when the factory returns a parent, and explained why = again. >=20 > This should simplify the overall behavior by preventing edge case that= wouldn't have easy answers. If those limitations prove too restrictive = in practice (my experience tells me they should be fine), they could be = leveraged in the future. >=20 > On our side, this should close the last topics we wanted to address be= fore opening the vote. >=20 > Please let us know if anyone has other concerns. >=20 > Cheers, > Nicolas If you cannot use an instance of a subclass as the actual object, then i= t the methods probably shouldn't exist on ReflectionClass since you use = that to reflect on interfaces and abstract classes. This also limits the= usability, as for example, in a container, you might know that the type= you need to return is MyInterface, but not know what type the actual fa= ctory will return. =E2=80=94 Rob --15e8d820c798462e9651617aee827851 Content-Type: text/html;charset=utf-8 Content-Transfer-Encoding: quoted-printable
On Wed, Jul 17,= 2024, at 20:31, Nicolas Grekas wrote:
Dear all,

Le mar. 16 juil. 2024 =C3=A0 17:51, Nicolas Gr= ekas <nicolas.greka= s+php@gmail.com> a =C3=A9crit :
Hi there,

Le mar. 16 juil. 202= 4 =C3=A0 10:13, Nicolas Grekas <nicolas.grekas+php@gmail.com>= a =C3=A9crit :


Le lun. 15 juil. 2024 =C3=A0 21:42, Tim D=C3=BC= sterhus <tim@ba= stelstu.be> a =C3=A9crit :
Hi
<= br>
On 7/15/24 09:25, Nicolas Grekas wrote:
&g= t; Testing is actually a good domain where resetting lazy objects might = open
> interesting use cases.
> This= reminded me about zenstruck/foundry, which leverages the
= > LazyProxyTrait to provide refreshable fixture objects
> <= https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#au= to-refresh>
> and provides nice DX thanks to th= is capability.
>

I hav= e not used this library before, but I have taken a (very) brief
look at the code and documentation.

My understanding is that all the fixture objects are generated by a <= br>
corresponding Factory class. This factory clearly has the= capability of
constructing objects by itself, so it cou= ld just create a lazy proxy
instead?

=
I'm seeing the `instantiateWith()` example in the documentat= ion where
the user can return a constructed object thems= elves, but I'm not seeing
how that can safely be combine= d with the `reset*()` methods: Anything
special the user= did to construct the object would be reverted, so the
u= ser might as well rely on the default construction logic of the factory =
then.

What am I missing?<= br>

Finding the spot where the res= et method would be useful is not easy. Here it is:

<= /div>
Basically, the reset method is not needed when creating the la= zy proxy. But it's needed to refresh it when calling $object->_refres= h(). The implementation I just linked swaps the real object bound to the= proxy for another one (the line "Configuration::instance()->persiste= nce()->refresh($object);" swaps by reference).
<= /blockquote>


After chatting a bit with= Benjamin on Slack, I realized that the sentence "The indented use-case = is for an object to manage its own laziness by calling the method in its= constructor" was a bit restrictive and that there are more use cases fo= r reset methods.

Here is the revised part a= bout resetAsLazyGhost in the RFC:

This= method allows an object to manage its own laziness by calling the metho= d in its constructor, as demonstrated here.= In such cases, the proposed lazy-object API can be used to achieve lazy= initialization at the implementation detail level.

<= /div>
Another use case for this method is to achieve resettable serv= ices. In these scenarios, a service object already inserted into a compl= ex dependency graph can be reset to its initial state using the lazy obj= ect infrastructure, without its implementation being aware of this conce= rn. A concrete example of this use case is the Doctrine EntityManager, w= hich can end up in a hard to recover "closed" state, preventing it= s use in long-running processes. However, thanks to t= he lazy-loading code infrastructure, recovering from such a state is= possible. This method would be instrumental in achieving this capabilit= y without resorting to the current complex code used in userland.

I hope this helps.

A bit unrelated to the above topic: we've = further clarified the RFC by addition restrictions to what can be done w= ith lazy proxies. Namely, when the factory returns an object from a pare= nt class, we describe that adding more on the proxy class would throw, a= nd we also explain why. We also added a restriction to prevent a proxy f= rom having an overridden __clone or __destruct when the factory returns = a parent, and explained why again.

This sho= uld simplify the overall behavior by preventing edge case that wouldn't = have easy answers. If those limitations prove too restrictive in practic= e (my experience tells me they should be fine), they could be leveraged = in the future.

On our side, this should clo= se the last topics we wanted to address before opening the vote.

Please let us know if anyone has other concerns.

Cheers,
Nicolas

If you cannot use an instance of= a subclass as the actual object, then it the methods probably shouldn't= exist on ReflectionClass since you use that to reflect on interfaces an= d abstract classes. This also limits the usability, as for example, in a= container, you might know that the type you need to return is MyInterfa= ce, but not know what type the actual factory will return.

=E2=80=94 Rob
--15e8d820c798462e9651617aee827851--