[PHP-DEV] [RFC] Asymmetric Visibility, v2

On Tue, Jun 4, 2024, at 5:01 AM, Andreas Heigl wrote:

There is only one thing that I stumbled upon which struck me as odd:

> The set visibility, if specified explicitly, MUST be equal to or
> lesser than the main (get) visibility. That is, protected public(set)
> string $foo is not allowed.

Why?

Why can we not set a property as publicly writable but unreadable?

If that is a technical necessity, then so be it. But if that is a
logical limitation, then I'm asking myself: why do we want to explicitly
limit possible usecases? Not that I have one right now in my mind but I
do know that we used similar settings in Filesystems for letterbox
systems where users could copy files into other users letterbox (write
only) without being able to later see the files.

Similar possibilities are available here here a property could be
publicly writeable but only privately readable as read-access is only
granted via a method.

I'm not saying it's the usualy use-case, but why explicitly disallowing it?

It's not a technical requirement. Mainly, we just cannot think of a reason why you'd ever do that, so making some seemingly nonsensical cases illegal seems like a good guardrail.

If enough people felt strongly that it should be allowed, I don't think there's any technical reason it couldn't be allowed, other than it would allow some rather silly combinations. (Ilija can tell me if I'm wrong.) However, also note that it is, of course, much easier to allow more combinations in the future than to remove them, should we find they cause trouble.

--Larry Garfield

Hi

On 6/4/24 15:30, Larry Garfield wrote:

If enough people felt strongly that it should be allowed, I don't think there's any technical reason it couldn't be allowed, other than it would allow some rather silly combinations. (Ilija can tell me if I'm wrong.) However, also note that it is, of course, much easier to allow more combinations in the future than to remove them, should we find they cause trouble.

One thing that would get pretty wonky would be private-read properties: Private property names are currently internally "mangled" to include the class name. This allows to define the same private property in multiple classes of an inheritance chain, without those classes needing to know about the private properties of each other and making the addition and removal of a private property not a BC break. For all intents and purposes those private properties to not exist, unless you are the class itself.

I have no idea what the semantics of a public-write, private-read property should be - and this problem is pretty similar to the sibling-discussion about making private-set properties implicitly final, because otherwise the semantics get wonky.

I believe that the case of making a property public-write, private-read is best left to a virtual set-only hook.

Best regards
Tim Düsterhus

On Fri, May 31, 2024, at 8:59 PM, Larry Garfield wrote:

On Fri, May 31, 2024, at 5:45 PM, Claude Pache wrote:

Le 31 mai 2024 à 18:08, Larry Garfield <larry@garfieldtech.com> a écrit :

However, this also brings up another interesting issue: readonly properties (in 8.3) DO allow redeclaration, essentially adjusting the property scope (the class that declares it) to make the visibility check pass. That is, the definition of the class it is private to changes, which is different from how inheritance works elsewhere. When the parent writes to the same property, a special check is needed to verify the two properties are related. All that special casing effectively means that readonly in 8.4 wouldn't really be "write once + private(set)", but "write once + private(set) - final", which is... just kinda screwy. That means our options are:

* A BC break on readonly (not allowing it to be overridden)
* Make readonly an exception to the implicit final.
* Just don't allow readonly with aviz after all.

Another possible option is:

* Make readonly be `protected(set)` by default.

That would weaken the originally intended semantics of readonly, but in
a compatible and acceptable way?

—Claude

Only sort of compatible. It would allow readonly props to be
redefined, and wouldn't break any existing code, I think... but it
would mean any time you use readonly, suddenly a child class can come
along and mess with it out from under you.

In practice that's likely not a common concern, but it is a behavior
change. I think it's possible (I need to confirm with Ilija), if we
want that slight BC break, but I don't know if we do.

--Larry Garfield

Ilija and I have been discussing this issue over the last few days. We agree that `private(set)` should imply `final`, as that eliminates a bunch of issues both implementation-wise and expectation-wise. However, that causes an issue for `readonly`.

The root issue is that if we say "`readonly int $x` is really just `private(set) readonly int $x`", that runs into the issue of "whelp, you've just made readonly always final, which is a BC break." So that's no good.

We see a couple of ways to resolve this, presented below in our order of preference.

