Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
On Thu, 23 Nov 2023 at 08:48, Nicolas Grekas wrote: > Sorry this comes as a surprise to you but you're rewriting history here. > The current behavior, the one that was fixed in that commit, matches how > PHP behaved before typed properties, so this commit brought consistency. > The question of "what does __get do with a property that has been declared but not assigned a value?" has no answer before PHP 7.4, because that situation simply never happened. So there isn't really one answer to what is "consistent". If I understand rightly, your position, and Nikita's, is that the behaviour should be consistent with the statement "if you have declared a property, access doesn't trigger __get unless you explicitly call unset()". This is what the change that slipped into 7.4.1 "fixed". The reason it surprised me is that I expected it to be consistent with a different statement: "if you have an __get method, this takes precedence over 'undefined variable' notices/warnings/errors". That statement was true in 7.3, and still true in the original implementation in 7.4.0 (including "unitialized" alongside "undefined"), but was *broken* by the change in 7.4.1: now, the "uninitialized property" error takes precedence over __get in the specific case of never having assigned a value. > About the behavior, it's been in use for many years to build lazy proxies. > I know two major use cases that leverage this powerful capability: Doctrine > entities and Symfony lazy services. There are more as any code that > leverages ocramius/proxy-manager relies on this. > Just to be clear, it is not the behaviour *after* calling unset which I am concerned about, it is the behaviour *before* calling unset or assigning any value. I was aware of the interaction with __get, but wrongly assumed that the rule was simply "uninitialized properties trigger __get". > About the vocabulary, the source tells us that "uninitialized" properties > that are unset() become "undefined". I know that's not super accurate since > a typed property is always defined semantically > Just "undefined" is not sufficiently unambiguous; you have to distinguish four different states: 1) Never declared, or added dynamically and then unset 2) Declared without a type, then unset 3) Declared with a type, not yet assigned any value 4) Declared with a type, then unset The messages presented to the user refer to both (1) and (2) as "undefined", and both (3) and (4) as "uninitialized". As it stands, the RFC would replace all instances of (2) with (4), but that still leaves us with two names for three states. Claude Pache wrote: > However, it is not a problem in practice, because users of classes implementing (1) but not (2) do not unset declared properties, ever. Nikita Popov wrote: > ... and then forbid calling unset() on declared properties Right now, unset() is also the only way to break a reference, other than another assign-by-reference, so it's quite reasonable to write "unset($this->foo)" instead of "$this->foo" to ensure a property is truly reset to a known state. Maybe we need a new function or operator to atomically break the reference and assign a new value, e.g. unreference($this->foo, 42); or $this->foo := 42; to replace unset($this->foo); $this->foo=42; Regards, -- Rowan Tommins [IMSoP]
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
On Thu, Nov 23, 2023, at 09:48, Nicolas Grekas wrote: > Hi Rowan, > > Le jeu. 23 nov. 2023 à 08:56, Rowan Tommins a > écrit : > > > On 23 November 2023 01:37:06 GMT, Claude Pache > > wrote: > > >What you describe in the last sentence is what was initially designed and > > implemented by the RFC: https://wiki.php.net/rfc/typed_properties_v2 > > (section Overloaded Properties). > > > > > >However, it was later changed to the current semantics (unset() needed in > > order to trigger __get()) in https://github.com/php/php-src/pull/4974 > > > > > > Good find. So not only is it not specified this way in the RFC, it > > actually made it into a live release, then someone complained and we rushed > > out a more complicated version "to avoid WTF". That's really unfortunate. > > > > I'm not at all convinced by the argument in the linked bug report - > > whether you get an error or an unexpected call to __get, the solution is to > > assign a valid value to the property. And making the behaviour different > > after unset() just hides the user's problem, which is that they didn't > > expect to *ever* have a call to __get for that property. > > > > But I guess I'm 4 years too late to make that case > > > > Sorry this comes as a surprise to you but you're rewriting history here. > The current behavior, the one that was fixed in that commit, matches how > PHP behaved before typed properties, so this commit brought consistency. > > About the behavior, it's been in use for many years to build lazy proxies. > I know two major use cases that leverage this powerful capability: Doctrine > entities and Symfony lazy services. There are more as any code that > leverages ocramius/proxy-manager relies on this. > > About the vocabulary, the source tells us that "uninitialized" properties > that are unset() become "undefined". I know that's not super accurate since > a typed property is always defined semantically, but that's nonetheless the > flag that is used in the source. Maybe this could help with the RFC. This. The lazy initialization use case is the only reason why we still allow declared properties to be unset at all. Our long term plan was to find an alternative way to support lazy initialization for properties, and then forbid calling unset() on declared properties. However, we still don't have that alternative today. Regards, Nikita
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
Hi Rowan, Le jeu. 23 nov. 2023 à 08:56, Rowan Tommins a écrit : > On 23 November 2023 01:37:06 GMT, Claude Pache > wrote: > >What you describe in the last sentence is what was initially designed and > implemented by the RFC: https://wiki.php.net/rfc/typed_properties_v2 > (section Overloaded Properties). > > > >However, it was later changed to the current semantics (unset() needed in > order to trigger __get()) in https://github.com/php/php-src/pull/4974 > > > Good find. So not only is it not specified this way in the RFC, it > actually made it into a live release, then someone complained and we rushed > out a more complicated version "to avoid WTF". That's really unfortunate. > > I'm not at all convinced by the argument in the linked bug report - > whether you get an error or an unexpected call to __get, the solution is to > assign a valid value to the property. And making the behaviour different > after unset() just hides the user's problem, which is that they didn't > expect to *ever* have a call to __get for that property. > > But I guess I'm 4 years too late to make that case > Sorry this comes as a surprise to you but you're rewriting history here. The current behavior, the one that was fixed in that commit, matches how PHP behaved before typed properties, so this commit brought consistency. About the behavior, it's been in use for many years to build lazy proxies. I know two major use cases that leverage this powerful capability: Doctrine entities and Symfony lazy services. There are more as any code that leverages ocramius/proxy-manager relies on this. About the vocabulary, the source tells us that "uninitialized" properties that are unset() become "undefined". I know that's not super accurate since a typed property is always defined semantically, but that's nonetheless the flag that is used in the source. Maybe this could help with the RFC. Cheers, Nicolas
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
On Thu, Nov 23, 2023 at 8:56 AM Rowan Tommins wrote: > > On 23 November 2023 01:37:06 GMT, Claude Pache wrote: > >What you describe in the last sentence is what was initially designed and > >implemented by the RFC: https://wiki.php.net/rfc/typed_properties_v2 > >(section Overloaded Properties). > > > >However, it was later changed to the current semantics (unset() needed in > >order to trigger __get()) in https://github.com/php/php-src/pull/4974 > > > Good find. So not only is it not specified this way in the RFC, it actually > made it into a live release, then someone complained and we rushed out a more > complicated version "to avoid WTF". That's really unfortunate. > > I'm not at all convinced by the argument in the linked bug report - whether > you get an error or an unexpected call to __get, the solution is to assign a > valid value to the property. And making the behaviour different after unset() > just hides the user's problem, which is that they didn't expect to *ever* > have a call to __get for that property. > > But I guess I'm 4 years too late to make that case. > > Regards, > > -- > Rowan Tommins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > Heh > But I guess I'm 4 years too late to make that case. 4 years ago, I was using this behavior to write proxies that actually called things over the network instead of the properties. I didn't file that bug, but I probably would have filed something similar. Nowadays, those proxies are dead as we long since generated code based on interfaces (so they'll pass PHP type checks). I venture once we have property hooks (assuming that is still a thing coming along), this behavior can be removed completely as you can just use property hooks instead of unset to call __get. I'm actually quite excited about property hooks and hope they pass. Personally, I haven't used this behavior since 7.4-ish, and I'd be surprised to see it relied on in modern PHP: code generation is too easy these days. Robert Landers Software Engineer Utrecht NL -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
On 23 November 2023 01:37:06 GMT, Claude Pache wrote: >What you describe in the last sentence is what was initially designed and >implemented by the RFC: https://wiki.php.net/rfc/typed_properties_v2 (section >Overloaded Properties). > >However, it was later changed to the current semantics (unset() needed in >order to trigger __get()) in https://github.com/php/php-src/pull/4974 Good find. So not only is it not specified this way in the RFC, it actually made it into a live release, then someone complained and we rushed out a more complicated version "to avoid WTF". That's really unfortunate. I'm not at all convinced by the argument in the linked bug report - whether you get an error or an unexpected call to __get, the solution is to assign a valid value to the property. And making the behaviour different after unset() just hides the user's problem, which is that they didn't expect to *ever* have a call to __get for that property. But I guess I'm 4 years too late to make that case. Regards, -- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
> Le 22 nov. 2023 à 23:17, Rowan Tommins a écrit : > > I'm probably going to regret asking this, but is there some reason it works > that way? Is there any chance of changing it to just: > >> Typed properties start off as uninitialized. >> >> Once you've assigned a value, you can't go back to the original >> uninitialized state using unset() >> >> Accessing an uninitialized property will first check for __get, and throw an >> error if that isn't defined. Hi Rowan, What you describe in the last sentence is what was initially designed and implemented by the RFC: https://wiki.php.net/rfc/typed_properties_v2 (section Overloaded Properties). However, it was later changed to the current semantics (unset() needed in order to trigger __get()) in https://github.com/php/php-src/pull/4974 —Claude
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
On 22 November 2023 14:12:09 GMT, Nicolas Grekas wrote: >I think there is an inaccuracy that needs to be fixed in the after-unset >state : as noted later in the RFC, magic accessors are called after an >unset($this->typedProps). This means the state cannot be described as >identical ("uninitialized') before and after unset() in the first table in >the RFC. Isn't there some vocabulary in the source that we can use to >describe those states more accurately? Oh. Wow. That's more than just inaccurate terminology... I always assumed the rule was "access to uninitialised properties triggers __get", not that there was yet another magical state buried in the implementation. From a user point of view, I find that frankly terrible: > Typed properties start off as uninitialized, but if you use unset(), you can > make them *super-uninitialized*. > > There's no way to actually see if something's uninitialized or > super-uninitialized; and once you've assigned a value, you can't go back to > the original uninitialized, only to super-uninitialized. > > Accessing an uninitialized property always throws an error, whereas accessing > a super-uninitialized property will first check for __get. I'm not sure choosing a different name from "super-uninitialized" makes much difference to how that reads. I'm probably going to regret asking this, but is there some reason it works that way? Is there any chance of changing it to just: > Typed properties start off as uninitialized. > > Once you've assigned a value, you can't go back to the original uninitialized > state using unset() > > Accessing an uninitialized property will first check for __get, and throw an > error if that isn't defined. Regards, -- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
Hi Rowan, Larry, Thanks for the RFC. I think there is an inaccuracy that needs to be fixed in the after-unset state : as noted later in the RFC, magic accessors are called after an unset($this->typedProps). This means the state cannot be described as identical ("uninitialized') before and after unset() in the first table in the RFC. Isn't there some vocabulary in the source that we can use to describe those states more accurately? My initial gut reaction is that both ReflectionParameter and > ReflectionProperty should treat "omitted" as "mixed", and just evaluate > their type to "mixed". It is in practice a distinction without meaning, or > will be after this RFC. That said, I also have an itch telling me that > there are a few small-but-important edge cases where you would care about > the difference between mixed and none in reflection, and that I'd be the > one to run into them, but I cannot think of what they would be. > I think this needs to be clarified. I can't think of when this difference could matter. I never wrote any code that could give any meaning to mixed vs untyped properties, and many here know that I can write unusual PHP code. I actually always wondered why we have this difference and I therefore welcome the RFC. Maybe this can be evaluated up to the point where we realize that the change could go into 8.4? I'd be happy to run a patched PHP on some codebases I maintain to see how it goes. Nicolas
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
On Tue, Nov 21, 2023 at 12:41 AM Larry Garfield wrote: > > On Mon, Nov 20, 2023, at 11:08 PM, Rowan Tommins wrote: > > On 16/11/2023 20:41, Rowan Tommins wrote: > >> Hi all, > >> > >> I have finally written up an RFC I have been considering for some > >> time: Harmonise "untyped" and "typed" properties > >> > >> RFC URL: https://wiki.php.net/rfc/mixed_vs_untyped_properties > > > > > > I've revised the RFC; it now proposes to keep the implicit "= null" for > > untyped properties, although I'm still interested in suggestions for > > other strategies around that. I have also added discussion of variance > > checks (thanks Claude for the tips on that). > > Thanks. It's looking pretty good, and should simplify things considerably. > > > While doing so, I checked Reflection, and am unsure how to proceed. > > Currently ReflectionParameter shows a difference between "function > > foo($bar)" and "function foo(mixed $bar)", even though these are > > analysed as equivalent in inheritance checks. Should ReflectionProperty > > also retain this distinction? Was the possibility discussed when "mixed" > > was introduced of using a ReflectionType of mixed for both cases? > > I don't recall any discussion of ReflectionType when mixed was added, no. > > My initial gut reaction is that both ReflectionParameter and > ReflectionProperty should treat "omitted" as "mixed", and just evaluate their > type to "mixed". It is in practice a distinction without meaning, or will be > after this RFC. That said, I also have an itch telling me that there are a > few small-but-important edge cases where you would care about the difference > between mixed and none in reflection, and that I'd be the one to run into > them, but I cannot think of what they would be. > > --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > Hmmm, > That said, I also have an itch telling me that there are a few > small-but-important edge cases where you would care about the difference > between mixed and none in reflection, I'd also probably run into them, and the only one I can think of will be moot after this RFC: - `unset($this->var)` to use magic methods instead of the properties in some older proxies Other than that, I can't think of anything, though that isn't really important for reflection... Robert Landers Software Engineer Utrecht NL -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
On Mon, Nov 20, 2023, at 11:08 PM, Rowan Tommins wrote: > On 16/11/2023 20:41, Rowan Tommins wrote: >> Hi all, >> >> I have finally written up an RFC I have been considering for some >> time: Harmonise "untyped" and "typed" properties >> >> RFC URL: https://wiki.php.net/rfc/mixed_vs_untyped_properties > > > I've revised the RFC; it now proposes to keep the implicit "= null" for > untyped properties, although I'm still interested in suggestions for > other strategies around that. I have also added discussion of variance > checks (thanks Claude for the tips on that). Thanks. It's looking pretty good, and should simplify things considerably. > While doing so, I checked Reflection, and am unsure how to proceed. > Currently ReflectionParameter shows a difference between "function > foo($bar)" and "function foo(mixed $bar)", even though these are > analysed as equivalent in inheritance checks. Should ReflectionProperty > also retain this distinction? Was the possibility discussed when "mixed" > was introduced of using a ReflectionType of mixed for both cases? I don't recall any discussion of ReflectionType when mixed was added, no. My initial gut reaction is that both ReflectionParameter and ReflectionProperty should treat "omitted" as "mixed", and just evaluate their type to "mixed". It is in practice a distinction without meaning, or will be after this RFC. That said, I also have an itch telling me that there are a few small-but-important edge cases where you would care about the difference between mixed and none in reflection, and that I'd be the one to run into them, but I cannot think of what they would be. --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
[PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties
On 16/11/2023 20:41, Rowan Tommins wrote: Hi all, I have finally written up an RFC I have been considering for some time: Harmonise "untyped" and "typed" properties RFC URL: https://wiki.php.net/rfc/mixed_vs_untyped_properties I've revised the RFC; it now proposes to keep the implicit "= null" for untyped properties, although I'm still interested in suggestions for other strategies around that. I have also added discussion of variance checks (thanks Claude for the tips on that). While doing so, I checked Reflection, and am unsure how to proceed. Currently ReflectionParameter shows a difference between "function foo($bar)" and "function foo(mixed $bar)", even though these are analysed as equivalent in inheritance checks. Should ReflectionProperty also retain this distinction? Was the possibility discussed when "mixed" was introduced of using a ReflectionType of mixed for both cases? Regards, -- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php