# [RFC] Asymmetric Visibility, with readonly

- Thread: https://externals.io/message/118994
- Original archive: https://news-web.php.net/php.internals/118994
- Started by Larry Garfield on 2022-11-13T20:08:01+00:00
- Messages: 44
- Page 1 of 2: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/118994.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #118994 — Larry Garfield — 2022-11-13T20:08:01+00:00

URL: https://externals.io/message/118994#118994

Hi folks.  Ilija is nearly done with the implementation for asymmetric visibility and flushing out edge cases, but we've run into one design question we'd like feedback on.

There's two design decisions we've made at this point, both of which we think are logical and reasonable:

1. If specified, the set visibility must be tighter than the get visibility.  So `protected protected(set)` and `protected public(set)` are not permitted, for instance.

2. `readonly` is a "write once" flag that may be combined with asymmetric visibility.  If no set visibility is specified, `readoly` implies `private(set)`, but a different set visibility may also be provided.

These are both reasonable rules.  However, it creates a conflict.  Specifically, in the following cases:

public public(set) readonly string $foo

protected protected(set) readonly string $foo

These would be the only way to have a non-private-set readonly property.  While the first is in practice quite unlikely, the second has valid use cases.  (In particular, a base class that provides properties expected to be set by a child constructor, and then used by a method in the parent class.)  However, it would not be allowed under the rules above.  Working around it would require specifying `public protected(set) readonly...`, which means exposing a property that likely should not be exposed.

That creates an odd situation where readonly and asymmetric visibility may only be combined "sometimes."  That is not deesireable.  The only way to combine them in their current form is to allow `protected protected(set)` only if readonly is in use, which is excessively complicated both to implement and to explain/document/use.

We see two possible ways to resolve this conflict:

1. Relax the set-is-tighter restriction.  That would allow `protected protected(set)` etc. on any property.  It wouldn't be particularly useful unless readonly is being used, but it would be syntactically legal and behave as you'd expect.  We could still disallow "set is more permissive" combinations (eg, `private public(set)`), as those have no apparent use case.

2. Disallow readonly and asymmetric visibility being combined, because readonly already has a hard-coded implied asymmetric visibility.  This option removes some potential use cases (they would most likely drop the readonly), but has the upside that it's easier to re-allow at some point in the future.

3. Some other brilliant idea we've not thought of.


Both are viable approaches with pros and cons.  We're split on which way to go with this, so we throw it out to the group for feedback.  Which approach would you favor, or do you have some other brilliant idea to square this circle?


-- 
  Larry Garfield
  larry@garfieldtech.com

---

## #118995 — Larry Garfield — 2022-11-13T21:24:19+00:00

URL: https://externals.io/message/118994#118995 · In reply to: #118994

> Hi folks.  Ilija is nearly done with the implementation for asymmetric 
> visibility and flushing out edge cases, but we've run into one design 
> question we'd like feedback on.
>
> There's two design decisions we've made at this point, both of which we 
> think are logical and reasonable:
>
> 1. If specified, the set visibility must be tighter than the get 
> visibility.  So `protected protected(set)` and `protected public(set)` 
> are not permitted, for instance.
>
> 2. `readonly` is a "write once" flag that may be combined with 
> asymmetric visibility.  If no set visibility is specified, `readoly` 
> implies `private(set)`, but a different set visibility may also be 
> provided.
>
> These are both reasonable rules.  However, it creates a conflict.  
> Specifically, in the following cases:
>
> public public(set) readonly string $foo
>
> protected protected(set) readonly string $foo
>
> These would be the only way to have a non-private-set readonly 
> property.  While the first is in practice quite unlikely, the second 
> has valid use cases.  (In particular, a base class that provides 
> properties expected to be set by a child constructor, and then used by 
> a method in the parent class.)  However, it would not be allowed under 
> the rules above.  Working around it would require specifying `public 
> protected(set) readonly...`, which means exposing a property that 
> likely should not be exposed.
>
> That creates an odd situation where readonly and asymmetric visibility 
> may only be combined "sometimes."  That is not deesireable.  The only 
> way to combine them in their current form is to allow `protected 
> protected(set)` only if readonly is in use, which is excessively 
> complicated both to implement and to explain/document/use.
>
> We see two possible ways to resolve this conflict:
>
> 1. Relax the set-is-tighter restriction.  That would allow `protected 
> protected(set)` etc. on any property.  It wouldn't be particularly 
> useful unless readonly is being used, but it would be syntactically 
> legal and behave as you'd expect.  We could still disallow "set is more 
> permissive" combinations (eg, `private public(set)`), as those have no 
> apparent use case.
>
> 2. Disallow readonly and asymmetric visibility being combined, because 
> readonly already has a hard-coded implied asymmetric visibility.  This 
> option removes some potential use cases (they would most likely drop 
> the readonly), but has the upside that it's easier to re-allow at some 
> point in the future.
>
> 3. Some other brilliant idea we've not thought of.
>
>
> Both are viable approaches with pros and cons.  We're split on which 
> way to go with this, so we throw it out to the group for feedback.  
> Which approach would you favor, or do you have some other brilliant 
> idea to square this circle?

Addendum: Ilija and I were talking past each other a bit, it seems.  Allowing `protected protected(set) readonly` only in the context of readonly would be straightforward to implement.  It's potentially not the easiest to explain that exception, but implementation-wise it's fine.

So that gives another viable option to consider:

4. Allow get and set visibility to be both set explicitly to the same level, iff in the presence of readonly.  So `protected protected(set) string $foo` is not allowed, but `protected protected(set) readonly string $foo` is allowed.  This gives the most options and the least pointless syntax, but at the cost of having an exception that needs to be documented/explained/understood.