1. Disallow readonly with aviz => No BC break, and we don't need to define readonly in terms of private(set). The only really useful combination anyway would be `readonly protected(set)`, in which case the protected(set) is doing 90% of the work already. There's few cases where the readonly is truly necessary at that point. Any other oddball edge cases could be handled by a custom hook.
2. Make `readonly` implicitly `protected(set)` unless you explicitly specify `private(set)` => Would have the most consistent result, and this is probably the cleanest in the engine, as `readonly private(set)` would mean exactly what it says on the tin, with no inconsistency of "well it's kinda sorta `private(set)`" as `readonly` has now. However, this would be an expectation change, as suddenly all readonly properties could be written to from a child class. That may be good in some cases but it's possible some objects could have unexpected behavior if they didn't expect to be extended. (No existing code will break, but you could now do things to it in a child class that the author didn't anticipate.)
3. You can't mix `readonly` with `private(set)`, but can use other visibilities => No BC break, and we don't need to define readonly in terms of `private(set)`. However, it means the implicit `private(set)` of `readonly` and an explicit private(set) behave differently (one is final, one is not). It also unclear if a `readonly` property can be overridden with `readonly protected(set)` only, or also `readonly private(set)`. If the latter, does it become implicitly `final` at that point?
4. `readonly` behaves differently for an explicit (final) and implicit (not-final) `private(set)` => No BC break, but it's kinda weird and non-obvious to explain. It also has the same non-obvious inheritance questions as option 3.

We consider only the first two to be really viable. For simplicity, we'd favor doing option 1, and if desired option 2 could be presented in the future as its own RFC as that is technically a behavior change, not just addition, so deserves careful consideration. However, if there is a clear consensus to go with option 2 now, we're open to that.

--Larry Garfield

Le 5 juin 2024 à 16:28, Larry Garfield larry@garfieldtech.com a écrit :

On Fri, May 31, 2024, at 8:59 PM, Larry Garfield wrote:

On Fri, May 31, 2024, at 5:45 PM, Claude Pache wrote:

Le 31 mai 2024 à 18:08, Larry Garfield larry@garfieldtech.com a écrit :

However, this also brings up another interesting issue: readonly properties (in 8.3) DO allow redeclaration, essentially adjusting the property scope (the class that declares it) to make the visibility check pass. That is, the definition of the class it is private to changes, which is different from how inheritance works elsewhere. When the parent writes to the same property, a special check is needed to verify the two properties are related. All that special casing effectively means that readonly in 8.4 wouldn’t really be “write once + private(set)”, but “write once + private(set) - final”, which is… just kinda screwy. That means our options are:

  • A BC break on readonly (not allowing it to be overridden)
  • Make readonly an exception to the implicit final.
  • Just don’t allow readonly with aviz after all.

Another possible option is:

  • Make readonly be protected(set) by default.

That would weaken the originally intended semantics of readonly, but in
a compatible and acceptable way?

—Claude

Only sort of compatible. It would allow readonly props to be
redefined, and wouldn’t break any existing code, I think… but it
would mean any time you use readonly, suddenly a child class can come
along and mess with it out from under you.

In practice that’s likely not a common concern, but it is a behavior
change. I think it’s possible (I need to confirm with Ilija), if we
want that slight BC break, but I don’t know if we do.

–Larry Garfield

Ilija and I have been discussing this issue over the last few days. We agree that private(set) should imply final, as that eliminates a bunch of issues both implementation-wise and expectation-wise. However, that causes an issue for readonly.

The root issue is that if we say “readonly int $x is really just private(set) readonly int $x”, that runs into the issue of “whelp, you’ve just made readonly always final, which is a BC break.” So that’s no good.

We see a couple of ways to resolve this, presented below in our order of preference.

  1. Disallow readonly with aviz => No BC break, and we don’t need to define readonly in terms of private(set). The only really useful combination anyway would be readonly protected(set), in which case the protected(set) is doing 90% of the work already. There’s few cases where the readonly is truly necessary at that point. Any other oddball edge cases could be handled by a custom hook.
  2. Make readonly implicitly protected(set) unless you explicitly specify private(set) => Would have the most consistent result, and this is probably the cleanest in the engine, as readonly private(set) would mean exactly what it says on the tin, with no inconsistency of “well it’s kinda sorta private(set)” as readonly has now. However, this would be an expectation change, as suddenly all readonly properties could be written to from a child class. That may be good in some cases but it’s possible some objects could have unexpected behavior if they didn’t expect to be extended. (No existing code will break, but you could now do things to it in a child class that the author didn’t anticipate.)
  3. You can’t mix readonly with private(set), but can use other visibilities => No BC break, and we don’t need to define readonly in terms of private(set). However, it means the implicit private(set) of readonly and an explicit private(set) behave differently (one is final, one is not). It also unclear if a readonly property can be overridden with readonly protected(set) only, or also readonly private(set). If the latter, does it become implicitly final at that point?
  4. readonly behaves differently for an explicit (final) and implicit (not-final) private(set) => No BC break, but it’s kinda weird and non-obvious to explain. It also has the same non-obvious inheritance questions as option 3.

We consider only the first two to be really viable. For simplicity, we’d favor doing option 1, and if desired option 2 could be presented in the future as its own RFC as that is technically a behavior change, not just addition, so deserves careful consideration. However, if there is a clear consensus to go with option 2 now, we’re open to that.

–Larry Garfield

Hi Larry and Ilija,

Thanks for your work. Here is my opinion:

First, I do think that readonly should integrate with aviz, unless that implies truly controversial changes on readonly. As Theodore Brown commented in the previous version of the RFC: “Proposal feels unfinished since it can’t be used in conjunction with readonly properties/classes. In my opinion the issues with this need to be resolved first, to avoid the language moving towards a messy hodgepodge of features that don’t work well together.”

Second, I think that making readonly implicitly protected(set) by default (Option 2) is the way to go:

  • At first glance it is an expectation change. But, in reality, all readonly properties can already be written to from a child class as of today: it suffices that the child class in question redeclare those properties: https://3v4l.org/9AV4r. From the point of view of the child class, the only thing that will change, is that it will no longer be required to explicitly opt into that possibility by redeclaring the readonly properties. From the point of view of the parent class, nothing will change, except false expectations—and it is a good thing that false expectations are eliminated.
  • Relatively of Options 3 and 4, Option 2 leaves the language in a more simple and regular state.

—Claude

Hello everyone,I’ve been seeing readonly bashed/blamed/being roadblock, etc, etc as in the implementation ended up being sloppy and blocking other things or making things hard…
While I know BC is king and stuff, why not just say “yes, this was designed badly and we will redo it” and just do it? While there’s not yet an absolute boatload of that code out there when it would be absolutely massive BC break? Don’t repeat the mistakes of the old days :smiley:

Cause the impression I’m getting any significant RFC now has to work around the readonly’s sloppy implementation and there’s a bigger and bigger section on that with each next RFC when there’s more and more advanced features for the OOP part of things.

···

Arvīds Godjuks+371 26 851 664
arvids.godjuks@gmail.com
Telegram: @psihius https://t.me/psihius

Hi Tim

On Tue, Jun 4, 2024 at 7:54 PM Tim Düsterhus <tim@bastelstu.be> wrote:

One thing that would get pretty wonky would be private-read properties:
Private property names are currently internally "mangled" to include the
class name. This allows to define the same private property in multiple
classes of an inheritance chain, without those classes needing to know
about the private properties of each other and making the addition and
removal of a private property not a BC break. For all intents and
purposes those private properties to not exist, unless you are the class
itself.

I have no idea what the semantics of a public-write, private-read
property should be - and this problem is pretty similar to the
sibling-discussion about making private-set properties implicitly final,
because otherwise the semantics get wonky.

I believe that the case of making a property public-write, private-read
is best left to a virtual set-only hook.

Indeed. A private property with a more permissible set operation is
the wrong approach. What we'd want here is a public property with a
restricted get operation. This is not quite expressible with the
current syntax. We'd need something like `public private(get)`, or
`public $prop { private get; }` with the C# equivalent. However, this
is quite an edge case, and since it requires additional syntax I don't
think it's something we should support without a specific use-case.
You can emulate it with a set-only virtual property, if you really
want to.

Ilija

On Wed, Jun 5, 2024, at 7:55 PM, Arvids Godjuks wrote:

On Wed, 5 Jun 2024 at 19:59, Claude Pache <claude.pache@gmail.com> wrote:

*snip*
Hi Larry and Ilija,

Thanks for your work. Here is my opinion:

First, I do think that `readonly` should integrate with aviz, unless that implies truly controversial changes on `readonly`. As Theodore Brown commented in the previous version of the RFC: “Proposal feels unfinished since it can't be used in conjunction with readonly properties/classes. In my opinion the issues with this need to be resolved first, to avoid the language moving towards a messy hodgepodge of features that don't work well together.”

Second, I think that making `readonly` implicitly `protected(set)` by default (Option 2) is the way to go:
* At first glance it is an expectation change. But, in reality, all readonly properties can *already* be written to from a child class as of today: it suffices that the child class in question redeclare those properties: Online PHP editor | output for 9AV4r. From the point of view of the child class, the only thing that will change, is that it will no longer be required to explicitly opt into that possibility by redeclaring the readonly properties. From the point of view of the parent class, nothing will change, except false expectations—and it is a good thing that false expectations are eliminated.
* Relatively of Options 3 and 4, Option 2 leaves the language in a more simple and regular state.

That's a valid point, actually. The implicit "private(set)" is already untrue because of readonly-specific workarounds to enable redeclaration. Switching it to implicit "protected(set)" would remove that special case, and still allow an explicit private(set) if desired (which would then imply final).

Since no one else has weighed in, we'll go with that route: readonly on its own changes to implicit protected(set), remove any special casing, and then readonly and aviz should be safely compatible. I'll update the RFC and Ilija will confirm that there's no implementation gotchas we're not aware of yet.

Hello everyone,
I've been seeing readonly bashed/blamed/being roadblock, etc, etc as in
the implementation ended up being sloppy and blocking other things or
making things hard...
While I know BC is king and stuff, why not just say "yes, this was
designed badly and we will redo it" and just do it? While there's not
yet an absolute boatload of that code out there when it would be
absolutely massive BC break? Don't repeat the mistakes of the old days
:smiley:

Well, readonly has been out for 3 years. There is an absolute boatload of code out there that we do not want to break. :slight_smile:

Cause the impression I'm getting any significant RFC now has to work
around the readonly's sloppy implementation and there's a bigger and
bigger section on that with each next RFC when there's more and more
advanced features for the OOP part of things.

It's not a sloppy implementation per se. (I can't actually speak to the implementation myself.) It's the design of an implicit private(set) that works differently from any other private variable. The issue with "thou shalt not touch it outside of the constructor" isn't a language bug, it's a static-analyzer bug that those projects refuse to fix. Not something we can really do much about here. Uninitialized wasn't introduced by readonly but by property types in 7.4; readonly just inherited it. For hooks, the issue is that readonly needs a value to check to see if it's uninitialized, and with hooks, you don't always have that.

