Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:123515 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 15A871A009C for ; Wed, 5 Jun 2024 12:34:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1717590909; bh=ZZ9CC3HnZBVSUKGZAqTE/2o0aoKKmt8xaSl8lEd0YQA=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=XiSiMGykOnBl7Ip3Uik4inXVpRMNzybYXLe+gI+OKyKAmGoJOkD/YdCLd4JMVLWnp cF9HUwTKZzPolYU32FpuU9CmqDv/TYT0PxskUdyuT2bIp/O76gG/jbnUNFDTuK72vU UzTJ0DsRa91rnfSsk2E4FpRJQ0kUWIY5RSoKYYzCC6EOD4gPRzywRaRIYiaT+7v4GE RbGRxsrXc/0hy5kU+Aoc4OFIOBDE7zpuvWkxg/PETbKCE820RuVuCIJsDIg0aG1y8U tHfiRTjlC1htteKoC8+HbK01fw23uZ9zK8Va+yW09nC5vlzk9id4URxlNCrmpar33X t6ZMndrumUVmA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2ECBB180039 for ; Wed, 5 Jun 2024 12:35:08 +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-lj1-f174.google.com (mail-lj1-f174.google.com [209.85.208.174]) (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 ; Wed, 5 Jun 2024 12:35:07 +0000 (UTC) Received: by mail-lj1-f174.google.com with SMTP id 38308e7fff4ca-2eaa80cb550so28297311fa.0 for ; Wed, 05 Jun 2024 05:34:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1717590842; x=1718195642; 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=XRIs0UOaIRsMVgJ7oXilKwYandvH/y4CuRImXsfw50I=; b=ngKL2lQslYEGLmlIxgNiSP+B9rjJSl2UOjB7sfkWfJOHFM5IamHWD9z9Vu74bwOKje o4bqNWmuo0B5xwIEmFkHJwVPcDKsmNmg9jN55IbOKpCyvgG5/8PtGf/l0knWomaelzE7 bwAyUPPDdcu0uAvqdZYJZgjxNlpPEBFl9c13SQ0nfURks9G9Jxy9zAtjk6WgpLXfO9st BxE2XoxxOwaJvk1oCkNBzIWl3nSa2Q7Ii6wB+qKLCxIcMwGB/4XETqRGs9yLu9WxFy2j ySjPwkahiAAI+ahL66xxU4ZnKhig4fxWR3m5hHjRokQZaVusxyOL2NvjRWEQDvOKq0xH 3efw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717590842; x=1718195642; 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=XRIs0UOaIRsMVgJ7oXilKwYandvH/y4CuRImXsfw50I=; b=ZWfkYPbW+TcuQJgaPLTrk1SGMv2ot2u+tcuq0aP2JbFJwvbEMRwB8GJIlZUVE/F/g7 M6OFr0Vv/MbpOSTMS8A+I13csvkR819ZdHj7gyMWF50ZdEeKRVCgq4qColeaz4rLgTHi wbWChW0JEKUqvueHYU1tFWkAQGWIb8apH0ZOAgJDccdfuO/3i7UYzdn7DBCU7zYh4r7B TlTqstffXdeqhavQz6878S/2HW+bPiaHt57BgyBmSgCDsxaO47r+xvtgl1g1qg1L9Hjq GzNAFzZlOUJg/h2GQWxFfXTlHdbty6qvhUDR8RKNMw0v3rViEDIozz0j0ztsV+N5gxHF jSog== X-Forwarded-Encrypted: i=1; AJvYcCWvD1d5Hc5MI42VyNPgQrSfqF5z5TIMV9Vh4VhhzAOTGgt4e9evEOjJCk/vs371rnceJdfz62Zs7YrcHVsczmG4NeCjcOenZA== X-Gm-Message-State: AOJu0Yx3f7ZSGWhdr/jsQq+9Z8xPWQ2PPYFk9hjCrfcVS1/OOBZ+9qb2 vc5IPIMF2YOegazH/c/sKomOhDMJ1DHXe3XCNNMlkw3zWPSuvB2CqT0miUb5zdPg41NO3TlDxYA Xwyw2YAITz2Clgz8GsyDzaXrmNjs= X-Google-Smtp-Source: AGHT+IHr6HfKZggRLt6Z8KnKf5Z73Mhevbgam0cTV48pa5k72OL8JcIJP18p6nYSr1LH0Mkh31V68QrQ8z7wna5TGWQ= X-Received: by 2002:a05:651c:210e:b0:2e9:795a:c25c with SMTP id 38308e7fff4ca-2eac7a95374mr18271401fa.39.1717590841434; Wed, 05 Jun 2024 05:34:01 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 References: In-Reply-To: Date: Wed, 5 Jun 2024 14:33:49 +0200 Message-ID: Subject: Re: [PHP-DEV] [RFC] Lazy Objects To: =?UTF-8?Q?Tim_D=C3=BCsterhus?= Cc: Nicolas Grekas , PHP Internals List , Arnaud Le Blanc Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: landers.robert@gmail.com (Robert Landers) On Tue, Jun 4, 2024 at 10:23=E2=80=AFPM Tim D=C3=BCsterhus wrote: > > Hi > > On 6/4/24 14:28, Nicolas Grekas wrote: > > Please find all the details here: > > https://wiki.php.net/rfc/lazy-objects > > > > We look forward to your thoughts and feedback. > > I've gave the RFC three or four passes and I'm not quite sure if I > follow everything, here's a list of some questions / remarks that came > to mind, roughly ordered by the order of things appearing in the RFC. > > - "been tested successfully on the Doctrine and on the Symfony projects" > > Is there a PoC patch showcasing how the code would change / be > simplified for those pre-existing codebases? > > - int $options =3D 0 > > Not a fan of flag parameters that take a bitset, those provide for a > terrible DX due to magic numbers. Perhaps make this a regular (named) > parameter, or an list of enum LazyObjectOptions { case > SkipInitOnSerialize; }? > > - skipProperty() > > Not a fan of the method name, because it doesn't really say what it > does, without consulting the docs. Perhaps `skipInitializationFor()` or > similar? > > - setProperty() > > Not a fan of the method name, because it is not a direct counterpart to > `getProperty()`. Unfortunately I don't have a better suggestion. > > - The examples should be expanded and clarified, especially the one for > makeLazyProxy(): > > My understanding is that the $object that is passed to the first > parameter of makeLazyProxy() is completely replaced. Is this > understanding correct? What does that mean for spl_object_hash(), > spl_object_id()? What does this mean for WeakMap and WeakReference? What > does this mean for objects that are only referenced from within $object? > > 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? > > - What happens if I make an object lazy that already has all properties > initialized? Will that be a noop? Will that throw? Will that create a > lazy object that will never automatically be initialized? > > - Cloning, unless __clone() is implemented and accesses a property. > > The semantics of cloning a lazy object should be explicitly spelled out > in the RFC, ideally with an example of the various edge cases (should > any exist). > > - Before calling the initializer, properties that were not initialized > with ReflectionLazyObject::skipProperty(), > ReflectionLazyObject::setProperty(), > ReflectionLazyObject::setRawProperty() are initialized to their default > value. > > Should skipProperty() also skip the initialization to the default value? > My understanding is that it allows skipping the initialization on > access, but when initialization actually happens it should probably be > set to a well-defined value, no? > > Am I also correct in my understanding that this should read "initialized > to their default value (if any)", meaning that properties without a > default value are left uninitialized? > > - If an exception is thrown while calling the initializer, the object is > reverted to its pre-initialization state and is still considered lazy. > > Does this mean that the initializer will be called once again when > accessing another property? Will the "revert to its pre-initialization" > work properly when you have nested lazy objects? An example would > probably help. > > - The initializer is called with the object as first parameter. > > What is the behavior of accessing the object properties, while the > initializer is active? Based on the examples, I assume it will not be > recursively called, similarly to how the hooks work? > > - The object is marked as non-lazy and the initializer is released. > > What does it mean for the initializer to be released? Consider the > following example: > > ReflectionLazyObject::makeLazyGhost($o, $init =3D function ($o) use > (&$init) { > $o->init =3D $init; > }); > > - The return value of the initializer has to be an instance of a parent > or a child class of the lazy-object and it must have the same properties. > > 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 > > - The destructor of lazy non-initialized objects is not called. > > That sounds unsafe. Consider the following example: > > class Mutex { > public string $s; > public function __construct() { > // take lock > } > > public function __destruct() { > // release lock > } > } > > $m =3D new Mutex(); > ReflectionLazyObject::makeLazyGhost($m, function ($m) { > }); > unset($m); // will not release the lock. > > - Using the Manager::createManager() factory is not compatible with > ghost objects because their initializer requires initializing the ghost > object in place, > > I don't understand that example, because it doesn't actually use lazy > objects and thus I don't understand if Manager, Dispatcher, or both are > intended to be lazily initialized. It would help to rewrite the example > to use `makeLazyGhost()` and indicate with a comment in which cases the > problem would arise. > > - Backward Incompatible Changes > > There a technicality: The `ReflectionLazyObject` class name will no > longer be available to userland. > > Best regards > Tim D=C3=BCsterhus As someone who has had to maintain these proxies/ghosts before, this looks quite useful and powerful. I feel it has rather wonky syntax, but it is clearly better than the alternative of implementing it yourself. I'm also a huge fan that the syntax allows for usage/creation far and away from the definition/class itself. Good luck, and I hope it passes. Robert Landers Software Engineer Utrecht NL