--Larry Garfield

---

## #118996 — Deleu — 2022-11-13T21:50:45+00:00

URL: https://externals.io/message/118994#118996 · In reply to: #118994

>
>
> 2. `readonly` is a "write once" flag that may be combined with asymmetric
> visibility.  If no set visibility is specified, `readoly` implies
> `private(set)`, but a different set visibility may also be provided.
>
> These are both reasonable rules.  However, it creates a conflict.
> Specifically, in the following cases:
>
> public public(set) readonly string $foo
>
> protected protected(set) readonly string $foo
>

What if the implicit rule for `readonly` is changed into `protected(set)`?
Let's run with this for a minute.

1- It's not truly a CODE breaking change. Right now if you have a
`protected readonly` property, you're either calling the parent constructor
or you're never making use of the variable, otherwise you're getting a
Fatal Error.
2- It's a CONCEPTUAL breaking change. If you've declared a `protected
readonly` property, you may expect anyone in the inheritance chain to be
calling your constructor. A new PHP version will warn you to either change
your property into `protected private(set) readonly` (Rector might be able
to do this) or accept the new behavior.
3- The two logical design choices can be kept.

To be honest, I think asymmetric visibility seems way over complex for very
little benefit, so I'm a bit against the entire feature, but if we were to
have them, it would really be nicer to not have crazy shenanigans involved.

-- 
Marco Deleu

---

## #118997 — Larry Garfield — 2022-11-13T21:57:20+00:00

URL: https://externals.io/message/118994#118997 · In reply to: #118996

> > […]
>
> What if the implicit rule for `readonly` is changed into `protected(set)`?
> Let's run with this for a minute.
>
> 1- It's not truly a CODE breaking change. Right now if you have a
> `protected readonly` property, you're either calling the parent constructor
> or you're never making use of the variable, otherwise you're getting a
> Fatal Error.

This is untrue.  You can declare a private property identically in a parent and child class, even readonly.  I'm doing this now in a project.  It works, but would be unnecessary if the parent's property were protected(set).

--Larry Garfield

---

## #118998 — Deleu — 2022-11-13T22:08:25+00:00

URL: https://externals.io/message/118994#118998 · In reply to: #118997

>
>
> This is untrue.  You can declare a private property identically in a
> parent and child class, even readonly.  I'm doing this now in a project.
> It works, but would be unnecessary if the parent's property were
> protected(set).
>
> --Larry Garfield
>

I do understand the slight complexity here (perhaps stronger in terms of
implementation), but my assumption was that if a property is private and
"the set visibility must be tighter", PHP would never assume `private
protected(set) readonly`. In other words, if `private readonly` then
`private private(set) readonly`, therefore nothing changes. You can keep
the use of disjointed variables in inheritance provided they're all
private. The only concern is when a property is declared protected, which
then evaluates to what I mentioned on the previous email.

I'm also under the stated assumption that `public public(set) readonly` is
not something needed to be supported.

-- 
Marco Deleu

---

## #118999 — Deleu — 2022-11-13T22:48:22+00:00

URL: https://externals.io/message/118994#118999 · In reply to: #118994

On Sun, Nov 13, 2022 at 5:09 PM Larry Garfield <larry@garfieldtech.com>
wrote:

> Hi folks.  Ilija is nearly done with the implementation for asymmetric
> visibility and flushing out edge cases, but we've run into one design
> question we'd like feedback on.
>
> There's two design decisions we've made at this point, both of which we
> think are logical and reasonable:
>
> 1. If specified, the set visibility must be tighter than the get
> visibility.  So `protected protected(set)` and `protected public(set)` are
> not permitted, for instance.
>
> 2. `readonly` is a "write once" flag that may be combined with asymmetric
> visibility.  If no set visibility is specified, `readoly` implies
> `private(set)`, but a different set visibility may also be provided.


>
These are both reasonable rules.  However, it creates a conflict.
> Specifically, in the following cases:
>
To unsubscribe, visit: https://www.php.net/unsub.php
>
>
The more I think about this, the more it makes more sense (to me) to
simplify it even further;

1) The set visibility should be the same as the get visibility when one is
not provided. That is:

```
public $var -> public public(set) $var
protected $var -> protected protected(set) $var
private $var -> private private(set) $var
```

The explicit declaration of same visibility can be disallowed e.g. `public
public(set) $var` is not allowed, you must use `public $var` if you want
visibility symmetry. However, if it simplifies implementation to allow it,
why not? The behavior would already be in place anyway.

2) The readonly flag should no longer have an impact on the set visibility.
This is again a conceptual breaking change, but one that can even be fixed
with a SEARCH/REPLACE on a project scope. The "breaking change" is
categorized as follows:

The code `public readonly $var` used to be `public private(set) readonly
$var` should become `public public(set) readonly $var`
The code `protected readonly $var` used to be `protected private(set)
readonly $var` should become `protected protected(set) readonly $var`
The code `private readonly $var` should not have any change.

-- 
Marco Deleu

---

## #119000 — Claude Pache — 2022-11-14T07:04:24+00:00

URL: https://externals.io/message/118994#119000 · In reply to: #118994

> 
> public public(set) readonly string $foo
> 
> protected protected(set) readonly string $foo
> 
> These would be the only way to have a non-private-set readonly property.  While the first is in practice quite unlikely, the second has valid use cases.  

Both have use cases. Whether they are valid is quite subjective. (I am speaking as someone who suffer from the `match()` construct forbidding having both `default` and  some other `case` pointing to the same expression. That restriction seemed reasonable the time the feature was designed and voted.)