I think at this point, the change discussed above (making it implicit protected(set)) is the best we could do. In an ideal world, we would have never added readonly in the first place and just added aviz back in 8.1, which would cover nearly all the same use cases with fewer edge cases and oddities. Sadly, the world is not ideal.

--Larry Garfield

It does depend on what the fix is - if we are talking removing readonly keyword - that’s a yikes and if we go that route, it needs to have an official rector migration thing and it better be officially endorsed and provided :slight_smile:
If we are talking about tweaking how readonly works in some cases - while not great, I hold the opinion that it’s better to fix it now than 20 years down the line.

I do have to say that I do not see a “==” between aviz and readonly. While I can see how you can implement readonly with aviz, the boilerplate seems not worth it, especially for bigger classes/structures where you just designate the whole class with one “readonly class MyClass { … }”. And constructor promotion with “private readonly Class $class” with DI is basically all my services now - it’s convenient and short and I really do not need any more than that from the readonly :slight_smile: Maybe simplifying readonly is the answer and use aviz for more complicated cases?

···

Arvīds Godjuks+371 26 851 664
arvids.godjuks@gmail.com
Telegram: @psihius https://t.me/psihius

On Sat, Jun 8, 2024, at 3:49 AM, Arvids Godjuks wrote:

On Fri, 7 Jun 2024 at 17:30, Larry Garfield <larry@garfieldtech.com> wrote:

On Wed, Jun 5, 2024, at 7:55 PM, Arvids Godjuks wrote:
> On Wed, 5 Jun 2024 at 19:59, Claude Pache <claude.pache@gmail.com> wrote:
> *snip*
> Hello everyone,
> I've been seeing readonly bashed/blamed/being roadblock, etc, etc as in
> the implementation ended up being sloppy and blocking other things or
> making things hard...
> While I know BC is king and stuff, why not just say "yes, this was
> designed badly and we will redo it" and just do it? While there's not
> yet an absolute boatload of that code out there when it would be
> absolutely massive BC break? Don't repeat the mistakes of the old days
> :smiley:

Well, readonly has been out for 3 years. There is an absolute boatload of code out there that we do not want to break. :slight_smile:

> Cause the impression I'm getting any significant RFC now has to work
> around the readonly's sloppy implementation and there's a bigger and
> bigger section on that with each next RFC when there's more and more
> advanced features for the OOP part of things.

It's not a sloppy implementation per se. (I can't actually speak to the implementation myself.) It's the design of an implicit private(set) that works differently from any other private variable. The issue with "thou shalt not touch it outside of the constructor" isn't a language bug, it's a static-analyzer bug that those projects refuse to fix. Not something we can really do much about here. Uninitialized wasn't introduced by readonly but by property types in 7.4; readonly just inherited it. For hooks, the issue is that readonly needs a value to check to see if it's uninitialized, and with hooks, you don't always have that.

I think at this point, the change discussed above (making it implicit protected(set)) is the best we could do. In an ideal world, we would have never added readonly in the first place and just added aviz back in 8.1, which would cover nearly all the same use cases with fewer edge cases and oddities. Sadly, the world is not ideal.

--Larry Garfield

