Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124399 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 6A8B21A00B7 for ; Thu, 11 Jul 2024 23:40:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1720741296; bh=ic7FDyIxMIvMZOYe8Yz8y3V8JoWID/ATJu0YGgLDCkM=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=WjgQ6aJ04472qqoWVFL0Ui0C9zxpLO8P4PUxEmw/XpXPlU0zkw8ev+V7WCbSwld3A MPyIIeSQWcxGp0cdqVkzhrF9YHw6dgB353WfLeYm3bo+veUwFFgGMYVPbrz+6ZIcSt umHjbd3fbXrvnbQSvrPOPX5+18dMpeW9gVvdFWES6limRP0Q04im4N43OUKDBVTnyo wBBgamAnjqOxmeQ4UAX/RYlqCpOtOVOZOvqh9W+kfi9b8AnQnvw1TfuDMAAblpWIiq 7Gi2PGYqW9/G1LuQdwjJSj46HykU1kFfcRDyyLycKJwZ4tir59DmMs3Qa8PuvdS/rF lkU7/dXc1LRgQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 61D4B18038D for ; Thu, 11 Jul 2024 23:41:33 +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.8 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DMARC_MISSING,HTML_MESSAGE,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-vs1-f44.google.com (mail-vs1-f44.google.com [209.85.217.44]) (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, 11 Jul 2024 23:41:32 +0000 (UTC) Received: by mail-vs1-f44.google.com with SMTP id ada2fe7eead31-48ffa17fa2fso533965137.0 for ; Thu, 11 Jul 2024 16:40:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=beberlei-de.20230601.gappssmtp.com; s=20230601; t=1720741205; x=1721346005; 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=pTmkjP7a2hmTQuodToTRe9g2fT0mN32x06j19gh6iqQ=; b=Vs3vyoWh4Mft61R7hGRxnM/wafgET1BttPXHN2JakaAT322cPnDxhOkVDmzsWD5eZv fNbl6GvsbIiFiKjZ4OvyapdbyfycVBi8dJiuyNBlkLs4PNnecJiEaRd/B3xeq5D+6dfM 4Jqt2N0X2oX7NewaFAQDAYxCAkupqhcVI9st7zpAkilRtehotzGKC+pnnxf6Xze3p1pK 8g25b2Ppf15LCbNk48qbQlaZgef4UYK8GZ0GJr4hfdqljCfvsdaFp9FWzwgJnGKKDsBh 3gBYr1aPX8K+go2sn7CT/TDdtnMVuRg31VWWETuCRjHNIrGjS1xXO/2ZzlG0ZThDFzDp mu8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720741205; x=1721346005; 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=pTmkjP7a2hmTQuodToTRe9g2fT0mN32x06j19gh6iqQ=; b=EliuDKTGQ5XuIGnYAVt1+UAlrE5n2LCAS21ZE3ofBFF2hdKZJVBxPtguuFocJLck9S nHsliLrp27vySxr5pXmTS0w+ch+2vTao38UfrVv6BUP1uGrubemcfbXOKYiAUcMFj7RP 04yY/Gygc5qMJGwfRIIKU4LHlTIMAL9ct1rSegHAVfpHYQxaesNdGfoH8VodXZrhMcI4 HWFzZZNuQAzvf2TTXpaPdgoza4hrkLZoWYM7OAfGAP3VoZ6x9V/Bs/AByFdAGRE42Z6J XR3hUye/mhbhr1XJjRjjLq+VsGeekMK6pYYVryYU5Z0oEO/K+i+yte0yJlBmxBaFCBQw PVGg== X-Gm-Message-State: AOJu0YwSsoEuAaFaEOwn7sSHgSk0iM2a2Nz/TruLE0x7FXwYgejTYmB3 ob5h9b7Pa4aLrWI/QyYwyyhISLYPYhY1oqqc9pclAIRuBZdTJB+8lUYoW40b02EbOJK9PoRUrML dnQjwi1mGXS4C6bcaV2erl/DdS3YK0ra7PXI59/6DGYjRCIpX X-Google-Smtp-Source: AGHT+IH+w4GxoFBYYl7sqcrDQujJsmxQb0AdO8R+fwUbJr4q1xm9gkLBe7FDlhUyRI8wr0hbSYq+JSqigE3V8bhlWiI= X-Received: by 2002:a05:6102:b12:b0:48f:4857:18f5 with SMTP id ada2fe7eead31-49032132d11mr9908175137.20.1720741205091; Thu, 11 Jul 2024 16:40:05 -0700 (PDT) Received: from 1064022179695 named unknown by gmailapi.google.com with HTTPREST; Thu, 11 Jul 2024 16:40:03 -0700 Received: from 1064022179695 named unknown by gmailapi.google.com with HTTPREST; Fri, 12 Jul 2024 01:40:00 +0200 Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 (Mimestream 1.3.6) 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> In-Reply-To: <61ab36bc-b045-452a-84e0-87367d4c680e@bastelstu.be> Date: Thu, 11 Jul 2024 16:40:03 -0700 Message-ID: Subject: Re: [PHP-DEV] [RFC] Lazy Objects To: =?UTF-8?Q?Tim_D=C3=BCsterhus?= Cc: PHP Internals List , Nicolas Grekas Content-Type: multipart/alternative; boundary="00000000000047c718061d014849" From: kontakt@beberlei.de (=?UTF-8?Q?Benjamin_Au=C3=9Fenhofer?=) --00000000000047c718061d014849 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Am 11.07.2024, 20:31:44 schrieb Tim D=C3=BCsterhus : > Hi > > On 7/11/24 10:32, Nicolas Grekas wrote: > > > Many things are already possible in userland. That does not always mean > > > that the cost-benefit ratio is appropriate for inclusion in core. I get > > > behind the two examples in the =E2=80=9CAbout Lazy-Loading Strategies= =E2=80=9D section, > > > but I'm afraid I still can't wrap my head why I would want an object > > > that makes itself lazy in its own constructor: I have not yet seen a > > > real-world example. > > > > > > Keeping this capability for userland is not an option for me as it would > > mostly defeat my goal, which is to get rid of any userland code on this > > topic (and is achieved by the RFC). > > > Here is a real-world example: > > > https://github.com/doctrine/DoctrineBundle/blob/2.12.x/src/Repository/Laz= yServiceEntityRepository.php > > > This class currently uses a poor-man's implementation of lazy objects and > > would greatly benefit from resetAsLazyGhost(). > > > > Sorry, I was probably a little unclear with my question. I was not > specifically asking if anyone did that, because I am fairly sure that > everything possible has been done before. > > I was interested in learning why I would want to promote a > "LazyServiceEntityRepository" instead of the user of my library just > making the "ServiceEntityRepository" lazy themselves. > > I understand that historically making the "ServiceEntityRepository" lazy > yourself would have been very complicated, but the new RFC makes this > super easy. > > So based on my understanding the "LazyServiceEntityRepository" > (c|sh)ould be deprecated with the reason that PHP 8.4 provides all the > necessary tools to do it yourself, no? That would also match your goal > of getting rid of userland code on this topic. > > To me this is what the language evolution should do: Enable users to do > things that previously needed to be provided by userland libraries, > because they were complicated and fragile, not enabling userland > libraries to simplify things that they should not need to provide in the > first place because the language already provides it. > I agree with Tim here, the Doctrine ORM EntityRepository plus Symfony Service Entity Repository extension are not a necessary real world case that would require this RFC to include a way for classes to make themselves lazy. I took the liberty at rewriting the code of DefaultRepositoryFactory (Doctrine code itself) and ContainerRepositoryFactory in a way to make the repositories lazy without needing resetAsLazy, just $reflector->createLazyProxy. In case of the second the LazyServiceEntityRepository class could be deleted. https://gist.github.com/beberlei/80d7a3219b6a2a392956af18e613f86a Please let me know if this is not how it works or can work or if my reasoning is flawed. Unless you have no way of getting to the =E2=80=9Enew $object=E2=80=9C in t= he code, there is always a way to just use newLazy*. And when a library does not expose new $object to you to override, then that is an architectural choice (and maybe flaw that you have to accept). I still think not having the reset* methods would greatly simplify this RFC and would allow to force more constraints, have less footguns. For example we could simplify the API of newLazyProxy to not receive a $factory that can arbitrarily create and get objects from somewhere, but also initializer and always force the lazy object to be an instance created by newInstanceWithoutConstructor. You said in a previous mail about reset*() From a technical pov, this is just a different flavor of the same code > infrastructure, so this is pretty aligned with the rest of the proposed > API. > We are not specifically considering the technical POV, but even more importantly the user facing API. And this just adds to the surface of the API a lot of things that are pushing only a 1-5% edge case. > > I have one question regarding the updated initialization sequence. The > > > RFC writes: > > > > > >> Properties that are declared on the real instance are uninitialized on > > > the proxy instance (including overlapping properties used with > > > ReflectionProperty::skipLazyInitialization() or > > > setRawValueWithoutLazyInitialization()) to synchronize the state shared > by > > > both instances. > > > > > > I do not understand this. Specifically I do not understand the "to > > > synchronize the state" bit. > > > > We reworded this sentence a bit. Clearer? > > > Yes, I think it is clearer. Let me try to rephrase this differently to > see if my understanding is correct: > > --- > > For every property on that exists on the real instance, the property on > the proxy instance effectively [1] is replaced by a property hook like > the following: > > public PropertyType $propertyName { > get { > return $this->realInstance->propertyName; > } > set(PropertyType $value) { > $this->realInstance->propertyName =3D $value; > } > } > > And value that is stored in the property will be freed (including > calling the destructor if it was the last reference), as if `unset()` > was called on the property. > > [1] No actual property hook will be created and the `realInstance` > property does not actually exist, but the semantics behave as if such a > hook would be applied. > > --- > > > > My understanding is that the proxy will > > > always forward the property access, so there effectively is no state on > > > the proxy?! > > > > It follows that more properties can exist on the proxy itself (declared b= y > > child classes of the real object that the proxy implements). > > > > Right, that's mentioned in (2), so all clear. > > > >> That is very true. I had a look at the userland implementation and > > > indeed, > > >> we keep the wrapper while cloning the backing instance (it's not that = we > > >> have the choice, the engine doesn't give us any other options). > > >> RFC updated. > > >> > > >> We also updated the behavior when an uninitialized proxy is cloned: we > > > now > > >> postpone calling $real->__clone to the moment where the proxy clone is > > >> initialized. > > > > > > Do I understand it correctly that the initializer of the cloned proxy i= s > > > effectively replaced by the following: > > > > > > function (object $clonedProxy) use ($originalProxy) { > > > return clone $originalProxy->getRealObject(); > > > } > > > > > > Nope, that's not what we describe in the RFC so I hope you can read it > > again and get where you were confused and tell us if we're not clear enou= gh > > (to me we are :) ) > > > The "cloning of the real instance" bit is what lead me to this > understanding. > > The $originalProxy is *not* shared with $clonedProxy. Instead, it's > > *initializers* that are shared between clones. > > And then, when we call that shared initializer in the $clonedProxy, we > > clone the returned instance, so that even if the initializer returns a > > shared instance, we don't share anything with the $originalProxy. > > > > Ah, so you mean if the initializer would look like this instead of > creating a fresh object within the initializer? > > $predefinedObject =3D new SomeObj(); > $myProxy =3D $r->newLazyProxy(function () use ($predefinedObject) { > return $predefinedObject; > }); > $clonedProxy =3D clone $myProxy; > $r->initialize($myProxy); > $r->initialize($clonedProxy); > > It didn't even occur to me that one would be able to return a > pre-existing object: I assume that simply reusing the initializer would > create a separate object and that would be sufficient to ensure that the > cloned instance would be independent. > > > > ? Then I believe this is unsound. Consider the following: > > > > > > $myProxy =3D $r->newLazyProxy(...); > > > $clonedProxy =3D clone $myProxy; > > > $r->initialize($myProxy); > > > $myProxy->someProp++; > > > var_dump($clonedProxy->someProp); > > > > > > The clone was created before `someProp` was modified, but it outputs th= e > > > value after modification! > > > > > > Also: What happens if the cloned proxy is initialized *before* the > > > original proxy? There is no real object to clone. > > > > > > I believe the correct behavior would be: Just clone the proxy and keep > > > the same initializer. Then both proxies are actually fully independent > > > after cloning, as I would expect from the clone operation. > > > > > > That's basically what we do and what we describe in the RFC, just with th= e > > added lazy-clone operation on the instance returned by the initializer. > > > > This means that if I would return a completely new object within the > initializer then for a cloned proxy the new object would immediately be > cloned and the original object be destructed, yes? > > Frankly, thinking about this cloning behavior gives me a headache, > because it quickly leads to very weird semantics. Consider the following > example: > > $predefinedObject =3D new SomeObj(); > $initializer =3D function () use ($predefinedObject) { > return $predefinedObject; > }; > $myProxy =3D $r->newLazyProxy($initializer); > $otherProxy =3D $r->newLazyProxy($initializer); > $clonedProxy =3D clone $myProxy; > $r->initialize($myProxy); > $r->initialize($otherProxy); > $r->initialize($clonedProxy); > > To my understanding both $myProxy and $otherProxy would share the > $predefinedObject as the real instance and $clonedProxy would have a > clone of the $predefinedObject at the time of the initialization as its > real instance? > > To me this sounds like cloning an uninitialized proxy would need to > trigger an initialization to result in semantics that do not violate the > principle of least astonishment. > > I would assume that cloning a proxy is something that rarely happens, > because my understanding is that proxies are most useful for service > objects, whereas ghost objects would be used for entities / value > objects, so this should not be too much of a problem. > > > 2. > > > > > > > Properties are not initialized to their default value yet (they are > > > initialized before calling the initializer). > > > > > > I see that you removed the bit about this being not observable. What is > > > the reason that you removed that? One possible reason that comes to my > > > mind is a default value that refers to a non-existing constant. It woul= d > > > be observable because the initialization emits an error. Are there any > > > other reasons? > > > > > > That's because this is observable using e.g. (array) or var_dump. > > > > I see. Perhaps add a short sentence with the reasoning. Something like: > > Properties are not initialized to their default value yet (they are > initialized before calling the initializer). As an example, this has an > impact on the behavior of an (array) cast on uninitialized objects and > also when the default value is based on a constant that is not yet > defined when creating the lazy object, but will be defined at the point > of initialization. > > Best regards > Tim D=C3=BCsterhus > --00000000000047c718061d014849 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


Am 11.07.2024, 20:31:44 schrieb T= im D=C3=BCsterhus <tim@bastelstu.be<= /a>>:
=20
Hi

On 7/11/24 10:32, Nicolas Grekas wrote:
> Many things are already possible in userland. That does not = always mean
> that the cost-be= nefit ratio is appropriate for inclusion in core. I get
> behind the two examples in the =E2=80=9CAbout L= azy-Loading Strategies=E2=80=9D section,
> but I'm afraid I still can't wrap my head why I woul= d want an object
> that makes = itself lazy in its own constructor: I have not yet seen a
<= blockquote type=3D"cite">> real-world example.
>

Keeping this capability for userland is n= ot an option for me as it would
= mostly defeat my goal, which is to get rid of any userland code on this
=
topic (and is achieved by the RFC).=

Here is a real-world example:
https://github.com/doctri= ne/DoctrineBundle/blob/2.12.x/src/Repository/LazyServiceEntityRepository.ph= p

This class currently uses a poor-man's implementation= of lazy objects and
would great= ly benefit from resetAsLazyGhost().


Sorry, I was probably a little unclear with my que= stion. I was not
specifically asking if anyone did that, because I am f= airly sure that
everything possible has been done before.

I was = interested in learning why I would want to promote a
"LazyServiceE= ntityRepository" instead of the user of my library just
making the= "ServiceEntityRepository" lazy themselves.

I understand t= hat historically making the "ServiceEntityRepository" lazy
yo= urself would have been very complicated, but the new RFC makes this
sup= er easy.

So based on my understanding the "LazyServiceEntityRep= ository"
(c|sh)ould be deprecated with the reason that PHP 8.4 pro= vides all the
necessary tools to do it yourself, no? That would also ma= tch your goal
of getting rid of userland code on this topic.

To = me this is what the language evolution should do: Enable users to do
th= ings that previously needed to be provided by userland libraries,
becau= se they were complicated and fragile, not enabling userland
libraries t= o simplify things that they should not need to provide in the
first pla= ce because the language already provides it.

I agree with Tim here, the Doctrine ORM = EntityRepository plus Symfony Service Entity Repository extension are not a= necessary real world case that would require this RFC =C2=A0to include a w= ay for classes to make themselves lazy.

I took the libert= y at rewriting the code of DefaultRepositoryFactory (Doctrine code itself) = and ContainerRepositoryFactory in a way to make the repositories lazy witho= ut needing resetAsLazy, just $reflector->createLazyProxy. In case of the= second the LazyServiceEntityRepository class could be deleted.


Please let me know if this is not how it works= or can work or if my reasoning is flawed.

Unless you hav= e no way of getting to the =E2=80=9Enew $object=E2=80=9C in the code, there= is always a way to just use newLazy*. And when a library does not expose n= ew $object to you to override, then that is an architectural choice (and ma= ybe flaw that you have to accept).

I still think not havi= ng the reset* methods would greatly simplify this RFC and would allow to fo= rce more constraints, have less footguns.=C2=A0

For examp= le we could simplify the API of newLazyProxy to not receive a $factory that= can arbitrarily create and get objects from somewhere, but also initialize= r and always force the lazy object to be an instance created by newInstance= WithoutConstructor.

<= div class=3D"gmail_quote" dir=3D"ltr">You said in a previous mail about res= et*()

From a technical pov, this is just=C2=A0=C2=A0a different flavor of the same code infrastructure, so thi= s is pretty=C2=A0=C2=A0aligned with the rest of the = proposed API.

We are not specifical= ly considering the technical POV, but even more importantly the user facing= API. And this just adds to the surface of the API a lot of things that are= pushing only a 1-5% edge case.


> I have one question regar= ding the updated initialization sequence. The
> RFC writes:
>= ;
>> Properties that are de= clared on the real instance are uninitialized on
> the proxy instance (including overlapping properties u= sed with
> ReflectionProperty:= :skipLazyInitialization() or
>= setRawValueWithoutLazyInitialization()) to synchronize the state shared by=
> both instances.
>
> I do not understand this. Specifically I do not understand the &quo= t;to
> synchronize the state&q= uot; bit.


We reword= ed this sentence a bit. Clearer?

Yes, I think it is cle= arer. Let me try to rephrase this differently to
see if my understandin= g is correct:

---

For every property on that exists on the re= al instance, the property on
the proxy instance effectively [1] is repl= aced by a property hook like
the following:

=C2=A0=C2=A0=C2=A0= =C2=A0public PropertyType $propertyName {
=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0get {
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return $this->realInstance->propertyNam= e;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0}
=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0set(PropertyType $value) {
=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$this->= ;realInstance->propertyName =3D $value;
=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0}
=C2=A0=C2=A0=C2=A0=C2=A0}

And value that = is stored in the property will be freed (including
calling the destruct= or if it was the last reference), as if `unset()`
was called on the pro= perty.

[1] No actual property hook will be created and the `realInst= ance`
property does not actually exist, but the semantics behave as if = such a
hook would be applied.

---


> My understanding is that the proxy will
> always forward the property access, so there effect= ively is no state on
> the pro= xy?!


It follows tha= t more properties can exist on the proxy itself (declared by
child classes of the real object that the prox= y implements).

=
Right, that's mentioned in (2), so all clear.


>> That is ve= ry true. I had a look at the userland implementation and
> indeed,
>> we keep the wrapper while cloning the backing instance (it'= ;s not that we
>> have the = choice, the engine doesn't give us any other options).
=
>> RFC updated.
>>
>> = We also updated the behavior when an uninitialized proxy is cloned: we
<= /blockquote>
> now
>> postpone calling $real->__clone to the moment whe= re the proxy clone is
>> in= itialized.
>
<= blockquote type=3D"cite">> Do I understand it correctly that the initial= izer of the cloned proxy is
> = effectively replaced by the following:
>
> =C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0function (object $clonedProxy) use ($originalProxy) {<= br>
> =C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return clone $originalProxy->getRealObj= ect();
> =C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0}
>

= Nope, that's not what we describe in the RFC so I hope you can read it=
again and get where you were co= nfused and tell us if we're not clear enough
(to me we are :) )

The "cloning o= f the real instance" bit is what lead me to this
understanding.
The $originalProxy is *not* shared with $cl= onedProxy. Instead, it's
*in= itializers* that are shared between clones.
And then, when we call that shared initializer in the $clonedPr= oxy, we
clone the returned insta= nce, so that even if the initializer returns a
shared instance, we don't share anything with the $origi= nalProxy.


A= h, so you mean if the initializer would look like this instead of
creat= ing a fresh object within the initializer?

=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0$predefinedObject =3D new SomeObj();
=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0$myProxy =3D $r->newLazyProxy(function () use ($predefinedObject) { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return $predefined= Object;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0});
=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0$clonedProxy =3D clone $myProxy;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$r= ->initialize($myProxy);
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$r->initial= ize($clonedProxy);

It didn't even occur to me that one would be = able to return a
pre-existing object: I assume that simply reusing the = initializer would
create a separate object and that would be sufficient= to ensure that the
cloned instance would be independent.


> ? Then= I believe this is unsound. Consider the following:
>
> =C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$myProxy =3D $r->newLazyProxy(...);
=
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0$clonedProxy =3D clone $myProxy;
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$r->initialize($myProxy);=
> =C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0$myProxy->someProp++;
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0var_dump($clonedProxy->somePro= p);
>
> The clone was created before `someProp` was modified= , but it outputs the
> value a= fter modification!
>
> Also: What happens if the cloned prox= y is initialized *before* the
>= ; original proxy? There is no real object to clone.
>
> I be= lieve the correct behavior would be: Just clone the proxy and keep
> the same initializer. Then both prox= ies are actually fully independent
> after cloning, as I would expect from the clone operation.
>

That's basically what= we do and what we describe in the RFC, just with the
added lazy-clone operation on the instance returned b= y the initializer.


This means that if I would return a completely new object within th= e
initializer then for a cloned proxy the new object would immediately = be
cloned and the original object be destructed, yes?

Frankly, t= hinking about this cloning behavior gives me a headache,
because it qui= ckly leads to very weird semantics. Consider the following
example:
=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$predefinedObject =3D new SomeObj();
= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$initializer =3D function () use ($predefine= dObject) {
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return= $predefinedObject;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0};
=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0$myProxy =3D $r->newLazyProxy($initializer);
=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0$otherProxy =3D $r->newLazyProxy($initializer= );
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$clonedProxy =3D clone $myProxy;
= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0$r->initialize($myProxy);
=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0$r->initialize($otherProxy);
=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0$r->initialize($clonedProxy);

To my understanding both $= myProxy and $otherProxy would share the
$predefinedObject as the real i= nstance and $clonedProxy would have a
clone of the $predefinedObject at= the time of the initialization as its
real instance?

To me this= sounds like cloning an uninitialized proxy would need to
trigger an in= itialization to result in semantics that do not violate the
principle o= f least astonishment.

I would assume that cloning a proxy is somethi= ng that rarely happens,
because my understanding is that proxies are mo= st useful for service
objects, whereas ghost objects would be used for = entities / value
objects, so this should not be too much of a problem.<= br>
> 2.
>
> =C2=A0=C2=A0&= gt; Properties are not initialized to their default value yet (they are
=
> initialized before calling the = initializer).
>
> I see that you removed the bit about this = being not observable. What is
>= ; the reason that you removed that? One possible reason that comes to my
> mind is a default value that r= efers to a non-existing constant. It would
> be observable because the initialization emits an error. Are= there any
> other reasons?
>

That's because= this is observable using e.g. (array) or var_dump.


I see. Perhaps add a short sentenc= e with the reasoning. Something like:

Properties are not initialized= to their default value yet (they are
initialized before calling the in= itializer). As an example, this has an
impact on the behavior of an (ar= ray) cast on uninitialized objects and
also when the default value is b= ased on a constant that is not yet
defined when creating the lazy objec= t, but will be defined at the point
of initialization.

Best rega= rds
Tim D=C3=BCsterhus
=20 --00000000000047c718061d014849--