Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124464 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 6C5991A00B7 for ; Thu, 18 Jul 2024 07:12:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1721286822; bh=tS4gOCsB5xMVqdY3kmedjIzjR/m6mhXZGfVOdHlRQA4=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=Ua1dY5gMu/PDOBthxmCvflwkBCN5Yc1CkTf5MdMheCfwab2ximdFjsnnsoeMeLr9d R6cIhzYF7AGLM6pLx7N4gcSD2GrwqFvZ9GgUixI7dWS4WLKOika2L+azzKLg6KBtFr 6BKuUrmX/GW2O6lbNcNN/nzJiHOjA2xm9HUtEKBPX0AmdW8JikYlQQYSsWZI6YFejY 9ZuHEJL0QsYnqXvJq/SfB5TlczA4CDxQ4eDNSbnW7AFsNzJjqBhRo7n2oDkao63V+X SjQ81n8OScaXSLMkvgHVKuihrjom2BzMxvdXj+FoKUUIuKa08/VdNKPEbeq1N5r7CG dpFa6QKftxIOA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 3E1581801E6 for ; Thu, 18 Jul 2024 07:13:39 +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=1.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, FREEMAIL_REPLY,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-lf1-f43.google.com (mail-lf1-f43.google.com [209.85.167.43]) (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:13:36 +0000 (UTC) Received: by mail-lf1-f43.google.com with SMTP id 2adb3069b0e04-52e99060b41so525769e87.2 for ; Thu, 18 Jul 2024 00:12:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721286724; x=1721891524; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=4t+PUqlm8Xr4ajh3fHIthaRQoHmSZG0tFD3dpVPKt7Y=; b=NPzoCdWO/hR7LHdelcOQ63x4+OZK4dV0MzERYqsxQF3C6B2uuX/u9KZHuWIF9iE8Tr fdXtHxeGLpi+civO/SF4ShpHHC3Yo7IrBthGcnFb8OPwnLT1hchnHDtWsoHssnNH2oOc kN2NylxdnlHwb9Kl+Cbl8FjSCds/5/Z6Uft/hXEMGROl14wAhaa+ZeDrD4u/VEWT/JoL GgF/vISTKwKBf6FPQFnvlgwyaH2HmP3bUyH7EPwVqTau5nmPJNgONOcN2RPB6WOJcmbY nS7e4YRAadsIYQEnuTZ9tgvC3FHwvpL+7Dr1bSuWEQa1T+3zW/7g8qOLyU0MhD3QJlFX bw5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721286724; x=1721891524; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=4t+PUqlm8Xr4ajh3fHIthaRQoHmSZG0tFD3dpVPKt7Y=; b=dDIbH/uInRzV7saIsWJqgDFa7gBlC7kR5CS9mVrOlbQdD0v8CtgtlW5JbbDQvEaMT2 kNLyaO235AQgfEqUr/VaFqCEY/efLwlCBCLzDCNArxL8qwLj0EF2bypbqNVX/L3FWmwK ssqIIjoOdE7WF5FezwvpWg18KkNsreE45w2LdH3LFMbJpnm170EZTV+gD0dj+okBEDzC rT4Ge2ASEbg6lKUu3be/iNgCSftsLdBwzsjcsirJlGLD1q4xd/DD+5NLG8mI+T4D1uFh YxocAzAj9WPTWwBeKmyrzVcdA01eKxiMCCuweGB3QDWMc5FWX2clftFGFTToF6azPy/s rxIg== X-Forwarded-Encrypted: i=1; AJvYcCXzXhp+0QdRofNIxsqhVgwmNXoUHScJec5lV9KhEV/jI77/RA57gm0Bdyy8AUxP+1F/sGMerBod016G1ph+jKzEj4pVL86pcQ== X-Gm-Message-State: AOJu0Yw6rshiFNCMqrmKimek2tgmzBRWxyAfLBzS+ww0oxHOZCSBig2g OlLEmZpyjnCYqNPKavm1kmwv7gPXHxL/ubiNxetW+Vhz1K2G8HgqvfeSrDTfcDZf+2ppSOi+XG/ 1yvroLVOf+mjksjWOb2rt6a0+PaZvfPuO X-Google-Smtp-Source: AGHT+IEIz+R+dDzHwQQPzb6M12Z02m25YejXuThLsUitkAnfTnFiyMamibGhSBITBEUMqotHgArgRg49NFyqv6qEaFs= X-Received: by 2002:a05:6512:2316:b0:52e:be50:9c66 with SMTP id 2adb3069b0e04-52ee546a40fmr2774788e87.53.1721286723610; Thu, 18 Jul 2024 00:12:03 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 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> <3e637648-dcfa-4264-9d17-e7129936f44b@app.fastmail.com> In-Reply-To: <3e637648-dcfa-4264-9d17-e7129936f44b@app.fastmail.com> Date: Thu, 18 Jul 2024 09:11:48 +0200 Message-ID: Subject: Re: [PHP-DEV] [RFC] Lazy Objects To: Rob Landers Cc: =?UTF-8?Q?Tim_D=C3=BCsterhus?= , =?UTF-8?Q?Benjamin_Au=C3=9Fenhofer?= , PHP Internals List Content-Type: multipart/alternative; boundary="000000000000b7af77061d804bbb" From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --000000000000b7af77061d804bbb Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Le jeu. 18 juil. 2024 =C3=A0 09:08, Rob Landers a =C3= =A9crit : > On Wed, Jul 17, 2024, at 20:31, Nicolas Grekas wrote: > > Dear all, > > Le mar. 16 juil. 2024 =C3=A0 17:51, Nicolas Grekas < > nicolas.grekas+php@gmail.com> a =C3=A9crit : > > Hi there, > > Le mar. 16 juil. 2024 =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=BCsterhus = a =C3=A9crit : > > Hi > > On 7/15/24 09:25, Nicolas Grekas wrote: > > Testing is actually a good domain where resetting lazy objects might op= en > > 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#aut= o-refresh > > > > and provides nice DX thanks to this capability. > > > > I have 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 > corresponding Factory class. This factory clearly has the capability of > constructing objects by itself, so it could just create a lazy proxy > instead? > > I'm seeing the `instantiateWith()` example in the documentation where > the user can return a constructed object themselves, but I'm not seeing > how that can safely be combined with the `reset*()` methods: Anything > special the user did to construct the object would be reverted, so the > user might as well rely on the default construction logic of the factory > then. > > What am I missing? > > > 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/IsProxy.= php#L66-L76 > > Basically, the reset method is not needed when creating the lazy proxy. > But it's needed to refresh it when calling $object->_refresh(). The > implementation I just linked swaps the real object bound to the proxy for > another one (the line > "Configuration::instance()->persistence()->refresh($object);" swaps by > reference). > > > > 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 the= re > are more use cases for reset methods. > > Here is the revised part about resetAsLazyGhost in the RFC: > > This method allows an object to manage its own laziness by calling the > method 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. > > Another use case for this method is to achieve resettable services. In > these scenarios, a service object already inserted into a complex > dependency graph can be reset to its initial state using the lazy object > infrastructure, without its implementation being aware of this concern. A > concrete 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. However, th= anks > to the lazy-loading code infrastructure > , > recovering from such a state is possible. This method would be instrument= al > in achieving this capability without resorting to the current complex cod= e > 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 with lazy proxies. Namely, when > the factory returns an object from a parent class, we describe that addin= g > more on the proxy class would throw, and we also explain why. We also add= ed > a restriction to prevent a proxy from having an overridden __clone or > __destruct when the factory returns a parent, and explained why again. > > 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. > > On our side, this should close the last topics we wanted to address befor= e > 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 tha= t > to reflect on interfaces and abstract classes. This also limits the > usability, as for example, in a container, you might know that the type y= ou > need to return is MyInterface, but not know what type the actual factory > will return. > Proxying by interface is not in the scope of this RFC. For that, one can use code generation, and libraries like symfony/var-exporter provide all the tools to make it easy to do. Nicolas --000000000000b7af77061d804bbb Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=
Le=C2=A0jeu. 18 juil. 2024 =C3=A0=C2= =A009:08, Rob Landers <rob@bottled.codes> a =C3=A9crit=C2=A0:
On Wed, Jul 17, 2024, at 20:31, Nicolas Gre= kas wrote:
Dear all,

=
Le=C2=A0mar. 16 juil. 2024 =C3=A0=C2=A017:51, Nicolas Grek= as <= nicolas.grekas+php@gmail.com> a =C3=A9crit=C2=A0:
Hi there,

Le=C2=A0mar. 16 juil. 2024 =C3=A0=C2=A010= :13, Nicolas Grekas <nicolas.grekas+php@gmail.com> a =C3=A9crit=C2=A0:

<= /div>

Le=C2=A0lun. 15 juil. 2024 =C3=A0= =C2=A021:42, Tim D=C3=BCsterhus <tim@bastelstu.be> a =C3=A9crit=C2=A0:
Hi

On 7/15/24 09:= 25, Nicolas Grekas wrote:
> Testing is actually a good do= main where resetting lazy objects might open
> interestin= g use cases.
> This reminded me about zenstruck/foundry, = which leverages the
> LazyProxyTrait to provide refreshab= le fixture objects
> and provides n= ice DX thanks to this capability.
>

<= /div>
I have not used this library before, but I have taken a (very) b= rief
look at the code and documentation.
My understanding is that all the fixture objects are generated= by a
corresponding Factory class. This factory clearly has= the capability of
constructing objects by itself, so it co= uld just create a lazy proxy
instead?

I'm seeing the `instantiateWith()` example in the documentati= on where
the user can return a constructed object themselve= s, but I'm not seeing
how that can safely be combined w= ith the `reset*()` methods: Anything
special the user did t= o construct the object would be reverted, so the
user might= as well rely on the default construction logic of the factory
then.

What am I missing?

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

Basically, the reset= method is not needed when creating the lazy proxy. But it's needed to = refresh it when calling $object->_refresh(). The implementation I just l= inked swaps the real object bound to the proxy for another one (the line &q= uot;Configuration::instance()->persistence()->refresh($object);"= swaps by reference).


After chatting a bit with Benjamin on Slack, I realized tha= t the sentence "The indented use-case is for an object to manage its o= wn laziness by calling the method in its constructor" was a bit restri= ctive and that there are more use cases for reset methods.
Here is the revised part about resetAsLazyGhost in the RFC:

This method allows an object to manage its = own laziness by calling the method 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.=

Another use case for this method is to achie= ve resettable services. In these scenarios, a service object already insert= ed into a complex dependency graph can be reset to its initial state using = the lazy object infrastructure, without its implementation being aware of t= his concern. A concrete example of this use case is the Doctrine EntityMana= ger, which can end up in a hard to recover "closed" state, pre= venting its use in long-running processes. However, thanks to = the lazy-loading code infrastructure, recovering from such a state is p= ossible. This method would be instrumental in achieving this capability wit= hout resorting to the current complex code used in userland.

I hope this helps.
<= div>
A bit unrelated to the above topic: we've further cl= arified the RFC by addition restrictions to what can be done with lazy prox= ies. Namely, when the factory returns an object from a parent class, we des= cribe that adding more on the proxy class would throw, and we also explain = why. We also added a restriction to prevent a proxy from having an overridd= en __clone or __destruct when the factory returns a parent, and explained w= hy again.

This should simplify the overall beh= avior 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.

=
On our side, this should close the last topics we wanted to addr= ess before opening the vote.

Please let us kno= w if anyone has other concerns.

Cheers,
Nicolas

If yo= u cannot use an instance of a subclass as the actual object, then it the me= thods 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 factory will retur= n.


Pro= xying by interface is not in the scope of this RFC. For that, one can use c= ode generation, and libraries like symfony/var-exporter provide all the to= ols to make it easy to do.

Nicolas
= --000000000000b7af77061d804bbb--