It does depend on what the fix is - if we are talking removing readonly
keyword - that's a yikes and if we go that route, it needs to have an
official rector migration thing and it better be officially endorsed
and provided :slight_smile:
If we are talking about tweaking how readonly works in some cases -
while not great, I hold the opinion that it's better to fix it now than
20 years down the line.

I do have to say that I do not see a "==" between aviz and readonly.
While I can see how you can implement readonly with aviz, the
boilerplate seems not worth it, especially for bigger
classes/structures where you just designate the whole class with one
"readonly class MyClass { ... }". And constructor promotion with
"private readonly Class $class" with DI is basically all my services
now - it's convenient and short and I really do not need any more than
that from the readonly :slight_smile: Maybe simplifying readonly is the answer and
use aviz for more complicated cases?

That is essentially what we've ended up on. For *services*, aviz is probably not super useful; for that matter readonly is only marginally useful in services, but a lot of people (myself included) use it as a matter of course anyway. The only change we're discussing there is that "public readonly" or "protected readonly" will now be implicitly "protected(set)" instead of implicitly "private(set)". If you're already using "private readonly", then that will actually get a bit stricter as it removes the loophole that exists only for readonly. So this is a very small, focused change.

Aviz is much more useful for data classes: value objects, DTOs, domain models, ORM models, and so on. For that, readonly serves one narrow use case and does it well, but falls apart and often gets in the way for anything even slightly more complex/interesting. For that, aviz offers vastly more flexibility and robustness in about the same amount of syntax.

--Larry Garfield

On Wed, May 29, 2024, 2:16 PM Larry Garfield <larry@garfieldtech.com> wrote:

As promised, Ilija and I offer this revised version of asymmetric visibility.

https://wiki.php.net/rfc/asymmetric-visibility-v2

It’s still essentially the same as last year’s version, but with a few adjustments and changes:

  • readonly properties are now supported in a logical fashion.
  • We’ve brought back the abbreviated form, as public-read, something else set is the most common use case.
  • The section on magic methods has been greatly simplified. The implementation itself hasn’t changed, but the explanation is a lot less confusing now.
  • We’ve explained how aviz interacts with hooks (they don’t, really) and with interface properties (in the obvious way), which didn’t exist at the time of the last draft.
  • We’ve added a section with examples of how aviz is a concrete improvement, even in a world with readonly and hooks.
  • We’ve added a section discussing why the prefix-style syntax was chosen.

dons flame retardant suit


Larry Garfield
larry@garfieldtech.com

Sending an email to quickly enable a new mailing list subscriber to engage in the conversation.

I didn't like the `Prefix-style` syntax. I prefer the `Hook-embedded-style` syntax. First let's look at the counterpoints of the `Prefix-style` syntax:

## 1) "`Prefix-style` is more visually scannable"

The set visibility is 10 lines away from the get visibility!

Solution:

class HookEmbeddedStyle
{
    public string $phone {
        private set {
            $this->phone = implode('', array_filter(fn($c) => is_numeric($c), explode($value)))
        }
        get {
            if (!$this->phone) {
                return '';
            }
            if ($this->phone[0] === 1) {
                return 'US ' . $this->phone;
            }
            return 'Intl +' . $this->phone;
        }
    }
}

The set visibility is not 10 lines away from the get visibility if you put it at the top. For me this is about code convention, not about syntactic structure:
only put the hook with explicit visibility at the top, when some of the hooks do not have explicit visibility!

## 2) "`Prefix-style` is shorter"

public private(set) string $name;

public string $name { private set; }

Irrelevant to consider 1-2 characters.
If you break the lines needed for the hook block, the line (the horizontal size) is smaller when using `Hook-embedded-style` and in my opinion it is more readable because there is less information per line:

public private(set) string $name;

public string $name {
   private set;
}

## 3) "`Prefix-style` doesn't presume a connection with hooks"

As noted above in "Interaction with hooks", visibility controls exist independently of hooks.

I agree, but with Property Hooks, this should now define the overall visibility only. Like a bigger gate that you open, and there are other doors defining the visibility of the operations get, set (hooks).

In fact, as implemented, they don't interact at all. Using hook syntax for visibility controls is therefore surprising and confusing.

Why "surprising and confusing"? I see hooks as a different kind of function/method.
Property Hooks RFC shouldn't pass without requiring hook parentheses, for any hook when the property is not abstract. The `$value` in the `set` hook seems to come out of nowhere to some people (the exception is for hooks declared on an abstract property which you can define without parameters and thus you are free to define whatever parameters you want on the concrete property).

When you define a method in PHP, it should be possible to omit the "function", but the parameters should be required because that's the nature of a function/method: to have parameters.

## 4) "It's non-obvious in `Hook-embedded-style` what hook behavior should be implied"

...So “arrays with hooks can do less” is already an established fact of the language. However, if the hook-style syntax is used for visibility:

class A
{
    public array $arr { protected set; }
}

class B extends A
{
    public function add(int $val)
    {
        // Should this be legal?
        $this->arr[] = $val;
    }
}

$b = new B();

$b->add(5);

First of all: non-abstract property hook must have a body.
Second: when asymmetric visibility is explicit, it means that symmetric visibility is implicit: a declared hook that does not have declared visibility has the same general visibility as the property, in this case: public.

Third:
There's another limitation on hooks here that makes things a bit confusing: there's a missing hook for a specific operation because you can clearly separate the `set` from the `push` operation...

Solution:

abstract class A
{
    abstract public array $arr {
        push; // Hook available only for "array" type properties only; public visibility
        private set;
    }
}

class B extends A
{
    public array $arr {
        push ($value) { // `public push ...` here is redundant
            // Mandatory to implement logic here.
        }
        private set ($value) {
            // Mandatory to Implement logic here.
        }
    }

    public function __construct ()
    {
        // Legal ✅ (set hook is protected)
        $this->arr = [1]; // call set hook
    }
    public function add(int $value)
    {
        // Legal ✅ (push hook is public)
        $this->arr[] = $value; // call push hook
    }
}

$b = new B();

$b->add(5);
$b->arr; // Legal ✅ (Inherited from the general visibility that is public)
$b->arr = [1, 2, 3]; // Fatal error ❌ - access to set hook is private only
$b->arr[] = 4; // Legal ✅ - call public push hook

