Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122511 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 390C01AD8F6 for ; Mon, 26 Feb 2024 23:39:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1708990748; bh=XtojyFXamSYTOuObZgRIaNR5c135yHUAJdkfp3u0qLI=; h=In-Reply-To:References:Date:From:To:Subject:From; b=LWKbhgcjGdq3+9N+70k2SMSW8CmgXl91onJVUexcoGruiLQr25BodbJ5HxjME92YA QHrMWwEYqc46LEJQDLhUai+IDNqXLyonj29OzAfeJo5b88A7v6j/BDhm71swdKx3Xm PACjI/FgD9eUckaaD94lWNXMRbwDWxoAE4npSospla4HQzkosEpeXFSwlLlldIYBOq qfowmLmQw9kTEOCrjpce614FSb43PF6orZ2fbYqDs+zTIu9O8+GHuUuMMVECz++G6Z rqgmMZUlcdIqVQMmEwrE7R0tRcbjIIn5a1fiAM2DIH8gzsGqEoo6AE9fanvMql6lK1 5hCX2bHSYi+Kg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 7D495180618 for ; Mon, 26 Feb 2024 23:39:06 +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.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,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 fout2-smtp.messagingengine.com (fout2-smtp.messagingengine.com [103.168.172.145]) (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 ; Mon, 26 Feb 2024 15:39:05 -0800 (PST) Received: from compute1.internal (compute1.nyi.internal [10.202.2.41]) by mailfout.nyi.internal (Postfix) with ESMTP id B233E13800BE for ; Mon, 26 Feb 2024 18:38:57 -0500 (EST) Received: from imap49 ([10.202.2.99]) by compute1.internal (MEProxy); Mon, 26 Feb 2024 18:38:57 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1708990737; x=1709077137; bh=5HS348g2L3 FuDlbdNpP05iXeCr05BXh9xyEY4nGcHdk=; b=Mz2U3tuQROmyUUbRiSIAOyBUQ6 DaOj7YNKWDZVOJqIBTiJIoMObUJTa25Q25YlUaFBOBldzjeRzYzOTkzzA9V9akew Uqltd4V0AvS3pbe0VdRmsTEp2m19jKv+R7nyp52oiofCLiK8kdTvm33NY8By9vym aCNteXzgstalXLEjYRN2ZnD3Xlk4THuOTHeDAWS9JoehYytOFBuFNR7MgV6JRkn8 OtKvdbabaZ7ssruFeti3Pw3VjMdpaWGtMbo+nIpRNKTLOpLF+kjk8KDntRey96l5 BSPIs5ufkQthFaM79s5LQOIrxmJn9GZRK6HLnZH9SgZLiJZT146hsa/uXgwQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; t=1708990737; x=1709077137; bh=5HS348g2L3FuDlbdNpP05iXeCr05 BXh9xyEY4nGcHdk=; b=c+krR574z4IRxa9x/o5k2nK+ef6L3yotPERdVNZw2tlQ aI0b9wXcK/LbdWVsx3jn0yZpN4SVsi0uDGIFUyFicMq8BHNXu3qxV0tTmzSG2cyV sAO2UYWPsQsbvBDZR2OBntflF38iVjOZs9N/E2zAKBwfYnya8pCLqP501lhr7YWP TljdjmOADTIcjb7yRYxyg7RBTFhZOvmlELvgeDYSJWE8ywkJ49kFuof9GT9qFYEC MsGFOstfVgxcacvngJPhuNYLEMW7UkQkdtFOm2EifObuMej1lVnKwtvplnMzhumU vj8n9a3Z+IoYjSr+4mbmI7BGdZp5hX0hSEuDNGeVdQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvledrgeefgdduvdcutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecunecujfgurhepofgfggfkjghffffhvffutgesrgdtre erreerjeenucfhrhhomhepfdftohgsucfnrghnuggvrhhsfdcuoehrohgssegsohhtthhl vggurdgtohguvghsqeenucggtffrrghtthgvrhhnpeehffefhfduueevteetgedvffdule ejvdehledvvdfgiedvvdelveeihfekiefhleenucffohhmrghinhepghgvnhhkghhordgt ohhmnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprh hosgessghothhtlhgvugdrtghouggvsh X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.nyi.internal (Postfix, from userid 501) id 5138115A0092; Mon, 26 Feb 2024 18:38:57 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.11.0-alpha0-153-g7e3bb84806-fm-20240215.007-g7e3bb848 Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 Message-ID: <2160f86c-5c9c-40be-a1fc-27bc7f34c621@app.fastmail.com> In-Reply-To: <963f5cc5-cdb1-4384-b519-5cb15640654e@genkgo.nl> References: <790b5b4e-f51b-4050-a12a-5fa903d0568f@app.fastmail.com> <52C6F501-8E23-42D7-8541-88A22AD79375@koalephant.com> <36e90d8d-d275-4ce9-9dd9-1e2422c6d3a9@app.fastmail.com> <2fdf1933-b51c-40cc-8d02-31899b96c71c@genkgo.nl> <95e93cb9-3ab0-4cf3-8ec5-83e74c9dd607@genkgo.nl> <876aff9f-3eae-4d2d-8e3f-30dfbbeed49c@rwec.co.uk> <963f5cc5-cdb1-4384-b519-5cb15640654e@genkgo.nl> Date: Tue, 27 Feb 2024 00:38:31 +0100 To: internals@lists.php.net Subject: Re: [PHP-DEV] [RFC[ Property accessor hooks, take 2 Content-Type: multipart/alternative; boundary=74f67ef0c2204f6b9a24e1c9f6173c71 From: rob@bottled.codes ("Rob Landers") --74f67ef0c2204f6b9a24e1c9f6173c71 Content-Type: text/plain;charset=utf-8 Content-Transfer-Encoding: quoted-printable On Tue, Feb 27, 2024, at 00:11, Frederik Bosch wrote: > Hi Rowan, >=20 > On 26-02-2024 23:46, Rowan Tommins [IMSoP] wrote: >> On 26/02/2024 20:21, Frederik Bosch wrote: >>> I do note that $this->propName might suggest that the backing value = is accessible from other locations than only the property's own get/set = methods, because of $this usage. >>=20 >>=20 >> Yes, I actually stumbled over that confusion when I was writing some = of the examples in my lengthy e-mail in this thread. As I understand it,= this would work:=20 >>=20 >> public string $foo {=20 >> get { $this->foo ??=3D 0; $this->foo++; return $this->foo; }=20 >> set { throw new Exception; }=20 >> }=20 >>=20 >> Outside the hooks, trying to write to $this->foo would throw the exce= ption, because it refers to the hooked property as a whole; but inside, = the same name refers to something different, which isn't accessible anyw= here else.=20 >>=20 >> Now that I've looked more at how Kotlin uses "field", I understand wh= y it makes sense - it's not an alias for the property itself, but the wa= y to access a "backing store" which has no other name.=20 >>=20 >> Using $this->foo as the name is tempting if you think of hooks as hap= pening "on top of" the "real" property; but that would be a different fe= ature, like Switft's "property observers" (willSet and didSet). What's r= eally happening is that we're declaring two things at once, and giving t= hem the same name; almost as if we'd written this:=20 >>=20 >> public string $foo {=20 >> get { static $_foo; $_foo ??=3D 0; $_foo++; return $_foo; }=20 >> set { throw new Exception; }=20 >> }=20 >>=20 >> Kotlin's "field" is kind of the equivalent of that "static $_foo" >>=20 >>=20 > And what happens in the following situation, how are multiple get call= s working together? >=20 > public string $fullName {=20 > get =3D> $this->first . ' ' . $this->last; // is this accessing th= e backed value, or is it accessing via get > set($value) =3D> $this->fullName =3D $value; > } >=20 > public string $first {=20 > get =3D> explode(' ', $this->fullName)[0], // is this accessing th= e backed value, or is it accessing via get > set($value) =3D> $value; > } >=20 > Isn't it weird that $this->propName gives different results from one g= et function, compared to the other. I would say $this->prop should alway= s follow the same semantics as explained in the RFC (first __get/__set, = then the accessor). >=20 >>=20 >>> Regarding returning void=3Dnull, this is something that IDE and stat= ic analyzers already pick-up as an error. I think being stricter on that= in this RFC would actually make sense, and treat void not as null. >>=20 >> What would happen if a setter contained both "return 42;" and "return= ;"? The latter is explicitly allowed in "void" functions, but is also al= lowed in a non-void function as meaning "return null;" > return 42; // returns (int)42 > return; // early return, void, same as no return > return null; // returns null >>=20 >>=20 >>> And why yield is magic, I do not get that. The word and the expressi= on actually expresses that something is, well, yielded. >>=20 >> But yielded to where? My mental model of "return to set" is that this= :=20 >>=20 >> public string $name { set($value) { $x =3D something($value); return = $x + 1; } }=20 >>=20 >> Is effectively:=20 >>=20 >> private function _name_set($value) { $x =3D something($value); return= $x + 1; } }=20 >> plus:=20 >> $this->name =3D $this->_name_set($value);=20 >>=20 >> With "yield", I can't picture that simple translation; the "magic" is= whatever translates the "yield" keyword into "$this->name =3D" > You would picture it by explaining how it works from the source side. = A set function that contains a yield turns the set function into a direc= tly consumed generator. Considering the following: >=20 >=20 > public string $first {=20 > set($value) =3D> { > yield 'First name'; > yield 'Given name'; > return 'My name'; > } > } >=20 > the pseudo-code from the PHP source side would look as follows. >=20 > $generator =3D setCall($class, 'first', $value); > foreach ($generator as $value) { > writeProperty($class, 'first', $value); > } > if ($generator->hasReturn()) { > writeProperty($class, 'first', $generator->getReturn()); > } >=20 The yield is much more intuitive than magic fields and $this->prop (whic= h feels like an infinite loop). Yield is remarkably simple.=20 Looking at this, I'm still not sure what would happen here, though (mayb= e it is covered in the RFC, and I missed it) -- going to use yield here = to try it out: public string $name { set =3D> { if(strlen($value) < 5) { yield 'invalid'; yield $this->invalidName($value); } yield $value; } } public function invalidName($name) { return $this->name =3D str_pad($name, 5); } This is probably an infinite loop in this particular example, but more i= mportantly, do setters allow reentry while executing? >>=20 >> I would file it with the type widening in the RFC: seems kind of cool= , but probably isn't worth the added complexity.=20 >>=20 >>=20 >> Regards, > -- > Frederik Bosch >=20 > Partner >=20 >=20 > Genkgo logo >=20 > Mail: f.bosch@genkgo.nl > Web: support.genkgo.com >=20 > Entrada 123 > Amsterdam > +31 20 244 1920 >=20 > Genkgo B.V. staat geregistreerd bij de Kamer van Koophandel onder numm= er 56501153 =E2=80=94 Rob --74f67ef0c2204f6b9a24e1c9f6173c71 Content-Type: text/html;charset=utf-8 Content-Transfer-Encoding: quoted-printable
On Tue, Feb 27,= 2024, at 00:11, Frederik Bosch wrote:

Hi Rowan,

On 26-02-2024 23:46, Rowan Tommins [IMSoP] wrote:
On 26/02/2024 20:21, Frederik Bosch wrote:
I do note that $this->propName might suggest that the backing value is accessible from other locations than only the property's own get/set methods, because of $this usage.


Yes, I actually stumbled over that confusion when I was writing some of the examples in my lengthy e-mail in this thread. As I understand it, this would work:

pu= blic string $foo {
    get { $this->fo= o ??=3D 0; $this->foo++; return $this->foo; }
    set { throw ne= w Exception; }
}

Outside= the hooks, trying to write to $this->foo would throw the exception, because it refers to the hooked property as a whole; but inside, the same name refers to something different, which isn't accessible anywhere else.

Now that I've looked more at how Kotlin uses "field", I understand why it makes sense - it's not an alias for the property itself, but the way to access a "backing store" which has no other name. <= br>

Using $this->foo as the name is tempti= ng if you think of hooks as happening "on top of" the "real" property; but that would be a different feature, like Switft's "property observers" (willSet and didSet). What's really happening is that we're declaring two things at once, and giving them the same name; almost as if we'd written this:

public string $foo {=
    get { static $_foo; $_foo ??=3D 0; $= _foo++; return $_foo; }
    set { throw n= ew Exception; }
}

Kotlin= 's "field" is kind of the equivalent of that "static $_foo"


And what happens in the follo= wing situation, how are multiple get calls working together?

public string $fullName {
    = get =3D> $this->first . ' ' . $this->last; // is this accessing the backed value, or is it accessing via get
 = ;   set($value) =3D> $this->fullName =3D $value;
}

public= string $first {
    get =3D> explode(' ', $this-= >fullName)[0], // is this accessing the backed value, or is it accessing via get
 = ;   set($value) =3D> $value;
}

Isn't it we= ird that $this->propName gives different results from one get function, compared to the other. I would say $this->prop should always follow the same semantics as explained in the RFC (first __get/__set, then the accessor).


Regarding= returning void=3Dnull, this is something that IDE and static analyzers already pick-up as an error. I think being stricter on that in this RFC would actually make sense, and treat void not as null.

What would happen if a setter contained both "return 4= 2;" and "return;"? The latter is explicitly allowed in "void" functions, but is also allowed in a non-void function as meaning "return null;"
return 42; // returns (int)42
return; // early return, void, same as no return
<= div> return null; // returns null


And why yield is magic, I= do not get that. The word and the expression actually expresses that something is, well, yielded.

But= yielded to where? My mental model of "return to set" is that this:

public string $name { set($v= alue) { $x =3D something($value); return $x + 1; } }

Is effectively:

private function _name_set($value) { $x =3D so= mething($value); return $x + 1; } }
plus:
$this->= ;name =3D $this->_name_set($value);

W= ith "yield", I can't picture that simple translation; the "magic" is whatever translates the "yield" keyword into "$this->name =3D= "

You would picture it by explaining how it wor= ks from the source side. A set function that contains a yield turns the set function into a directly consumed generator. Considering the following:
=

pu= blic string $first {
    set($value) =3D> {
&= nbsp;       yield 'First name';
   = ;     yield 'Given name';
     &nb= sp;  return 'My name';
    }
}

the pseudo-code from the PHP source side would look as follows.
=

$generator = =3D setCall($class, 'first', $value);
foreach ($generator as $value) {
   wr= iteProperty($class, 'first', $value);
}
if ($generator->hasRe= turn()) {
 
writeProperty($class, 'first', $ge= nerator->getReturn());
}


The yield is much m= ore intuitive than magic fields and $this->prop (which feels like an = infinite loop). Yield is remarkably simple.

Looking at this, I'm still not sure what would happen here, though (ma= ybe it is covered in the RFC, and I missed it) -- going to use yield her= e to try it out:

public string $name {
<= /div>
  set =3D> {
    if(strlen($va= lue) < 5) {
      yield 'invalid';
      yield $this->invalidName($value);
    }
    yield $value;
  }
}

public fu= nction invalidName($name) {
  return $this->name =3D= str_pad($name, 5);
}

This is= probably an infinite loop in this particular example, but more importan= tly, do setters allow reentry while executing?

<= blockquote type=3D"cite" id=3D"qt" style=3D"">

I would file it with the type widening in the RFC: seems kind of cool, but probably isn't worth the added complexity.


Regards,
--

Frederik Bosch

Partner


3D"Genkgo


Entrada 123
Amsterdam
+31 20 244 1920

Genkgo B.V. staat geregi= streerd bij de Kamer van Koophandel onder nummer 56501153
<= /div>

=E2=80=94 Rob<= br>
--74f67ef0c2204f6b9a24e1c9f6173c71--