> 
> 1. Relax the set-is-tighter restriction.  That would allow `protected protected(set)` etc. on any property.  It wouldn't be particularly useful unless readonly is being used, but it would be syntactically legal and behave as you'd expect.  We could still disallow "set is more permissive" combinations (eg, `private public(set)`), as those have no apparent use case.

I think that Option 1 is the most reasonable. The meaning of `public public(set)` and `protected protected(set)` is straightforward; from a user point-of-view, disallowing them seems more like “it is useless” than “it is confusing”. Unless there are good technical reason to restrict it, we can just leave to linting tools the care of reporting it.

—Claude

---

## #119001 — Stephen Reay — 2022-11-14T09:15:04+00:00

URL: https://externals.io/message/118994#119001 · In reply to: #118994

> 
> 
> Hi folks.  Ilija is nearly done with the implementation for asymmetric visibility and flushing out edge cases, but we've run into one design question we'd like feedback on.
> 
> There's two design decisions we've made at this point, both of which we think are logical and reasonable:
> 
> 1. If specified, the set visibility must be tighter than the get visibility.  So `protected protected(set)` and `protected public(set)` are not permitted, for instance.
> 
> 2. `readonly` is a "write once" flag that may be combined with asymmetric visibility.  If no set visibility is specified, `readoly` implies `private(set)`, but a different set visibility may also be provided.
> 
> These are both reasonable rules.  However, it creates a conflict.  Specifically, in the following cases:
> 
> public public(set) readonly string $foo
> 
> protected protected(set) readonly string $foo
> 
> These would be the only way to have a non-private-set readonly property.  While the first is in practice quite unlikely, the second has valid use cases.  (In particular, a base class that provides properties expected to be set by a child constructor, and then used by a method in the parent class.)  However, it would not be allowed under the rules above.  Working around it would require specifying `public protected(set) readonly...`, which means exposing a property that likely should not be exposed.
> 
> That creates an odd situation where readonly and asymmetric visibility may only be combined "sometimes."  That is not deesireable.  The only way to combine them in their current form is to allow `protected protected(set)` only if readonly is in use, which is excessively complicated both to implement and to explain/document/use.
> 
> We see two possible ways to resolve this conflict:
> 
> 1. Relax the set-is-tighter restriction.  That would allow `protected protected(set)` etc. on any property.  It wouldn't be particularly useful unless readonly is being used, but it would be syntactically legal and behave as you'd expect.  We could still disallow "set is more permissive" combinations (eg, `private public(set)`), as those have no apparent use case.
> 
> 2. Disallow readonly and asymmetric visibility being combined, because readonly already has a hard-coded implied asymmetric visibility.  This option removes some potential use cases (they would most likely drop the readonly), but has the upside that it's easier to re-allow at some point in the future.
> 
> 3. Some other brilliant idea we've not thought of.
> 
> 
> Both are viable approaches with pros and cons.  We're split on which way to go with this, so we throw it out to the group for feedback.  Which approach would you favor, or do you have some other brilliant idea to square this circle?
> 
> 
> -- 
>  Larry Garfield
>  larry@garfieldtech.com
> 
> -- 
> 
> To unsubscribe, visit: https://www.php.net/unsub.php
> 

Hi Larry,

I asked for some clarification / further thought about the magic getters/setters interaction of this RFC back when you announced it, and never heard much besides saying that you’re working on a consolidated response to the feedback, but I’ve not seen anything regarding what I’d asked about, and the RFC still seems to have the (IMO) confusing and inconsistent magic getters/setters interaction described.


As I understand it, you’re suggesting that a property declared as `public protected(set)` would never trigger __set().


While I understand that this is meant to “keep consistency with readonly”, I would imagine people would want to use native asymmetric visibility on objects that currently have protected (or private) properties that are accessed via magic getters and setters. I think it would be a natural assumption that asymmetric visibility would allow the user to remove the often boilerplate __get method, while maintaining the ability to have checks/other logic beyond simple type checks when a property is being set. Having the association of a *set* action tied to the *get* visibility is very unintuitive to me.

The impact of this “same” limitation on readonly properties is IMO greatly reduced, because of the nature of something being readonly. The ability to run some logic when writing to a property that’s otherwise readable is surely a concept that’s very common and easy to understand, it’s literally the next example after (1) “readonly” and (2)  “asymmetric visibility” in Nikita’s original property accessors RFC.

The stated reason (on both this and the readonly RFC) for not allowing __set() is "to avoid surprising behaviour”. To me, having a field marked as `public protected(set)` then not call __set() when setting a property from outside the class **is** the surprising behaviour.



Cheers

Stephen

---

## #119002 — Derick Rethans — 2022-11-14T11:28:13+00:00

URL: https://externals.io/message/118994#119002 · In reply to: #118994

> There's two design decisions we've made at this point, both of which 
> we think are logical and reasonable:
> 
> 1. If specified, the set visibility must be tighter than the get 
> visibility.  So `protected protected(set)` and `protected public(set)` 
> are not permitted, for instance.
> 
> 2. `readonly` is a "write once" flag that may be combined with 
> asymmetric visibility.  If no set visibility is specified, `readoly` 
> implies `private(set)`, but a different set visibility may also be 
> provided.
> 
> These are both reasonable rules.  However, it creates a conflict.  
> Specifically, in the following cases:
> 
> public public(set) readonly string $foo
> 
> protected protected(set) readonly string $foo

Just to see if I understand this correctly, the "protected(set)" 
overwrites the implicit "private(set)" that "readonly" confers?

I think that alone is confusing.