My point: `Prefix-style` is not future-proof
## 1) The `Prefix-style` is not compatible with new hooks
If more hooks are added in the future, such as the `push` hook for arrays or even a hook that is compatible with all types such as `init`, `Hook-embedded-style` will become compatible, but `Prefix-style` not.

## 2) Hook overloading
If hook overloading is added in the future, `Prefix-style` would not be supported to have this granular visibility into operations. For example, it might be possible to add a public hook and a protected hook for the same operation, where one would be triggered if the operation occurred from outside the class, and the other if the operation occurred from inside the class.
Em 10 de jun. de 2024 13:15 -0300, Tiffany <tiffany.k.taylor@gmail.com> escreveu:

> On Wed, May 29, 2024, 2:16 PM Larry Garfield <larry@garfieldtech.com> wrote:
> > As promised, Ilija and I offer this revised version of asymmetric visibility.
> >
> > PHP: rfc:asymmetric-visibility-v2
> >
> > It's still essentially the same as last year's version, but with a few adjustments and changes:
> >
> > * readonly properties are now supported in a logical fashion.
> > * We've brought back the abbreviated form, as public-read, something else set is the most common use case.
> > * The section on magic methods has been greatly simplified. The implementation itself hasn't changed, but the explanation is a lot less confusing now.
> > * We've explained how aviz interacts with hooks (they don't, really) and with interface properties (in the obvious way), which didn't exist at the time of the last draft.
> > * We've added a section with examples of how aviz is a concrete improvement, even in a world with readonly and hooks.
> > * We've added a section discussing why the prefix-style syntax was chosen.
> >
> > *dons flame retardant suit*
> >
> > --
> > Larry Garfield
> > larry@garfieldtech.com

Sending an email to quickly enable a new mailing list subscriber to engage in the conversation.

You guys are killing PHP. There is a lot of work to be done on the engine
to modernize it and make it more robust and sturdy. Shit like this just
adds more complexity to PHP in the name of convenience. I think this is my
cue to explore other languages for handling requests

On Mon, Jun 10, 2024 at 1:18 PM Rodrigo Vieira <rodrigo.vieira92@outlook.com>
wrote:

I didn't like the `Prefix-style` syntax. I prefer the
`Hook-embedded-style` syntax. First let's look at the counterpoints of the
`Prefix-style` syntax:

## 1) "`Prefix-style` is more visually scannable"
> The set visibility is 10 lines away from the get visibility!

Solution:

class HookEmbeddedStyle
{
    public string $phone {
        private set {
            $this->phone = implode('', array_filter(fn($c) =>
is_numeric($c), explode($value)))
        }
        get {
            if (!$this->phone) {
                return '';
            }
            if ($this->phone[0] === 1) {
                return 'US ' . $this->phone;
            }
            return 'Intl +' . $this->phone;
        }
    }
}

The set visibility is not 10 lines away from the get visibility if you put
it at the top. For me this is about code convention, not about syntactic
structure:
only put the hook with explicit visibility at the top, when some of the
hooks do not have explicit visibility!

## 2) "`Prefix-style` is shorter"

public private(set) string $name;

public string $name { private set; }

Irrelevant to consider 1-2 characters.
If you break the lines needed for the hook block, the line (the horizontal
size) is smaller when using `Hook-embedded-style` and in my opinion it is
more readable because there is less information per line:

public private(set) string $name;

public string $name {
   private set;
}

## 3) "`Prefix-style` doesn't presume a connection with hooks"
> As noted above in "Interaction with hooks", visibility controls exist
independently of hooks.

I agree, but with Property Hooks, this should now define the overall
visibility only. Like a bigger gate that you open, and there are other
doors defining the visibility of the operations get, set (hooks).

> In fact, as implemented, they don't interact at all. Using hook syntax
for visibility controls is therefore surprising and confusing.

Why "surprising and confusing"? I see hooks as a different kind of
function/method.
Property Hooks RFC shouldn't pass without requiring hook parentheses, for
any hook when the property is not abstract. The `$value` in the `set` hook
seems to come out of nowhere to some people (the exception is for hooks
declared on an abstract property which you can define without parameters
and thus you are free to define whatever parameters you want on the
concrete property).

When you define a method in PHP, it should be possible to omit the
"function", but the parameters should be required because that's the nature
of a function/method: to have parameters.

## 4) "It's non-obvious in `Hook-embedded-style` what hook behavior should
be implied"

> ...So “arrays with hooks can do less” is already an established fact of
the language. However, if the hook-style syntax is used for visibility:

class A
{
    public array $arr { protected set; }
}

class B extends A
{
    public function add(int $val)
    {
        // Should this be legal?
        $this->arr[] = $val;
    }
}

$b = new B();

$b->add(5);

First of all: non-abstract property hook must have a body.
Second: when asymmetric visibility is explicit, it means that symmetric
visibility is implicit: a declared hook that does not have declared
visibility has the same general visibility as the property, in this case:
public.

Third:
There's another limitation on hooks here that makes things a bit
confusing: there's a missing hook for a specific operation because you can
clearly separate the `set` from the `push` operation...

Solution:

abstract class A
{
    abstract public array $arr {
        push; // Hook available only for "array" type properties only;
public visibility
        private set;
    }
}

class B extends A
{
    public array $arr {
        push ($value) { // `public push ...` here is redundant
            // Mandatory to implement logic here.
        }
        private set ($value) {
            // Mandatory to Implement logic here.
        }
    }

    public function __construct ()
    {
        // Legal ✅ (set hook is protected)
        $this->arr = [1]; // call set hook
    }
    public function add(int $value)
    {
        // Legal ✅ (push hook is public)
        $this->arr[] = $value; // call push hook
    }
}

$b = new B();

$b->add(5);
$b->arr; // Legal ✅ (Inherited from the general visibility that is public)
$b->arr = [1, 2, 3]; // Fatal error ❌ - access to set hook is private only
$b->arr[] = 4; // Legal ✅ - call public push hook

My point: `Prefix-style` is not future-proof
## 1) The `Prefix-style` is not compatible with new hooks
If more hooks are added in the future, such as the `push` hook for arrays
or even a hook that is compatible with all types such as `init`,
`Hook-embedded-style` will become compatible, but `Prefix-style` not.

