Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:123601 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 B53711A009C for ; Fri, 14 Jun 2024 12:13:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1718367292; bh=ZsOsiUhJ4ZKQYth9qAxuQsTQgl2rGsVdnpNTUAtRbtw=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=ScFDqfiv1RNoTdV5E7RJB4131TfAVmJvjYGjPVwdJGOAwxfqcUVYMqC/TKf0dLvA+ Q0Y0OgLWTsOt8AhEyol9Z+Dhp4wEKtHByE2av4E/2vyjIgXF5mqhZgp2wrjCWh2qOO YPY3RRsJZvmHw4xf6pbx+n6joNPA6vWYS6MxcVuKoiIJ1PHLKaCzbOil/uGbfd8US3 TjYifo/uDCan4chp6Z/wtuIcvauQIvGaWUsVJ6w+1CTF4Q9j5dWZ0A3/nuCiyAs4OW yVnfDmW1/7RoJgQcn2ZkkQUvdtZnb9EtZt52kfej8zYO2wuZI/sxLRmLrEwlSD2FOr rcFPHo5H1c2vg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 877B7180649 for ; Fri, 14 Jun 2024 12:14:51 +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.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from mail-ed1-f49.google.com (mail-ed1-f49.google.com [209.85.208.49]) (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 ; Fri, 14 Jun 2024 12:14:51 +0000 (UTC) Received: by mail-ed1-f49.google.com with SMTP id 4fb4d7f45d1cf-57cbc66a0a6so1770865a12.1 for ; Fri, 14 Jun 2024 05:13:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718367220; x=1718972020; darn=lists.php.net; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=nvYb8zclKwp90nOnne+IH64gINdtXiQ1fzfbd6M6iRA=; b=ZFkZyGFIzOAa/tRv9rFzOx6G7ql9l9OzI/ThYKcHKRasijG8Hz3srrS7uqLi5s8Ykc wI1i45T0IIm1El2IUN3UuhIUXameyKWHAs4DxymKI/Me8bfjDswFCzUC2QeuYzFEXivY cAuoLhYNjLGoJVdMCAps/qamjoXA17s5fNqhZ5qIb7rNY7go9XBy5+AEsyMF9gH7lkds zBlY29xLeDIs62+DAc9fpF6+6H/JKOYlSo31a9jhwQlAmX0wi/MniaMbXlJ61wi2kCU7 iJYOWbdeYJ92kW+8U1ZPd/sLeEvAUMRJObceHfimqJzJ/kwfjlw9YapTViJVzFWSHHz/ t5hg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718367220; x=1718972020; h=content-transfer-encoding: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=nvYb8zclKwp90nOnne+IH64gINdtXiQ1fzfbd6M6iRA=; b=P2xWE7mroaGFlm/i4zIrXIar1VwCemk2j6V3VxksZVzle9vSWhTLk0Wn5HVeFscZGG P+Mdv31dSCNJgQrMx5IaoD3ZWrH2Z8RgdX93ZfIhwyhIPuJJY1PbAJfyYMFSBl0KtNGI IQkxWIu1XNYxxcmXg0qykMSL0cjL1MJHse9zXyVCFNYEy6Wfb/Dpt03ereNLHGNkKqBu Qkm4TOPdm/BIARSDovEIkwlpVvQK+kDMiFukQrsjSfKhYHh5kZUrMJWAbAAjzl9lSnhP g29mHncuOLlfqda1eshJX0tB/XXU5go3gofLl5K9VEWBnq65IlTForx/Yg/uV2WoJMGq hKkQ== X-Forwarded-Encrypted: i=1; AJvYcCVPWUzQDMsJt1xaSILDBWPxaPjFZFHVH+SiurDjzKbxr1XclPHhhOpTW2v/VAPrOPcUdpyZ4vPmdWO4uHOEPgFTy1XFbXUG+Q== X-Gm-Message-State: AOJu0Yw+CdGEBIPe6xRBjiLgv61EE2+1mGZ+xYIzAHjduE+R0dG8eDZ/ /Jyi2JJrTIGCs81iE15CvN0icqI+tstB4NNt7alU5dB/Wsgf66ZFUj6WW/f1YoJGp/TIAgjE4CA reJutas4VElHjR50kgDEVUaZ+5Xc= X-Google-Smtp-Source: AGHT+IEfk+LTyVQUzYE/pUIK3ju2PrGK98KXQ79p1dbj476okDpD/aqUcN0XgIhOTDLAyj+Rm1k4FvmQ9nOd/4Wg3mA= X-Received: by 2002:aa7:cf0c:0:b0:57c:b976:9223 with SMTP id 4fb4d7f45d1cf-57cb97692b2mr3411469a12.16.1718367219614; Fri, 14 Jun 2024 05:13:39 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 References: <1118bbcd-a7b4-47bf-bf35-1a36ab4628e1@bastelstu.be> In-Reply-To: <1118bbcd-a7b4-47bf-bf35-1a36ab4628e1@bastelstu.be> Date: Fri, 14 Jun 2024 14:13:28 +0200 Message-ID: Subject: Re: [PHP-DEV] [RFC] Lazy Objects To: =?UTF-8?Q?Tim_D=C3=BCsterhus?= Cc: Nicolas Grekas , PHP Internals List Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: arnaud.lb@gmail.com (Arnaud Le Blanc) Hi Tim, We have updated the RFC to address your feedback. Please find additional answers below. On Wed, Jun 5, 2024 at 8:25=E2=80=AFPM Tim D=C3=BCsterhus wrote: > Is there any reason to call the makeLazyX() methods on an object that > was not just freshly created with ->newInstanceWithoutConstructor() > then? There are not many reasons to do that. The only indented use-case that doesn't involve an object freshly created with ->newInstanceWithoutConstructor() is to let an object manage its own laziness by making itself lazy in its constructor: ``` class C { public function __construct() { new ReflectionLazyObjectFactory(C::class)->makeInstanceLazyGhost($t= his, $this->init(...)); } } ``` When drafting this API we figured that makeLazy addressed all use-cases, and this allowed us to keep the API minimal. However, following different feedback we have now introduced two separate newLazyGhostInstance() and newLazyProxyInstance() methods. > Anything I do with the object before the call to makeLazyX() will > effectively be reverted, no? Yes, after calling makeLazy() the object is in the same state as if it was made lazy immediately after instantiation. > An example showcasing the intended usage, e.g. a simplified ORM example, > would really be helpful here. Agreed > > in userland by iterating on all properties via the Reflection API, and > > using unset() in the right scope with a Closure. > > > > spl_object_id(), spl_object_hash(), SplObjectStorage, WeakMap, > > WeakReference, strict equality, etc are not affected by makeLazy*(). > > That is true for *both* makeLazyGhost(), and makeLazyProxy()? > > What would the following example output? > > $object =3D new MyObject(); > var_dump(spl_object_id($object)); > $r =3D ReflectionLazyObject::makeLazyGhost($object, function > (MyObject $object) { > $object2 =3D new MyObject(); > var_dump(spl_object_id($object2)); > return $object2; > }); > var_dump(spl_object_id($object)); > $r->initialize(); > var_dump(spl_object_id($object)); > > What would happen if I would expose the inner $object2 to the outer > world by means of the super globals or by means of `use (&$out)` + `$out > =3D $object2`? We have clarified the RFC on these points > > The intended use of makeLazyGhost() and makeLazyProxy() is to call > > them either on an object created with > > ReflectionClass::newInstanceWithoutConstructor(), or on $this in a > > constructor. The latter is the reason why these APIs take an existing > > object. > > Okay, that answers the question above. Technically being capable of > calling it on an object that was not just freshly created sounds like a > footgun, though. What is the interaction with readonly objects? My > understanding is that it would allow an readonly object with initialized > properties to change after-the-fact? Good point about readonly, this is something we had overlooked. We have updated the RFC to address this. > >> Consider this example: > >> > >> class Foo { > >> public function __destruct() { echo __METHOD__; } > >> } > >> > >> class Bar { > >> public string $s; > >> public ?Foo $foo; > >> > >> public function __destruct() { echo __METHOD__; } > >> } > >> > >> $bar =3D new Bar(); > >> $bar->foo =3D new Foo(); > >> > >> ReflectionLazyObject::makeLazyProxy($bar, function (Bar $bar) { > >> $result =3D new Bar(); > >> $result->foo =3D null; > >> $result->s =3D 'init'; > >> return $result; > >> }); > >> > >> var_dump($bar->s); > >> > >> My understanding is that this will dump `string(4) "init"`. Will the > >> destructor of Foo be called? Will the destructor of Bar be called? > > > > This will print: > > > > Foo::__destruct (during makeLazyProxy()) > > string(4) "init" (during var_dump()) > > > > and eventually > > > > Bar::__destruct (when $bar is released) > > Okay, so only one Bar::__destruct(), despite two Bar objects being > created. I assume it's the destructor of the second Bar, i.e. if I would > dump `$this->foo` within the destructor, it would dump `null`? Following your feedback we have updated the RFC to include calling destructors in makeLazy by default. In the updated version, Bar::__destruct is called twice: Once on the first instance during the call to makeLazyProxy(), and another time on the second instance (the one created in the closure) when it's released. It is not called when the first instance is released, because it's now a proxy. > >> Will the "revert to its pre-initialization" > >> work properly when you have nested lazy objects? An example would > >> probably help. > > > > Only the effects on the object itself are reverted. External side > > effects are not reverted. > > Yes, it's obvious that external side effects are not reverted. I was > thinking about a situation like: > > $a =3D new A(); > $b =3D new B(); > ReflectionLazyObject::makeLazyGhost($b, function ($b) { > throw new \Exception('xxx'); > }); > ReflectionLazyObject::makeLazyGhost($a, function ($a) use ($b) { > $a->b =3D $b->somevalue; > }); > $a->init =3D 'please'; > > The initialization of $a will implicitly attempt to initialize $b, which > will fail. Am I correct in my understanding that both $a and $b will be > reverted back to a lazy object afterwards? If so, adding that example to > the RFC would help to make possible edge cases clear. We have added an example showing that. > >> - The return value of the initializer has to be an instance of a paren= t > >> or a child class of the lazy-object and it must have the same properti= es. > >> > >> Would returning a parent class not violate the LSP? Consider the > >> following example: > >> > >> class A { public string $s; } > >> class B extends A { public function foo() { } } > >> > >> $o =3D new B(); > >> ReflectionLazyObject::makeLazyProxy($o, function (B $o) { > >> return new A(); > >> }); > >> > >> $o->foo(); // works > >> $o->s =3D 'init'; > >> $o->foo(); // breaks > > > > $o->foo() calls B::foo() in both cases here, as $o is always the proxy > > object. We need to double check, but we believe that this rule doesn't > > break LSP. > > I don't understand what happens with the 'A' object then, but perhaps > this will become clearer once you add the requested examples. The 'A' object is what is called the "actual instance" in the RFC. $o acts as a proxy to the actual instance: Any property access on $o is forwarded to the actual instance A. Best Regards, Arnaud