Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122627 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 071D71AD8F6 for ; Tue, 12 Mar 2024 22:09:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1710281387; bh=m9u3cWb/r+b5IwnQpP6ps4RL9fCh/qnnDJe6XpFj9Nc=; h=Date:Subject:To:References:From:In-Reply-To:From; b=B26rUg01YXyEYJZVLNw8mPiILQjVavA2a89N1MatD90Kwb+XR0iYlFEefaZ+GMlEo NV6OyvlQmX2oaaBTbDancQ0Qz3QxrkDroNEAJHUvuR8FCxG9hp5oeh9GWlvJy35GS0 Y/icrjRGXGIzTUi+ewHHW5pTvZA83NXJPXDXjWqyPpXYOKVJHouhFDCrblo81zcY1Z h5KMH7FYVF/xf/jRVrAkUNld5Fqh7JZBFiQWmYPZH/U8metyiE4n/OS8DrHO5SryV7 ERFx2OwBFlrLEBQ8Ie/b4YaB0LJyo0zy5tjQw4ezFr4c4jigH8jPrXwkCr0D26BB+X 5V+d/LKHv7Y9Q== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 6F802180032 for ; Tue, 12 Mar 2024 22:09:45 +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.9 required=5.0 tests=BAYES_40,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: No X-Envelope-From: Received: from wfhigh8-smtp.messagingengine.com (wfhigh8-smtp.messagingengine.com [64.147.123.159]) (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 ; Tue, 12 Mar 2024 22:09:44 +0000 (UTC) Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailfhigh.west.internal (Postfix) with ESMTP id D761F180009D for ; Tue, 12 Mar 2024 18:09:26 -0400 (EDT) Received: from mailfrontend2 ([10.202.2.163]) by compute5.internal (MEProxy); Tue, 12 Mar 2024 18:09:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rwec.co.uk; 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=fm1; t=1710281366; x=1710367766; bh=IrvqHM7abX R6JOO3LWS1JoSKNuEcBAJuMfL1jp/9xkg=; b=kX9EmMEDun/uyw4Gv/awIZOail qQfYvsr0nBE17aGo6hUqd2krewwhJu8LghamPNeeek6rCSIQP5jE6jEMPbvTjAvT 0McXh6GZRi2bEjB/X4P+LUozOB75I2b0kQJr2gVFpPISJ0ot0MfUBau7vgWZjeII Y0EN1MhasqzQkKt3LXhFVbIV0tJUpLmpfAuepQM7VAKDnJrNuRfcDmfZkYwAN+R8 K9N0+IcmaweonBxpHc86Cv3OVWtsk+YRbsr+PNCH/BGY4ZOIqIcsHoqKdKTa25Sj MIgpssNaNIYSg1ICyFFiSCOkH1+6eJR9Qif3ndi//7DnI3zmTsnQyptAgrbQ== 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=1710281366; x=1710367766; bh=IrvqHM7abXR6JOO3LWS1JoSKNuEc BAJuMfL1jp/9xkg=; b=KuDY7YNcY8MZ8k7Pjh0V66mhdZ/13S0CS52/M7vGM0kE OC+06BwvSckjFo5gICmUS9k8tS5PtxVFfs+/qDMEab90uEVAdMgEo0ZnTkLmyfxM wAugsm0k3eB2Rx2WQOy6VsbIT2396OKK11dA5uK0zaoSmPwg8kcZkuiXU6mDwsNq rhpwWjtFRPml6KhfRpmHUnPql0b0h7p/+6IhyM+SSIvvm1Bk8PcVsE0E4jTNGzb7 zafzFUndpXy4d0JLvf7C9USXw1o0id0BqTjrIONJ5GntqVc3WcWT4PXdESLUXMKS cs2VPPep/8OeoMS1U9QwzdkEvydf26CXwdPXXbkEHQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvledrjeefgdduheejucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucgoufhushhpvggtthffohhmrghinhculdegledmne cujfgurheptgfkffggfgfuvfhfhfgjsegrtderredtvdejnecuhfhrohhmpedftfhofigr nhcuvfhomhhmihhnshculgfkoffuohfrngdfuceoihhmshhophdrphhhphesrhifvggtrd gtohdruhhkqeenucggtffrrghtthgvrhhnpedtfeeiueeuledugfetgedvkedujeejkeev teeivddujedtueetuddujeduvefgudenucffohhmrghinhepphhhphdrnhgvthdpfehvge hlrdhorhhgnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhho mhepihhmshhophdrphhhphesrhifvggtrdgtohdruhhk X-ME-Proxy: Feedback-ID: id5114917:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA for ; Tue, 12 Mar 2024 18:09:25 -0400 (EDT) Content-Type: multipart/alternative; boundary="------------bEerQroA3mRy3S5F40wCpqlj" Message-ID: Date: Tue, 12 Mar 2024 22:09:21 +0000 Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PHP-DEV] [RFC[ Property accessor hooks, take 2 Content-Language: en-GB To: internals@lists.php.net References: In-Reply-To: From: imsop.php@rwec.co.uk ("Rowan Tommins [IMSoP]") This is a multi-part message in MIME format. --------------bEerQroA3mRy3S5F40wCpqlj Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit On 08/03/2024 15:53, Larry Garfield wrote: > Hi folks. Based on earlier discussions, we've made a number of changes > to the RFC that should address some of the concerns people raised. We > also had some very fruitful discussions off-list with several developers > from the Foundation, which led to what we feel are some solid > improvements. > > https://wiki.php.net/rfc/property-hooks Hi Larry, Thanks again for the continuing hard work on this! > if a |get| hook for property |$foo| calls method |bar()|, then inside that method |$this->foo| will refer to the raw property, both read and write. If |bar()| is called from somewhere other than the hook, reading from |$this->foo| will trigger the |get| hook. This behavior is identical to that already used by |__get| and |__set| today. I'm slightly confused by this. If there is an actual property called $foo, then __get and __set will be called only when it is out of visibility, regardless of the call stack - e.g. a private property will always trigger __get from public scope, and always access it directly from private scope: https://3v4l.org/R5Yos That seems differ from what's proposed, where even a private call to bar() would trigger the hook. The protection against recursion appears to only be relevant for completely undefined properties. For __get, the direct access can never do anything useful - there's nothing to access: https://3v4l.org/2nDZS For __set, it is at least possible for the non-recursive write to succeed, but only in the niche case of creating a dynamic property: https://3v4l.org/dpYOj I'm not sure that there's any equivalent to this scenario for property hooks, since they can never be undefined/dynamic. > There is one exception to the above: if a property is virtual, then there is no presumed connection between the get and set operations. [...] For that reason, |&get| by reference is allowed for virtual properties, regardless of whether or not there is a |set| hook. I don't agree with this, and the example immediately following it demonstrates the exact opposite: the &get and set hooks are both proxying to the same backing value, and have all the same problems as if the property was non-virtual. I would imagine a lot of real-life virtual properties would be doing something similar: converting to/from a different type, proxying to another object, etc. I think this exception is unnecessarily complicated: either trust users to handle the implications of combining &get with set, or forbid it. > Additionally, |&get| hooks are allowed for arrays as well, provided there is no |set| hook. I mentioned in a previous e-mail the possibility of using the &get hook for array writes. Has this been considered? That is: $c->arr['beep'] = 'boop'; Would be equivalent to: $temp =& $c->arr; $temp['beep'] = 'boop'; unset($temp); Which would be valid if $arr had an &get hook defined. > A |set| hook on a typed property must declare a parameter type that is the same as or contravariant (wider) from the type of the property. > Once a property has both a |get| and |set| operation, however, it is no longer covariant or contravariant for further extension. How do these two rules interact? Could this: public string $foo {    get => $this->_foo;    set(string|Stringable $value) {        $this->_foo = (string)$value;    } } be over-ridden by this, where the property's "main type" remains invariant but its "settable type" is contravariant? public string $foo {    get => $this->_foo;    set(string|Stringable|SomethingElse $value) {        $this->_foo = $value instanceof SomethingElse ? $value->asString() : (string)$value;    } } > ReflectionProperty has several new methods to work with hooks. There should be some way to reliably determine the "settable type" of a property. At the moment, I think you would have to do something like this: $setHook = $property->getHook(PropertyHookType::Set); $writeType = $setHook === null ? $property->getType() : $setHook->getParameters()[0]->getType(); Once again, I would like to make the case that asymmetric types are an unnecessary complication that should be left to Future Scope. The fact that none of the other languages referenced have such a feature should also give us pause. There's nothing to stop us being the first to innovate a feature, but we should be extra cautious when doing so, with no previous experience to learn from. It also means there is no expectation from users coming from other languages that this will be possible. If it genuinely seems useful, it can be added in a follow-up RFC, or even a later version of PHP, with little impact on the rest of the feature. But if we add it now and regret it, or some detail of its implementation, we will be stuck with it forever. Regards, -- Rowan Tommins [IMSoP] --------------bEerQroA3mRy3S5F40wCpqlj Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit
On 08/03/2024 15:53, Larry Garfield wrote:
Hi folks.  Based on earlier discussions, we've made a number of changes 
to the RFC that should address some of the concerns people raised.  We 
also had some very fruitful discussions off-list with several developers
 from the Foundation, which led to what we feel are some solid 
improvements.

https://wiki.php.net/rfc/property-hooks


Hi Larry,

Thanks again for the continuing hard work on this!


> if a get hook for property $foo calls method bar(), then inside that method $this->foo will refer to the raw property, both read and write. If bar() is called from somewhere other than the hook, reading from $this->foo will trigger the get hook. This behavior is identical to that already used by __get and __set today.


I'm slightly confused by this.

If there is an actual property called $foo, then __get and __set will be called only when it is out of visibility, regardless of the call stack - e.g. a private property will always trigger __get from public scope, and always access it directly from private scope: https://3v4l.org/R5Yos That seems differ from what's proposed, where even a private call to bar() would trigger the hook.

The protection against recursion appears to only be relevant for completely undefined properties. For __get, the direct access can never do anything useful - there's nothing to access: https://3v4l.org/2nDZS For __set, it is at least possible for the non-recursive write to succeed, but only in the niche case of creating a dynamic property: https://3v4l.org/dpYOj I'm not sure that there's any equivalent to this scenario for property hooks, since they can never be undefined/dynamic.



> There is one exception to the above: if a property is virtual, then there is no presumed connection between the get and set operations. [...] For that reason, &get by reference is allowed for virtual properties, regardless of whether or not there is a set hook.


I don't agree with this, and the example immediately following it demonstrates the exact opposite: the &get and set hooks are both proxying to the same backing value, and have all the same problems as if the property was non-virtual. I would imagine a lot of real-life virtual properties would be doing something similar: converting to/from a different type, proxying to another object, etc. 

I think this exception is unnecessarily complicated: either trust users to handle the implications of combining &get with set, or forbid it.



> Additionally, &get hooks are allowed for arrays as well, provided there is no set hook.


I mentioned in a previous e-mail the possibility of using the &get hook for array writes. Has this been considered?

That is:

$c->arr['beep'] = 'boop';

Would be equivalent to:

$temp =& $c->arr;
$temp['beep'] = 'boop';
unset($temp);

Which would be valid if $arr had an &get hook defined.



> A set hook on a typed property must declare a parameter type that is the same as or contravariant (wider) from the type of the property.

> Once a property has both a get and set operation, however, it is no longer covariant or contravariant for further extension.


How do these two rules interact?

Could this:

public string $foo {
   get => $this->_foo;
   set(string|Stringable $value) {
       $this->_foo = (string)$value;
   }
}

be over-ridden by this, where the property's "main type" remains invariant but its "settable type" is contravariant?

public string $foo {
   get => $this->_foo;
   set(string|Stringable|SomethingElse $value) {
       $this->_foo = $value instanceof SomethingElse ? $value->asString() : (string)$value;
   }
}


> ReflectionProperty has several new methods to work with hooks.


There should be some way to reliably determine the "settable type" of a property. At the moment, I think you would have to do something like this:

$setHook = $property->getHook(PropertyHookType::Set);
$writeType = $setHook === null ? $property->getType() : $setHook->getParameters()[0]->getType();



Once again, I would like to make the case that asymmetric types are an unnecessary complication that should be left to Future Scope.

The fact that none of the other languages referenced have such a feature should also give us pause. There's nothing to stop us being the first to innovate a feature, but we should be extra cautious when doing so, with no previous experience to learn from. It also means there is no expectation from users coming from other languages that this will be possible.

If it genuinely seems useful, it can be added in a follow-up RFC, or even a later version of PHP, with little impact on the rest of the feature. But if we add it now and regret it, or some detail of its implementation, we will be stuck with it forever.


Regards,

-- 
Rowan Tommins
[IMSoP]
--------------bEerQroA3mRy3S5F40wCpqlj--