## 2) Hook overloading
If hook overloading is added in the future, `Prefix-style` would not be
supported to have this granular visibility into operations. For example, it
might be possible to add a public hook and a protected hook for the same
operation, where one would be triggered if the operation occurred from
outside the class, and the other if the operation occurred from inside the
class.
Em 10 de jun. de 2024 13:15 -0300, Tiffany <tiffany.k.taylor@gmail.com>
escreveu:

On Wed, May 29, 2024, 2:16 PM Larry Garfield <larry@garfieldtech.com>
wrote:

As promised, Ilija and I offer this revised version of asymmetric
visibility.

PHP: rfc:asymmetric-visibility-v2

It's still essentially the same as last year's version, but with a few
adjustments and changes:

* readonly properties are now supported in a logical fashion.
* We've brought back the abbreviated form, as public-read, something else
set is the most common use case.
* The section on magic methods has been greatly simplified. The
implementation itself hasn't changed, but the explanation is a lot less
confusing now.
* We've explained how aviz interacts with hooks (they don't, really) and
with interface properties (in the obvious way), which didn't exist at the
time of the last draft.
* We've added a section with examples of how aviz is a concrete
improvement, even in a world with readonly and hooks.
* We've added a section discussing why the prefix-style syntax was chosen.

*dons flame retardant suit*

--
  Larry Garfield
  larry@garfieldtech.com

Sending an email to quickly enable a new mailing list subscriber to engage
in the conversation.

On 10 June 2024 21:17:38 BST, Lanre <lnearwaju@gmail.com> wrote:

You guys are killing PHP. There is a lot of work to be done on the engine
to modernize it and make it more robust and sturdy. Shit like this just
adds more complexity to PHP in the name of convenience. I think this is my
cue to explore other languages for handling requests

Please moderate your language.

Posts like this do nothing except a higher chance for people to ignore you, when you've actual points to make, such as credible suggestions as to how to make PHP "more rubust and sturdy".

Looking forwards to your RFCs and patches.

cheers
Derick

Why invest time in crafting yet another pull request or RFC when it’s glaringly obvious that you guys have no clue what you’re doing? First, there’s the questionable decision to implement JIT in 8.0, followed by the introduction of an entirely new library (IR) to a language that’s predominantly request-based, boasting nothing more than a rudimentary AST, plagued with inconsistencies, clinging to CGI, and practically useless without a framework. And now, after greenlighting a dubious property hooks RFC, the same individuals are embroiled in a debate over Asymmetric Visibility? If you can’t think up ways to make the engine “more rubust and sturdy” as a paid member of the foundation then maybe it is also time to start reconsidering my donations.

On Mon, Jun 10, 2024 at 3:42 PM Derick Rethans <derick@php.net> wrote:

On 10 June 2024 21:17:38 BST, Lanre <lnearwaju@gmail.com> wrote:

You guys are killing PHP. There is a lot of work to be done on the engine
to modernize it and make it more robust and sturdy. Shit like this just
adds more complexity to PHP in the name of convenience. I think this is my
cue to explore other languages for handling requests

Please moderate your language.

Posts like this do nothing except a higher chance for people to ignore you, when you’ve actual points to make, such as credible suggestions as to how to make PHP “more rubust and sturdy”.

Looking forwards to your RFCs and patches.

cheers
Derick

On Mon, Jun 10, 2024, at 19:51, Rodrigo Vieira wrote:

I didn't like the `Prefix-style` syntax. I prefer the `Hook-embedded-style` syntax. First let's look at the counterpoints of the `Prefix-style` syntax:

## 1) "`Prefix-style` is more visually scannable"
> The set visibility is 10 lines away from the get visibility!

Solution:

class HookEmbeddedStyle
{
    public string $phone {
        private set {
            $this->phone = implode('', array_filter(fn($c) => is_numeric($c), explode($value)))
        }
        get {
            if (!$this->phone) {
                return '';
            }
            if ($this->phone[0] === 1) {
                return 'US ' . $this->phone;
            }
            return 'Intl +' . $this->phone;
        }
    }
}

The set visibility is not 10 lines away from the get visibility if you put it at the top. For me this is about code convention, not about syntactic structure:
only put the hook with explicit visibility at the top, when some of the hooks do not have explicit visibility!

## 2) "`Prefix-style` is shorter"

public private(set) string $name;

public string $name { private set; }

Irrelevant to consider 1-2 characters.
If you break the lines needed for the hook block, the line (the horizontal size) is smaller when using `Hook-embedded-style` and in my opinion it is more readable because there is less information per line:

public private(set) string $name;

public string $name {
   private set;
}

## 3) "`Prefix-style` doesn't presume a connection with hooks"
> As noted above in "Interaction with hooks", visibility controls exist independently of hooks.

I agree, but with Property Hooks, this should now define the overall visibility only. Like a bigger gate that you open, and there are other doors defining the visibility of the operations get, set (hooks).

> In fact, as implemented, they don't interact at all. Using hook syntax for visibility controls is therefore surprising and confusing.

Why "surprising and confusing"? I see hooks as a different kind of function/method.
Property Hooks RFC shouldn't pass without requiring hook parentheses, for any hook when the property is not abstract. The `$value` in the `set` hook seems to come out of nowhere to some people (the exception is for hooks declared on an abstract property which you can define without parameters and thus you are free to define whatever parameters you want on the concrete property).

When you define a method in PHP, it should be possible to omit the "function", but the parameters should be required because that's the nature of a function/method: to have parameters.

## 4) "It's non-obvious in `Hook-embedded-style` what hook behavior should be implied"

> ...So “arrays with hooks can do less” is already an established fact of the language. However, if the hook-style syntax is used for visibility:

class A
{
    public array $arr { protected set; }
}

class B extends A
{
    public function add(int $val)
    {
        // Should this be legal?
        $this->arr[] = $val;
    }
}

$b = new B();

$b->add(5);

First of all: non-abstract property hook must have a body.
Second: when asymmetric visibility is explicit, it means that symmetric visibility is implicit: a declared hook that does not have declared visibility has the same general visibility as the property, in this case: public.