> These would be the only way to have a non-private-set readonly 
> property.  While the first is in practice quite unlikely, the second 
> has valid use cases.  (In particular, a base class that provides 
> properties expected to be set by a child constructor, and then used by 
> a method in the parent class.)  However, it would not be allowed under 
> the rules above.  Working around it would require specifying `public 
> protected(set) readonly...`, which means exposing a property that 
> likely should not be exposed.
> 
> That creates an odd situation where readonly and asymmetric visibility 
> may only be combined "sometimes."  That is not deesireable.  The only 
> way to combine them in their current form is to allow `protected 
> protected(set)` only if readonly is in use, which is excessively 
> complicated both to implement and to explain/document/use.
> 
> We see two possible ways to resolve this conflict:
> 
> 1. Relax the set-is-tighter restriction.  That would allow `protected 
> protected(set)` etc. on any property.  It wouldn't be particularly 
> useful unless readonly is being used, but it would be syntactically 
> legal and behave as you'd expect.  We could still disallow "set is 
> more permissive" combinations (eg, `private public(set)`), as those 
> have no apparent use case.
> 
> 2. Disallow readonly and asymmetric visibility being combined, because 
> readonly already has a hard-coded implied asymmetric visibility.  
> This option removes some potential use cases (they would most likely 
> drop the readonly), but has the upside that it's easier to re-allow at 
> some point in the future.

I am in favour of this option 2. As I already think "protected 
protected(set) readonly" is a confusing thing to see. If I hadn't read 
this email, I wouldn't have understood the interacting between the 
"protected(set)" and "readonly".

cheers,
Derick

---

## #119003 — Derick Rethans — 2022-11-14T11:32:04+00:00

URL: https://externals.io/message/118994#119003 · In reply to: #119001

> As I understand it, you’re suggesting that a property declared as 
> `public protected(set)` would never trigger __set().

I would think that that would be against our current practice, which is 
easy enough to explain as "if the property isn't visible set, 
then use __set()". I would argue that "protected(set)" marks a property 
as invisible from a non-inherited class/method, and hence __set should 
be called.

cheers,
Derick

---

## #119004 — Ilija Tovilo — 2022-11-14T12:00:23+00:00

URL: https://externals.io/message/118994#119004 · In reply to: #119003

Hi Derick

> > […]
>
> I would think that that would be against our current practice, which is
> easy enough to explain as "if the property isn't visible set,
> then use __set()". I would argue that "protected(set)" marks a property
> as invisible from a non-inherited class/method, and hence __set should
> be called.

What you're describing does not match the readonly behavior.
https://3v4l.org/X76pV

Readonly properties only call __set once they are explicitly unset.
The same applies to __unset. I changed this in the asymmetric
visibility implementation recently to match this behavior but the RFC
has not been adjusted to reflect this yet.

AFAIK this behavior is there to allow calls to __set for certain edge
cases. While not very intuitive it makes sense to stay consistent. If
this behavior is undesirable we should change it in both places.

Ilija

---

## #119005 — Larry Garfield — 2022-11-14T17:50:27+00:00

URL: https://externals.io/message/118994#119005 · In reply to: #118998

> > […]
>
> I do understand the slight complexity here (perhaps stronger in terms of
> implementation), but my assumption was that if a property is private and
> "the set visibility must be tighter", PHP would never assume `private
> protected(set) readonly`. In other words, if `private readonly` then
> `private private(set) readonly`, therefore nothing changes. You can keep
> the use of disjointed variables in inheritance provided they're all
> private. The only concern is when a property is declared protected, which
> then evaluates to what I mentioned on the previous email.

I don't follow.  Right now, this is legal:

class P {
  protected readonly string $foo;

  public useFoo() {
    if ($this->foo == 'beep') { ... }
  }
}

class C extends P {
  protected readonly string $foo;

  public function __construct(string $foo) {
    $this->foo = $foo;
  }
}

The redeclaration of $foo is necessary because of the implicit private-set on readonly today.  I have code that does this.

If we go with options 1 or 4, then that could be simplified to:

class P {
  protected protected(set) readonly string $foo;

  public useFoo() {
    if ($this->foo == 'beep') { ... }
  }
}

class C extends P {
  public function __construct(string $foo) {
    $this->foo = $foo;
  }
}

This would not require "set is wider", just "set is the same".

If we changed readonly to be an implicit protected(set) instead of private(set), that would also resolve that use case but I do not know if that would cause other problems with code like the above.

> I'm also under the stated assumption that `public public(set) readonly` is
> not something needed to be supported.

I am not aware of any use cases for it, though if it technically becomes possible as a side effect of other changes I don't object.

--Larry Garfield

---

## #119006 — Claude Pache — 2022-11-14T20:02:50+00:00

URL: https://externals.io/message/118994#119006 · In reply to: #119000

> > […]
> 
> I think that Option 1 is the most reasonable. The meaning of `public public(set)` and `protected protected(set)` is straightforward; from a user point-of-view, disallowing them seems more like “it is useless” than “it is confusing”. Unless there are good technical reason to restrict it, we can just leave to linting tools the care of reporting it.
> 
> —Claude
> 

To clarify my position:

* The set visibility must be either more restrictive or of the same restriction level than the get visibility.

* When the set visibility is absent, it is inferred as following:
     * If `readonly` is present, the set visibility is `private` (as of today);
     * otherwise, the set visibility is the same as the get visibility (again, as of today).

* We don’t judge whether it is reasonable to write `protected protected(set) string $foo;` when you could just write `protected string $foo` for the same effect. Similarly, we don’t judge whether it is reasonable to write `public function()` when you could just write `function()` for the same effect. We leave it to coding styles and linters to decide whether the short form or the long form is preferred.

—Claude

---

## #119012 — Derick Rethans — 2022-11-15T10:34:09+00:00

URL: https://externals.io/message/118994#119012 · In reply to: #119004

