Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:123635 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 D8DB81A009C for ; Sun, 16 Jun 2024 13:46:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1718545674; bh=uVwPeaMET8NDFJ0NTCTT63Sy/JKTKXkmVsz0kE3vlvs=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=SKlgm9WPkVSrA8PJ8JwOMhNnxlkJYesdYdXHRA9oNivuCu9QFVflPXkD74jL2v99R j4oiseIo9rVHaTqzZW9nL7+/xhCW9N5yuhmA4merofD/d6YY/cj75Ajh0SN0krUTMj vfcLLzjOeMUfPJRF5E8mcr3PEYS/NIaiOlDhUd9v+BL8fVreoT/q5kRL8CLcER5Lmi 8b7qYy+DQK9IayOrqHXbLWIQF05w6gq+HhUSpndsGl6CfjhwW/6jScH6X/TjQ5LAeX UtezWAqIscqf2pqg/dLuMK7AYgpBGV8CIoDBsmX2AtGffVSx9yOdMRdBIR8DfHv3/4 wFKyB9EQZqvCg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id F2A8A18005D for ; Sun, 16 Jun 2024 13:47:53 +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-lf1-f54.google.com (mail-lf1-f54.google.com [209.85.167.54]) (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 ; Sun, 16 Jun 2024 13:47:53 +0000 (UTC) Received: by mail-lf1-f54.google.com with SMTP id 2adb3069b0e04-52bc29c79fdso4971553e87.1 for ; Sun, 16 Jun 2024 06:46:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718545600; x=1719150400; 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=zo3ZDH9Tihxgg8FXJxLGZwg5YnwJesDO4y+rXEyDjtg=; b=G+TVoMebfZHtaiHNcCx6tOfhLxwCWpvboDreNpPkgkmU8h7VhhiMU1cZ9AZpiLQKiy mvTPmnbvZITfM6KNCmxFf1GumHRWSySp9TccuejrHDAWDduz8+ccuwb+S3ZQ81RGPJrV zWrkrayIJOVpeYy6V+XPMiT1EHd5HQ+v1K8txSkEoG2zdLAcusLjnqrRyyTWq5Z9BV1f 8Pgx4mpX1+TcSI5A4chXqeIENqz1SAEFm+2ngd4UXOFLcWKOA6KcVwuPeUKP+6Sdc7gN 7fg5hDbdftPhutf3oJs08vHzVSnKlO9udiYV/Sy3crOuLforgI5MY+9EOXy38ziCZIq3 SiqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718545600; x=1719150400; 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=zo3ZDH9Tihxgg8FXJxLGZwg5YnwJesDO4y+rXEyDjtg=; b=FWWZEBgc87D6+Dgg+irPse8iSIsqIjZONEryJ3o3abLjMD9zFQS2Un1Wa/YKfAFIgg +tLT+2IqLqT3q4SD4mwwE4ZPQKyAoePuin2S5s7dIT05youTvx9cO68j6neOI/xjauWE DP2WX3gVMvvxJIO9BXYxqjZP+PkxXkvIIlmsIxwvzJBdOoPcgxXFE2aNQrGrpQFHAz9V OLa6ZNfyEXblGVlmcKZsjGgrxJGh8QtIOAndgBPG3ut3ALiC1YOSysh8EGPE2TGPZeze fNJijv1o3Gl+6s5ZWjQQy6sBefkFefGDUR/7UWV9JkrzwfkZjvTJh3B3UsRplamqNd3j 5m1g== X-Gm-Message-State: AOJu0YwPItcjpqQ9u8yoLZnVGblGYODVWs3NO8x/lmA4HN06a7XAGznl xQFVBoi4gbF/vjGfqfL9/xIZfbEvw+1/B3ogESqzrE68ye22gXmDvEBe47305X7VYcnJ/JDK2dD q4taUjHE3TGntovClJBLAS1S2vLmk4Qex X-Google-Smtp-Source: AGHT+IHuSvlhuJ+P8Dcw90uoaPUyeBNZiBbXXRxxoYFUoV8L6tG6NYVgzhO6cSBz2K/CEMVLrqq+TIRow2s1rtq16F8= X-Received: by 2002:a19:2d41:0:b0:52c:868f:a28d with SMTP id 2adb3069b0e04-52ca6e9276fmr5369144e87.50.1718545600148; Sun, 16 Jun 2024 06:46:40 -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: Date: Sun, 16 Jun 2024 15:46:28 +0200 Message-ID: Subject: Re: [PHP-DEV] [RFC] Lazy Objects To: Larry Garfield Cc: php internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: arnaud.lb@gmail.com (Arnaud Le Blanc) On Sat, Jun 15, 2024 at 7:13=E2=80=AFPM Larry Garfield wrote: > > In practice I expect there will be two kinds of ghost initializers: > > - Those that just call one public method of the object, such as the con= structor > > - Those that initialize everything with ReflectionProperty::setValue() > > as in the Doctrine example in the "About Lazy-Loading strategies" > > section > I'm still missing an example with ::bind(). Actually, I tried to write a= version of what I think the intent is and couldn't figure out how. :-) > > $init =3D function() use ($c) { > $this->a =3D $c->get(ServiceA::class); > $this->b =3D $c->get(ServiceB::class); > } > > $service =3D new ReflectionLazyObjectFactory(Service::class, $init); > > // We need to bind $init to $service now, but we can't because $init is a= lready registered as the initializer for $service, and binding creates a ne= w closure object, not modifying the existing one. So, how does this even w= ork? Oh I see. Yes you will not be able to bind $this in a simple way here, but you could bind the scope. This modified example will work: ``` $init =3D function($object) use ($c) { $object->a =3D $c->get(ServiceA::class); $object->b =3D $c->get(ServiceB::class); } $service =3D new ReflectionLazyObjectFactory(Service::class, $init->bindTo(null, Service::class)); ``` If you really want to bind $this you could achieve it in a more convoluted = way: ``` $init =3D function($object) use ($c) { (function () use ($c) { $this->a =3D $c->get(ServiceA::class); $this->b =3D $c->get(ServiceB::class); })->bindTo($object)(); } $service =3D new ReflectionLazyObjectFactory(Service::class, $init); ``` This is inconvenient, but the need or use-case is not clear to me. Could you describe some use-cases where you would hand-write initializers like this? Do you feel that the proposal should provide an easier way to change $this and/or the scope? > > In practice we expect that makeInstanceLazy*() methods will not be > > used on fully initialized objects, and that the flag will be set most > > of the time, but as it is the API is safe by default. > > In the case an object does not have a destructor, it won't make a differe= nce either way, correct? Yes > >> I find it interesting that your examples list DICs as a use case for p= roxies, when I would have expected that to fit ghosts better. The common p= attern, I would think, would be: > > The RFC didn't make it clear enough that the example was about the > > factory case specifically. > > Ah, got it. That makes more sense. > > Which makes me ask if the $initializer of a proxy should actually be call= ed $factory? Since that's basically what it's doing, Good point, $factory would be a good name for this parameter. > and I'm unclear what it would do with the proxy object itself that's pass= ed in. Passing the factory itself as argument could be used to make decisions based on the value of some initialized field, or on the class of the object, or on its identity. I think Nicolas had a real use-case where he detects clones based on the identity of the object: ``` $init =3D function ($object) use (&$originalObject) { if ($object !=3D=3D $originalObject) { // we are initializing a clone } }; $originalObject =3D $reflector->newProxyInstance($init); ``` This was on ghosts, but I think it's also a valid use-case example for prox= ies. > >> ReflectionLazyObjectFactory is a terrible name. Sorry, it is. :-) Es= pecially if it's subclassing ReflectionClass. If it were its own thing, ma= ybe, but it's still too verbose. I know you don't want to put more on the = "dumping ground" fo ReflectionClass, but honestly, that feels more ergonomi= c to me. That way the following are all siblings: > >> > >> newInstance(...$args) > >> newInstanceWithoutConstructor(...$args) > >> newGhostInstance($init) > >> newProxyInstance($init) > >> > >> That feels a lot more sensible and ergonomic to me. isInitialized(), = initialized(), etc. also feel like they make more sense as methods on Refle= ctionObject, not as static methods on a random new class. > > > > Thank you for the suggestion. We will check if this fits the > > use-cases. Moving some methods on ReflectionObject may have negative > > performance implications as it requires creating a dedicated instance > > for each object. Some use-cases rely on caching the reflectors for > > performance. > > > > Best Regards, > > Arnaud > > I'm not clear why there's a performance difference, but I haven't looked = at the reflection implementation in, well, ever. :-) What I meant is that creating an instance (not necessarily of ReflectionObject, but of any class) is more expensive than just doing nothing. The first two loops below would be fine, but the last one would be slower. This can make an important difference in a hot path. ``` foreach ($objects as $object) { ReflectionLazyObject::isInitialized($object); } $reflector =3D new ReflectionClass(SomeClass::class); foreach ($objects as $object) { $reflector->isInitialized($object); } foreach ($objects as $object) { $reflector =3D new ReflectionObject($object); $reflector->isInitialized($object); } ``` > If it has to be a separate object, please don't make it extend Reflection= Class but still give it useful dynamic methods rather than static methods. = Or perhaps even do something like > > $ghost =3D new ReflectionGhostInstance(SomeClass::class, $init); > $proxy =3D new ReflectionProxyINstance(SOmeClass::class, $init); > > And be done with it. (I'm just spitballing here. As I said, I like the = feature, I just want to ensure the ergonomics are as good as possible.) Thank you for your help. We will think about a better API.