Third:
There's another limitation on hooks here that makes things a bit confusing: there's a missing hook for a specific operation because you can clearly separate the `set` from the `push` operation...

Solution:

abstract class A
{
    abstract public array $arr {
        push; // Hook available only for "array" type properties only; public visibility
        private set;
    }
}

class B extends A
{
    public array $arr {
        push ($value) { // `public push ...` here is redundant
            // Mandatory to implement logic here.
        }
        private set ($value) {
            // Mandatory to Implement logic here.
        }
    }

    public function __construct ()
    {
        // Legal ✅ (set hook is protected)
        $this->arr = [1]; // call set hook
    }
    public function add(int $value)
    {
        // Legal ✅ (push hook is public)
        $this->arr[] = $value; // call push hook
    }
}

$b = new B();

$b->add(5);
$b->arr; // Legal ✅ (Inherited from the general visibility that is public) 
$b->arr = [1, 2, 3]; // Fatal error ❌ - access to set hook is private only
$b->arr[] = 4; // Legal ✅ - call public push hook

My point: `Prefix-style` is not future-proof
## 1) The `Prefix-style` is not compatible with new hooks
If more hooks are added in the future, such as the `push` hook for arrays or even a hook that is compatible with all types such as `init`, `Hook-embedded-style` will become compatible, but `Prefix-style` not.

## 2) Hook overloading
If hook overloading is added in the future, `Prefix-style` would not be supported to have this granular visibility into operations. For example, it might be possible to add a public hook and a protected hook for the same operation, where one would be triggered if the operation occurred from outside the class, and the other if the operation occurred from inside the class.
Em 10 de jun. de 2024 13:15 -0300, Tiffany <tiffany.k.taylor@gmail.com> escreveu:

On Wed, May 29, 2024, 2:16 PM Larry Garfield <larry@garfieldtech.com> wrote:

As promised, Ilija and I offer this revised version of asymmetric visibility.

PHP: rfc:asymmetric-visibility-v2

It's still essentially the same as last year's version, but with a few adjustments and changes:

* readonly properties are now supported in a logical fashion.
* We've brought back the abbreviated form, as public-read, something else set is the most common use case.
* The section on magic methods has been greatly simplified. The implementation itself hasn't changed, but the explanation is a lot less confusing now.
* We've explained how aviz interacts with hooks (they don't, really) and with interface properties (in the obvious way), which didn't exist at the time of the last draft.
* We've added a section with examples of how aviz is a concrete improvement, even in a world with readonly and hooks.
* We've added a section discussing why the prefix-style syntax was chosen.

*dons flame retardant suit*

--
  Larry Garfield
  larry@garfieldtech.com

Sending an email to quickly enable a new mailing list subscriber to engage in the conversation.

I’m also not a fan of the prefix style, but for different reasons. My main reason is that it increases the minimum line-length, potentially forcing you to chop things down into awkward looking lines:

public
private(set)
string $longvarname {
get;
set;
}

I find that extremely hard to scan, but maybe others do not. The more natural looking syntax is easier to scan and reason about (IMHO):

public
string $longvarname {
get;
private set;
}

If I’m having to read the code, I prefer to have everything near where it is used so I don’t have to scroll up to the top and see its visibility. Granted, I haven’t used property hooks and I have no idea how IDEs will help here; maybe it is a non-issue — but I guess people still have to do code reviews which very rarely comes with IDE powers.

— Rob

On Tue, Jun 11, 2024 at 3:15 AM Lanre <lnearwaju@gmail.com> wrote:

Why invest time in crafting yet another pull request or RFC when it’s glaringly obvious that you guys have no clue what you’re doing? First, there’s the questionable decision to implement JIT in 8.0, followed by the introduction of an entirely new library (IR) to a language that’s predominantly request-based, boasting nothing more than a rudimentary AST, plagued with inconsistencies, clinging to CGI, and practically useless without a framework. And now, after greenlighting a dubious property hooks RFC, the same individuals are embroiled in a debate over Asymmetric Visibility? If you can’t think up ways to make the engine “more rubust and sturdy” as a paid member of the foundation then maybe it is also time to start reconsidering my donations.

On Mon, Jun 10, 2024 at 3:42 PM Derick Rethans <derick@php.net> wrote:

On 10 June 2024 21:17:38 BST, Lanre <lnearwaju@gmail.com> wrote:

You guys are killing PHP. There is a lot of work to be done on the engine
to modernize it and make it more robust and sturdy. Shit like this just
adds more complexity to PHP in the name of convenience. I think this is my
cue to explore other languages for handling requests

Please moderate your language.

Posts like this do nothing except a higher chance for people to ignore you, when you’ve actual points to make, such as credible suggestions as to how to make PHP “more rubust and sturdy”.

Looking forwards to your RFCs and patches.

cheers
Derick

Sometimes people grow in a different direction than a project and that’s okay, you should do what you feel is best for you. Even if that means you will no longer be enriching us with your presence, I think the PHP project will eventually recover from such great loss.

On Tue, Jun 11, 2024, at 15:36, Larry Garfield wrote:

On Tue, Jun 11, 2024, at 6:47 AM, Rob Landers wrote:

I’m also not a fan of the prefix style, but for different reasons. My

main reason is that it increases the minimum line-length, potentially

forcing you to chop things down into awkward looking lines:

public

private(set)

string $longvarname {

get;

set;

}

I find that extremely hard to scan, but maybe others do not. The more

natural looking syntax is easier to scan and reason about (IMHO):

public

string $longvarname {

get;

private set;

}

If I’m having to read the code, I prefer to have everything near where

it is used so I don’t have to scroll up to the top and see its

visibility. Granted, I haven’t used property hooks and I have no idea

how IDEs will help here; maybe it is a non-issue — but I guess people

still have to do code reviews which very rarely comes with IDE powers.

— Rob

I have never in my life seen someone split the visibility to a separate line from the type and variable name in PHP. I don’t know why anyone would start now, especially not because of hooks or aviz. I just checked and PER-CS very directly states “All keywords MUST be on a single line, and MUST be separated by a single space.” So splitting it like shown above would be against standard coding conventions anyway.

This is really a strawman argument.

–Larry Garfield