> Hi Derick
> 
> > […]
> 
> What you're describing does not match the readonly behavior.
> https://3v4l.org/X76pV
> 
> Readonly properties only call __set once they are explicitly unset.
> The same applies to __unset. I changed this in the asymmetric
> visibility implementation recently to match this behavior but the RFC
> has not been adjusted to reflect this yet.
> 
> AFAIK this behavior is there to allow calls to __set for certain edge
> cases. While not very intuitive it makes sense to stay consistent. If
> this behavior is undesirable we should change it in both places.

Now I have re-read the Assymetric Visibility RFC's "Interaction with 
__set" 
(https://wiki.php.net/rfc/asymmetric-visibility#interaction_with_set), 
I'm slightly more confused. Could you update that section, as what it 
currently states seems reasonable.

cheers,
Derick

---

## #119018 — Dan Ackroyd — 2022-11-20T13:20:55+00:00

URL: https://externals.io/message/118994#119018 · In reply to: #118994

Hi Larry,

Regarding the syntax, up until now PHP has only supported the letters
a-z and underscore in keywords.

I realise this is an aesthetic thing, but "private(set)" looks like a
function to me, and not a keyword. I saw the previous poll, and it
didn't include options for either protected_set/private_set or
protectedset/privateset.

Was there a technical reason for excluding them as an option?


>
> That creates an odd situation where readonly and asymmetric visibility
> may only be combined "sometimes."...
>
> 2. Disallow readonly and asymmetric visibility being combined, because
> readonly already has a hard-coded implied asymmetric visibility.

This is getting quite complicated.

I think unless someone makes a strong case for allowing the
combination of the two, disallowing them being combined is probably
the best choice.

cheers
Dan
Ack

---

## #119021 — Rowan Tommins — 2022-11-21T15:48:38+00:00

URL: https://externals.io/message/118994#119021 · In reply to: #119018

> This is getting quite complicated.
>
> I think unless someone makes a strong case for allowing the
> combination of the two, disallowing them being combined is probably
> the best choice.


I'm inclined to agree. There's going to be a lot to understand and agree 
on this RFC as it is, so if some edge cases can reasonably be defined as 
forbidden, we're less likely to end up regretting some detail.

Then if a good use case is identified, there can be a follow-up RFC, 
either within this release cycle, or in a future release.

Regards,

-- 
Rowan Tommins
[IMSoP]

---

## #119023 — tim@bastelstu.be — 2022-11-22T18:08:43+00:00

URL: https://externals.io/message/118994#119023 · In reply to: #119006

Hi


> To clarify my position:
> 
> * The set visibility must be either more restrictive or of the same restriction level than the get visibility.
> 
> * When the set visibility is absent, it is inferred as following:
>       * If `readonly` is present, the set visibility is `private` (as of today);
>       * otherwise, the set visibility is the same as the get visibility (again, as of today).
> 
> * We don’t judge whether it is reasonable to write `protected protected(set) string $foo;` when you could just write `protected string $foo` for the same effect. Similarly, we don’t judge whether it is reasonable to write `public function()` when you could just write `function()` for the same effect. We leave it to coding styles and linters to decide whether the short form or the long form is preferred.
> 

I agree with that.

--------------

As I'm sending an email anyway: Larry, will there be a a new separate 
discussion thread, once all the problems are resolved and once it's 
clear what you propose? I just noticed the "Abbreviated Form" section in 
the RFC 
(https://wiki.php.net/rfc/asymmetric-visibility#abbreviated_form) which 
I disagree with, which apparently was added in October, but I remember 
an email letting readers know of the updated RFC. I didn't follow the 
evolution of the RFC too closely, though, because I believed that it 
still was in a somewhat early stage and because discussion is already 
split into way-to-many threads and also the poll.

Best regards
Tim Düsterhus

---

## #119030 — Larry Garfield — 2022-11-24T23:35:03+00:00

URL: https://externals.io/message/118994#119030 · In reply to: #119023

> Hi
>
> 
> > […]
>
> I agree with that.
>
> --------------
>
> As I'm sending an email anyway: Larry, will there be a a new separate 
> discussion thread, once all the problems are resolved and once it's 
> clear what you propose? I just noticed the "Abbreviated Form" section in 
> the RFC 
> (https://wiki.php.net/rfc/asymmetric-visibility#abbreviated_form) which 
> I disagree with, which apparently was added in October, but I remember 
> an email letting readers know of the updated RFC. I didn't follow the 
> evolution of the RFC too closely, though, because I believed that it 
> still was in a somewhat early stage and because discussion is already 
> split into way-to-many threads and also the poll.
>
> Best regards
> Tim Düsterhus

Yes, once we make a decision on this point, I'll respond in this thread.  

The abbreviated form was part of the discussion earlier, and the syntax we settled on supported it, so it made sense to include.

I try to highlight on the list any time notable changes are made, even if I don't always have a detailed changelog.  Whether it makes sense to use the same thread or a new thread varies, and I'm absolutely certain others will make a different judgement call on that than I do.  I'm not aware of any standard convention around that, so until one exists I'll just continue making vaguely educated guesses on that front.

Given the timing, Ilija says he'd prefer to not call the vote until after the new year/holidays.  Feedback still welcome at this point but aside from edge cases like this thread's, I think we're pretty well set on the basic features.  Baring any major change of direction expect the vote to be called in early January, probably.  Time permitting we're going to also start work on property hooks/accessors, which is the intended follow-on to this RFC.

--Larry Garfield

---

## #119031 — Larry Garfield — 2022-11-25T00:07:14+00:00

URL: https://externals.io/message/118994#119031 · In reply to: #119018

> Hi Larry,
>
> Regarding the syntax, up until now PHP has only supported the letters
> a-z and underscore in keywords.
>
> I realise this is an aesthetic thing, but "private(set)" looks like a
> function to me, and not a keyword. I saw the previous poll, and it
> didn't include options for either protected_set/private_set or
> protectedset/privateset.
>
> Was there a technical reason for excluding them as an option?

Not in particular.  We originally proposed `private(set)` because that's what Swift uses.  In the discussion people proposed several alternatives that I put into the poll, but `private_set` wasn't one of them.  Although several people said they were opposed to parens in the keyword, the two options that had parens (`private(set)` and `pubilc(set: private)`) were the leaders for all rounds.  I was quite surprised at that, personally; I expected `private:set` to do a lot better under the circumstances.  But that's not how the poll played out.

--Larry Garfield

---

## #119051 — Larry Garfield — 2022-11-29T20:29:14+00:00

URL: https://externals.io/message/118994#119051 · In reply to: #118994

> Hi folks.  Ilija is nearly done with the implementation for asymmetric 
> visibility and flushing out edge cases, but we've run into one design 
> question we'd like feedback on.
>
> There's two design decisions we've made at this point, both of which we 
> think are logical and reasonable:
>
> 1. If specified, the set visibility must be tighter than the get 
> visibility.  So `protected protected(set)` and `protected public(set)` 
> are not permitted, for instance.
>
> 2. `readonly` is a "write once" flag that may be combined with 
> asymmetric visibility.  If no set visibility is specified, `readoly` 
> implies `private(set)`, but a different set visibility may also be 
> provided.
>
> These are both reasonable rules.  However, it creates a conflict.  
> Specifically, in the following cases:
>
> public public(set) readonly string $foo
>
> protected protected(set) readonly string $foo
>
> These would be the only way to have a non-private-set readonly 
> property.  While the first is in practice quite unlikely, the second 
> has valid use cases.  (In particular, a base class that provides 
> properties expected to be set by a child constructor, and then used by 
> a method in the parent class.)  However, it would not be allowed under 
> the rules above.  Working around it would require specifying `public 
> protected(set) readonly...`, which means exposing a property that 
> likely should not be exposed.
>
> That creates an odd situation where readonly and asymmetric visibility 
> may only be combined "sometimes."  That is not deesireable.  The only 
> way to combine them in their current form is to allow `protected 
> protected(set)` only if readonly is in use, which is excessively 
> complicated both to implement and to explain/document/use.
>
> We see two possible ways to resolve this conflict:
>
> 1. Relax the set-is-tighter restriction.  That would allow `protected 
> protected(set)` etc. on any property.  It wouldn't be particularly 
> useful unless readonly is being used, but it would be syntactically 
> legal and behave as you'd expect.  We could still disallow "set is more 
> permissive" combinations (eg, `private public(set)`), as those have no 
> apparent use case.
>
> 2. Disallow readonly and asymmetric visibility being combined, because 
> readonly already has a hard-coded implied asymmetric visibility.  This 
> option removes some potential use cases (they would most likely drop 
> the readonly), but has the upside that it's easier to re-allow at some 
> point in the future.
>
> 3. Some other brilliant idea we've not thought of.
>
>
> Both are viable approaches with pros and cons.  We're split on which 
> way to go with this, so we throw it out to the group for feedback.  
> Which approach would you favor, or do you have some other brilliant 
> idea to square this circle?


Thank you everyone for the feedback.  Based on this thread, we've made two changes to the RFC:

1. We've moved readonly back to forbidden with a-viz for now.  I've added a section to Future Scope where we really should sort this out in the future, but we'll do that in the future when we can all focus on the various nuances of just that piece.

2. I rewrote the section on __set to make it clearer.  That also included Ilija and I digging into all the nuances that are already present.  The text may still look a bit complex, but that's because the existing logic is already complex with readonly.  Long story short, the a-viz RFC does not change anything in the way __set works vis a vis asymmetric visibility; it just inherits and continues what readonly already started, so it's consistent.

The PR should be updated in the next week or two with the latest changes.  Baring any major need for change, we expect to call a vote for it shortly after New Years.

Thanks all.

--Larry Garfield

---

## #119053 — Claude Pache — 2022-11-29T23:46:51+00:00

URL: https://externals.io/message/118994#119053 · In reply to: #119051

> Le 29 nov. 2022 à 21:29, Larry Garfield <larry@garfieldtech.com> a écrit :
> 
> 
> Thank you everyone for the feedback.  Based on this thread, we've made two changes to the RFC:
> 
> 1. We've moved readonly back to forbidden with a-viz for now.  I've added a section to Future Scope where we really should sort this out in the future, but we'll do that in the future when we can all focus on the various nuances of just that piece.
> 
> 2. I rewrote the section on __set to make it clearer.  That also included Ilija and I digging into all the nuances that are already present.  The text may still look a bit complex, but that's because the existing logic is already complex with readonly.  Long story short, the a-viz RFC does not change anything in the way __set works vis a vis asymmetric visibility; it just inherits and continues what readonly already started, so it's consistent.
> 
> The PR should be updated in the next week or two with the latest changes.  Baring any major need for change, we expect to call a vote for it shortly after New Years.
> 
> Thanks all.
> 
> --Larry Garfield
> 

Hi,

In the RFC, section Permitted visibility (https://wiki.php.net/rfc/asymmetric-visibility#permitted_visibility <https://wiki.php.net/rfc/asymmetric-visibility#permitted_visibility>):
> The set visibility, if it differs from the main (get) visibility, MUST be strictly lesser than the main visibility. That is, the set visibility may only be protected or private. If the main visibility is protected, set visibility may only be private. Any violation of this rule will result in a compile time error.
> 
The first sentence does not forbid `public public(set)`, or `protected protected(set)`, etc. (the `set` visibility does not differ from the main visibility), but the rest of the paragraph does not allow it. That should be clarified.

(Because forbidding `public public(set)`, etc., makes it slightly more cumbersome to explain the rules, I am slightly in favour not to forbid it.)

> There is one exception, that of a private readonly property. That would technically expand to private private(set) readonly, which is allowed.

That sentence should be deleted, as `readonly` is now forbidden.

—Claude

---

## #119054 — Larry Garfield — 2022-11-30T01:27:41+00:00

URL: https://externals.io/message/118994#119054 · In reply to: #119053

> In the RFC, section Permitted visibility 
> (https://wiki.php.net/rfc/asymmetric-visibility#permitted_visibility 
> <https://wiki.php.net/rfc/asymmetric-visibility#permitted_visibility>):
> > […]
> The first sentence does not forbid `public public(set)`, or `protected 
> protected(set)`, etc. (the `set` visibility does not differ from the 
> main visibility), but the rest of the paragraph does not allow it. That 
> should be clarified.

Er.  That's exactly what it says: "strictly lesser" than the main visibility.  The lines after are just restating it.  "public public(set)" is not allowed.

(We may relax that in the future to make it compatible with readonly, but that's for later.)

>
> (Because forbidding `public public(set)`, etc., makes it slightly more 
> cumbersome to explain the rules, I am slightly in favour not to forbid 
> it.)
>
> > […]
>
> That sentence should be deleted, as `readonly` is now forbidden.

Good catch, fixed.  Thanks.

--Larry Garfield

---

## #119055 — Stephen Reay — 2022-11-30T05:25:20+00:00

URL: https://externals.io/message/118994#119055 · In reply to: #119054

> 
> 
> 
> 
> > […]
> 
> Er.  That's exactly what it says: "strictly lesser" than the main visibility.  The lines after are just restating it.  "public public(set)" is not allowed.
> 
> (We may relax that in the future to make it compatible with readonly, but that's for later.)
> 
> > […]
> 
> Good catch, fixed.  Thanks.
> 
> --Larry Garfield
> 
> -- 
> 
> To unsubscribe, visit: https://www.php.net/unsub.php
> 

Hi Larry,

Thank you for clarifying the setter behaviour in more explicit terms, but I have to say I’m quite disappointed in this continued “use the logic of readonly to apply to something that is explicitly not readonly” - this is even more stark now that you’ve explicitly made them mutually exclusive behaviours.

I’m generally very in favour of maintaining consistency, but this seems like it’s using technical consistency as an excuse to justify unintuitive behaviour that breaks consistency in another, much more obvious way.


Can you explain why it makes more sense to maintain consistency with “readonly” than it does to maintain consistency with the existing “__set()” behaviour for properties, particularly now that you’ve indicated these features (asymmetric visibility and readonly) are mutually exclusive? 

While it’s stated multiple times that “readonly” introduced a limited form of asymmetric visibility, and thus this is a continuation, in terms of intuitiveness, the existing __set() rules are very easy to comprehend even with readonly:

- if the property is declared as public, __set() is never called; if it’s declared as protected, __set is called when the property is accessed from outside that class or it’s hierarchy. Yes, I know that readonly imposes an implicit visibility difference - but that is essentially an implementation detail, from the point of view of the userland developer, it’s not a clear statement of intended behaviour on their part, expressed through the code as written.

For example, with `public readonly int $foo` it’s quite obvious why __set() isn’t called, using the exiting well-understood logic: it’s a public property. PHP applies a kind of asymmetric visibility to the property behind the scenes, but that isn’t what the developer declared, it’s the implementation. This behaviour matches that of regular, non-readonly fields: when the field is declared public (or has implicit public visibility) __set() is never called.

If we make that field protected, __set() will be called when the property is written to from outside the class, regardless of whether it’s readonly or not.


What you’re proposing changes that, in a way that is completely unintuitive: when attempting to *write* data to a property that is marked as protected(set), the __set() method will not be called.


So please, can you explain to me why consistency with an implementation detail of readonly properties is more important than consistency with declared developer intention for regular properties via the magic setter method?



Cheers

Stephen

---

## #119056 — drealecs@gmail.com — 2022-11-30T08:41:29+00:00

URL: https://externals.io/message/118994#119056 · In reply to: #119055

>
>
> > […]
> be strictly lesser than the main visibility. That is, the set visibility
> may only be protected or private. If the main visibility is protected, set
> visibility may only be private. Any violation of this rule will result in a
> compile time error.
> > […]
> visibility.  The lines after are just restating it.  "public public(set)"
> is not allowed.
> > […]
> but that's for later.)
> > […]
> would technically expand to private private(set) readonly, which is allowed.
> > […]
>
> Hi Larry,
>
> Thank you for clarifying the setter behaviour in more explicit terms, but
> I have to say I’m quite disappointed in this continued “use the logic of
> readonly to apply to something that is explicitly not readonly” - this is
> even more stark now that you’ve explicitly made them mutually exclusive
> behaviours.
>
> I’m generally very in favour of maintaining consistency, but this seems
> like it’s using technical consistency as an excuse to justify unintuitive
> behaviour that breaks consistency in another, much more obvious way.
>
>
> Can you explain why it makes more sense to maintain consistency with
> “readonly” than it does to maintain consistency with the existing “__set()”
> behaviour for properties, particularly now that you’ve indicated these
> features (asymmetric visibility and readonly) are mutually exclusive?
>
> While it’s stated multiple times that “readonly” introduced a limited form
> of asymmetric visibility, and thus this is a continuation, in terms of
> intuitiveness, the existing __set() rules are very easy to comprehend even
> with readonly:
>
> - if the property is declared as public, __set() is never called; if it’s
> declared as protected, __set is called when the property is accessed from
> outside that class or it’s hierarchy. Yes, I know that readonly imposes an
> implicit visibility difference - but that is essentially an implementation
> detail, from the point of view of the userland developer, it’s not a clear
> statement of intended behaviour on their part, expressed through the code
> as written.
>
> For example, with `public readonly int $foo` it’s quite obvious why
> __set() isn’t called, using the exiting well-understood logic: it’s a
> public property. PHP applies a kind of asymmetric visibility to the
> property behind the scenes, but that isn’t what the developer declared,
> it’s the implementation. This behaviour matches that of regular,
> non-readonly fields: when the field is declared public (or has implicit
> public visibility) __set() is never called.
>
> If we make that field protected, __set() will be called when the property
> is written to from outside the class, regardless of whether it’s readonly
> or not.
>
>
> What you’re proposing changes that, in a way that is completely
> unintuitive: when attempting to *write* data to a property that is marked
> as protected(set), the __set() method will not be called.
>
>
> So please, can you explain to me why consistency with an implementation
> detail of readonly properties is more important than consistency with
> declared developer intention for regular properties via the magic setter
> method?
>
>
Hey, Stephen, Larry,
Just clarifying how I see it:

Practically, the asymmetric visibility for properties is not more or less
than asymmetric write-access for properties.
That is what is implemented, that is what other languages have as well and
this is what's needed.
Of course, except for relation with __set(), unset() and other php
specials, it wouldn't make a difference.

If you think it like this, that property is actually visible but only there
is a restriction on writing it, you would probably agree that __set()
should not be called.

And given that, for clarity, I would be happy if the word visibility would
be changed in the rfc with write-access in various places, including the
rfc name.
But I don't think it's very important, as long as I've understood the
behavior and I can easily explain it going forward.

Regards,
Alex



>
> Cheers
>
> Stephen
>
> --
> 
> To unsubscribe, visit: https://www.php.net/unsub.php

---

## #119058 — Stephen Reay — 2022-11-30T09:27:29+00:00

URL: https://externals.io/message/118994#119058 · In reply to: #119056

> 
> 
> 
> 
> 
> 
> > […]
> 
> Hi Larry,
> 
> Thank you for clarifying the setter behaviour in more explicit terms, but I have to say I’m quite disappointed in this continued “use the logic of readonly to apply to something that is explicitly not readonly” - this is even more stark now that you’ve explicitly made them mutually exclusive behaviours.
> 
> I’m generally very in favour of maintaining consistency, but this seems like it’s using technical consistency as an excuse to justify unintuitive behaviour that breaks consistency in another, much more obvious way.
> 
> 
> Can you explain why it makes more sense to maintain consistency with “readonly” than it does to maintain consistency with the existing “__set()” behaviour for properties, particularly now that you’ve indicated these features (asymmetric visibility and readonly) are mutually exclusive? 
> 
> While it’s stated multiple times that “readonly” introduced a limited form of asymmetric visibility, and thus this is a continuation, in terms of intuitiveness, the existing __set() rules are very easy to comprehend even with readonly:
> 
> - if the property is declared as public, __set() is never called; if it’s declared as protected, __set is called when the property is accessed from outside that class or it’s hierarchy. Yes, I know that readonly imposes an implicit visibility difference - but that is essentially an implementation detail, from the point of view of the userland developer, it’s not a clear statement of intended behaviour on their part, expressed through the code as written.
> 
> For example, with `public readonly int $foo` it’s quite obvious why __set() isn’t called, using the exiting well-understood logic: it’s a public property. PHP applies a kind of asymmetric visibility to the property behind the scenes, but that isn’t what the developer declared, it’s the implementation. This behaviour matches that of regular, non-readonly fields: when the field is declared public (or has implicit public visibility) __set() is never called.
> 
> If we make that field protected, __set() will be called when the property is written to from outside the class, regardless of whether it’s readonly or not.
> 
> 
> What you’re proposing changes that, in a way that is completely unintuitive: when attempting to *write* data to a property that is marked as protected(set), the __set() method will not be called.
> 
> 
> So please, can you explain to me why consistency with an implementation detail of readonly properties is more important than consistency with declared developer intention for regular properties via the magic setter method?
> 
> 
> Hey, Stephen, Larry,
> Just clarifying how I see it:
> 
> Practically, the asymmetric visibility for properties is not more or less than asymmetric write-access for properties.
> That is what is implemented, that is what other languages have as well and this is what's needed.
> Of course, except for relation with __set(), unset() and other php specials, it wouldn't make a difference.
> 
> If you think it like this, that property is actually visible but only there is a restriction on writing it, you would probably agree that __set() should not be called.
> 
> And given that, for clarity, I would be happy if the word visibility would be changed in the rfc with write-access in various places, including the rfc name.
> But I don't think it's very important, as long as I've understood the behavior and I can easily explain it going forward.
> 
> Regards,
> Alex
> 
> 
> 
> 
> Cheers
> 
> Stephen 
> 
> -- 
> 
> To unsubscribe, visit: https://www.php.net/unsub.php <https://www.php.net/unsub.php>

Hi Alex,

No, I do not agree, and I don't think it’s a great sign that you’re suggesting we need to mentally swap out a significant part of what’s written in the RFC for some different concept to have it make sense.

The first paragraph, and the initial section of the proposal make it quite clear that this RFC is about splitting the existing visibility concept into ‘get’ and ’set’ operations. 

I find it hard to believe that the authors of the RFC would manage to reference the existing visibility system so many times and in so many ways, if they weren’t explicitly talking about a change related to visibility.



Cheers 

Stephen

---

Next page: https://externals.io/message/118994.md?page=2
