Re: [PHP-DEV] Re: [RFC][Discussion] Harmonise "untyped" and "typed" properties

2023-11-23 Thread Rowan Tommins
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

2023-11-23 Thread Nikita Popov
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

2023-11-23 Thread Nicolas Grekas
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

2023-11-23 Thread Robert Landers
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

2023-11-22 Thread Rowan Tommins
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

2023-11-22 Thread Claude Pache


> 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

2023-11-22 Thread Rowan Tommins
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

2023-11-22 Thread Nicolas Grekas
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

2023-11-22 Thread Robert Landers
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

2023-11-20 Thread Larry Garfield
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

2023-11-20 Thread Rowan Tommins

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