I’m willing to concede that it might be a straw man, though I did not intend it as such. I was being serious in my pointing out of it increasing the minimum line length and PER isn’t the only coding standard. It may result in some ugly code, as in my example.

— Rob

On Tue, Jun 11, 2024, at 6:47 AM, Rob Landers wrote:

I’m also not a fan of the prefix style, but for different reasons. My
main reason is that it increases the minimum line-length, potentially
forcing you to chop things down into awkward looking lines:

public
private(set)
string $longvarname {
get;
set;
}

I find that extremely hard to scan, but maybe others do not. The more
natural looking syntax is easier to scan and reason about (IMHO):

public
string $longvarname {
get;
private set;
}

If I’m having to read the code, I prefer to have everything near where
it is used so I don’t have to scroll up to the top and see its
visibility. Granted, I haven’t used property hooks and I have no idea
how IDEs will help here; maybe it is a non-issue — but I guess people
still have to do code reviews which very rarely comes with IDE powers.

— Rob

I have never in my life seen someone split the visibility to a separate line from the type and variable name in PHP. I don't know why anyone would start now, especially not because of hooks or aviz. I just checked and PER-CS very directly states "All keywords MUST be on a single line, and MUST be separated by a single space." So splitting it like shown above would be against standard coding conventions anyway.

This is really a strawman argument.

--Larry Garfield

On Tue, Jun 11, 2024, at 1:48 PM, Rob Landers wrote:

On Tue, Jun 11, 2024, at 15:36, Larry Garfield wrote:

On Tue, Jun 11, 2024, at 6:47 AM, Rob Landers wrote:

> I’m also not a fan of the prefix style, but for different reasons. My
> main reason is that it increases the minimum line-length, potentially
> forcing you to chop things down into awkward looking lines:
>
> public
> private(set)
> string $longvarname {
> get;
> set;
> }
>
> I find that extremely hard to scan, but maybe others do not. The more
> natural looking syntax is easier to scan and reason about (IMHO):
>
> public
> string $longvarname {
> get;
> private set;
> }
>
> If I’m having to read the code, I prefer to have everything near where
> it is used so I don’t have to scroll up to the top and see its
> visibility. Granted, I haven’t used property hooks and I have no idea
> how IDEs will help here; maybe it is a non-issue — but I guess people
> still have to do code reviews which very rarely comes with IDE powers.
>
> — Rob

I have never in my life seen someone split the visibility to a separate line from the type and variable name in PHP. I don't know why anyone would start now, especially not because of hooks or aviz. I just checked and PER-CS very directly states "All keywords MUST be on a single line, and MUST be separated by a single space." So splitting it like shown above would be against standard coding conventions anyway.

This is really a strawman argument.

--Larry Garfield

I’m willing to concede that it might be a straw man, though I did not
intend it as such. I was being serious in my pointing out of it
increasing the minimum line length and PER isn’t the only coding
standard. It may result in some ugly code, as in my example.

— Rob

I didn't think it was deliberate. But in practice:

public string $foo; // 13 chars, excluding variable.
public readonly string $foo; // 21 chars, excluding variable.
private(set) string $foo; // 18 chars, excluding variable.
protected(set) string $foo; // 19 chars, excluding variable.
public private(set) string $foo // 24 chars, excluding variable.
public protected(set) readonly string $foo; // 29 chars, excluding variable.

Even in the most pathologically long case, where you include every possible modifier, we're up to 29 characters. Typical coding guidelines say to use either 80 or 120 character lines. So even factoring in indentation, you're still going to need a 50+ character variable name before line length becomes an issue. At that point, the issue is your silly variable name, not the modifiers.

The far more common cases would be `private(set) string $foo` and `protected(set) string $foo`, both of which are... shorter than `public readonly string $foo`, which we already have.

In comparison, using hook-embedded style without hook implementations, we get:

public string $foo { private set; } // 29 characters excluding variable

So the base case with hook-embedded style is about 50% more characters than the prefix-style.

The only way that it wouldn't be is to structure it like so:

public string $foo {
  private set;
}

At which point I'd argue that's worse in every regard, especially with how many properties are now defined in constructor promotion. That would be vastly uglier in constructor promotion than the inline style, with either syntax.

To whatever extent we care about line length and conciseness, prefix-style wins over hook-embedded, unquestionably.

--Larry Garfield

Yeah i know im just one irrelevant person with crazy ideas (stable engine etc). I never said PHP needs me, I simply decided to no longer be a part of this shithole. I mean i brought up valid points about the engine, yet here you are with a retarded cheeky response when you could’ve just ignored me.

Cheers,
Lanre

On Tue, Jun 11, 2024 at 1:44 AM Lynn <kjarli@gmail.com> wrote:

On Tue, Jun 11, 2024 at 3:15 AM Lanre <lnearwaju@gmail.com> wrote:

Why invest time in crafting yet another pull request or RFC when it’s glaringly obvious that you guys have no clue what you’re doing? First, there’s the questionable decision to implement JIT in 8.0, followed by the introduction of an entirely new library (IR) to a language that’s predominantly request-based, boasting nothing more than a rudimentary AST, plagued with inconsistencies, clinging to CGI, and practically useless without a framework. And now, after greenlighting a dubious property hooks RFC, the same individuals are embroiled in a debate over Asymmetric Visibility? If you can’t think up ways to make the engine “more rubust and sturdy” as a paid member of the foundation then maybe it is also time to start reconsidering my donations.

On Mon, Jun 10, 2024 at 3:42 PM Derick Rethans <derick@php.net> wrote:

On 10 June 2024 21:17:38 BST, Lanre <lnearwaju@gmail.com> wrote:

You guys are killing PHP. There is a lot of work to be done on the engine
to modernize it and make it more robust and sturdy. Shit like this just
adds more complexity to PHP in the name of convenience. I think this is my
cue to explore other languages for handling requests

Please moderate your language.

Posts like this do nothing except a higher chance for people to ignore you, when you’ve actual points to make, such as credible suggestions as to how to make PHP “more rubust and sturdy”.

Looking forwards to your RFCs and patches.

cheers
Derick

Sometimes people grow in a different direction than a project and that’s okay, you should do what you feel is best for you. Even if that means you will no longer be enriching us with your presence, I think the PHP project will eventually recover from such great loss.