[RFC] Asymmetric visibility

php.internals

Larry Garfield

4 years ago
Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: Asymmetric Visibility. https://wiki.php.net/rfc/asymmetric-visibility Details are in the RFC, but it's largely a copy of Swift's support for the same.
-- Larry Garfield larry@garfieldtech.com

Thomas Nunninger

4 years ago
Hi, Am 05.08.22 um 19:08 schrieb Larry Garfield:
> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the same.
Great to hear that there is a new and simple proposals. One note: In the example about references, I assume the class Baz must extend from Foo. Good luck with this proposal Thomas

Larry Garfield

4 years ago
On Fri, Aug 5, 2022, at 12:39 PM, Thomas Nunninger wrote:
> Hi, > Am 05.08.22 um 19:08 schrieb Larry Garfield: >> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: Asymmetric Visibility. >> >> https://wiki.php.net/rfc/asymmetric-visibility >> >> Details are in the RFC, but it's largely a copy of Swift's support for the same. > > Great to hear that there is a new and simple proposals. > > One note: In the example about references, I assume the class Baz must > extend from Foo.
Yep, thanks, that's a typo on my part. Fixed now. --Larry Garfield

Matthew Weier O'Phinney

4 years ago
On Fri, Aug 5, 2022, 12:09 PM Larry Garfield <larry@garfieldtech.com> wrote:
> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: > Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the > same. >
I have two comments: - For reflection purposes, having two separate methods feels like it will be cumbersome; you'd need to check both to determine if you'd need to make the reflection property accessible before changing the value. An isPublicSet() would alleviate that, or potentially a getSetFlags() method against which you could apply a bit mask. You'd need to add a constant for public set I both cases. - The number of items that appear to the left of a property is growing, making understanding a declaration increasingly difficult, and your discussion of future scope indicates that this is likely to get worse. I'm wondering if this sort of behavior could be indicated via attributes instead? Something like `#[PropertySetBehavior(PROPERTY_SET_PRIVATE)]`. Attributes have the benefit of being separate from the property declaration, arguably more readable (one per line), and composable. This might play into some of the future scope items as well. Overall, though, love the design simplicity!

Larry Garfield

4 years ago
On Fri, Aug 5, 2022, at 1:08 PM, Matthew Weier O'Phinney wrote:
> On Fri, Aug 5, 2022, 12:09 PM Larry Garfield <larry@garfieldtech.com> wrote: > >> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: >> Asymmetric Visibility. >> >> https://wiki.php.net/rfc/asymmetric-visibility >> >> Details are in the RFC, but it's largely a copy of Swift's support for the >> same. >> > > I have two comments: > > - For reflection purposes, having two separate methods feels like it will > be cumbersome; you'd need to check both to determine if you'd need to make > the reflection property accessible before changing the value. An > isPublicSet() would alleviate that, or potentially a getSetFlags() method > against which you could apply a bit mask. You'd need to add a constant for > public set I both cases.
Reflection is a mess, unfortunately. :-( There's already separate isPrivate(), isProtected(), and isPublic() methods on ReflectionProperty, so this is consistent. Also, there's getModifiers() which returns a bitmask; these new flags would be included there. All these methods all do is check what is syntactically present, both the current ones and proposed ones. There's definitely room in the Reflection API for a "couldItBeModifiedFromScope($scope)" type method (and similar) that aggregates together symmetric visibility, readonly, asymmetric visibility, etc. into nice booleans. However, that is notably more work than just adding more syntax flag methods. It would probably be better as its own RFC, though I'd support that. If there's consensus that it has to be done here that could be done, but we'd prefer not as it feels like scope creep.
> - The number of items that appear to the left of a property is growing, > making understanding a declaration increasingly difficult, and your > discussion of future scope indicates that this is likely to get worse. I'm > wondering if this sort of behavior could be indicated via attributes > instead? Something like `#[PropertySetBehavior(PROPERTY_SET_PRIVATE)]`. > Attributes have the benefit of being separate from the property > declaration, arguably more readable (one per line), and composable. This > might play into some of the future scope items as well.
The list indeed groweth, but there's a couple of reasons it wouldn't be appropriate here to use an attribute. * It would mean get-visibility is a keyword and set-visibility is an attribute. That's weird and inconsistent. * There's a strong sense from many on the list that attributes should not be used for language behavior. Ilija agrees with that position, and I don't care enough to fight it one way or the other. * An attribute would be ignored in older PHP versions. For most attributes that's OK, but in this case it would be a potential bug source. Specifically, it would mean `#[PrivateSet] \n public string $value` would be publicly settable on PHP 8.2, but not publicly settable in 8.3. That means the integrity of the object is compromised on older versions. A keyword would make that a syntax error, which in this case is preferable.
> Overall, though, love the design simplicity!
Yay! --Larry Garfield

Robert Landers

4 years ago
On Fri, Aug 5, 2022 at 10:25 PM Larry Garfield <larry@garfieldtech.com> wrote:
> > On Fri, Aug 5, 2022, at 1:08 PM, Matthew Weier O'Phinney wrote: > > On Fri, Aug 5, 2022, 12:09 PM Larry Garfield <larry@garfieldtech.com> wrote: > > > >> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: > >> Asymmetric Visibility. > >> > >> https://wiki.php.net/rfc/asymmetric-visibility > >> > >> Details are in the RFC, but it's largely a copy of Swift's support for the > >> same. > >> > > > > I have two comments: > > > > - For reflection purposes, having two separate methods feels like it will > > be cumbersome; you'd need to check both to determine if you'd need to make > > the reflection property accessible before changing the value. An > > isPublicSet() would alleviate that, or potentially a getSetFlags() method > > against which you could apply a bit mask. You'd need to add a constant for > > public set I both cases. > > Reflection is a mess, unfortunately. :-( > > There's already separate isPrivate(), isProtected(), and isPublic() methods on ReflectionProperty, so this is consistent. Also, there's getModifiers() which returns a bitmask; these new flags would be included there. All these methods all do is check what is syntactically present, both the current ones and proposed ones. > > There's definitely room in the Reflection API for a "couldItBeModifiedFromScope($scope)" type method (and similar) that aggregates together symmetric visibility, readonly, asymmetric visibility, etc. into nice booleans. However, that is notably more work than just adding more syntax flag methods. It would probably be better as its own RFC, though I'd support that. > > If there's consensus that it has to be done here that could be done, but we'd prefer not as it feels like scope creep. > > > - The number of items that appear to the left of a property is growing, > > making understanding a declaration increasingly difficult, and your > > discussion of future scope indicates that this is likely to get worse. I'm > > wondering if this sort of behavior could be indicated via attributes > > instead? Something like `#[PropertySetBehavior(PROPERTY_SET_PRIVATE)]`. > > Attributes have the benefit of being separate from the property > > declaration, arguably more readable (one per line), and composable. This > > might play into some of the future scope items as well. > > The list indeed groweth, but there's a couple of reasons it wouldn't be appropriate here to use an attribute. > > * It would mean get-visibility is a keyword and set-visibility is an attribute. That's weird and inconsistent. > * There's a strong sense from many on the list that attributes should not be used for language behavior. Ilija agrees with that position, and I don't care enough to fight it one way or the other. > * An attribute would be ignored in older PHP versions. For most attributes that's OK, but in this case it would be a potential bug source. Specifically, it would mean `#[PrivateSet] \n public string $value` would be publicly settable on PHP 8.2, but not publicly settable in 8.3. That means the integrity of the object is compromised on older versions. A keyword would make that a syntax error, which in this case is preferable. > > > Overall, though, love the design simplicity! > > Yay! > > --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >
Hello, First of all, this is exciting! Looking at the behavior for references, can we explore the edges a bit? (I didn't see any tests that cover this functionality in the PR) 1. Passing a function a reference to a private-set property is allowed, such as to sort(). 2. Returning a reference to a private-set property is allowed and usable. 3. Trying to get a NEW reference to a private-set property outside of the defined scope is not allowed. So if I'm understanding correctly, references to private-set properties can be used outside of the defined scope but they can't be created outside of the defined scope? If so, should we fire off a Notice when references are modified outside of the defined scope? I can see that being annoying in certain contexts, such as sorting an array, so maybe not? FWIW, this behavior doesn't apply to readonly properties because attempting to pass a reference to the property fails.

Michał Brzuchalski

4 years ago
Hi Larry, pt., 5 sie 2022, 19:09 użytkownik Larry Garfield <larry@garfieldtech.com> napisał:
> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: > Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the > same. >
Thanks for taking efforts on new RFC. Why not C# style property accessors? It feels more natural for me while Swift solution IMHO looks weird/odd composed with PHP class definition syntax. The arguments in favor from the RFC look forcibly (couldn't find better word). Cheers, Michał Marcin Brzuchalski

Larry Garfield

4 years ago
On Fri, Aug 5, 2022, at 2:27 PM, Michał Marcin Brzuchalski wrote:
> Hi Larry, > > pt., 5 sie 2022, 19:09 użytkownik Larry Garfield <larry@garfieldtech.com> > napisał: > >> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: >> Asymmetric Visibility. >> >> https://wiki.php.net/rfc/asymmetric-visibility >> >> Details are in the RFC, but it's largely a copy of Swift's support for the >> same. >> > > Thanks for taking efforts on new RFC. > > Why not C# style property accessors? It feels more natural for me while > Swift solution IMHO looks weird/odd composed with PHP class definition > syntax. The arguments in favor from the RFC look forcibly (couldn't find > better word). > > Cheers, > Michał Marcin Brzuchalski
There's a couple of reasons. Ilija and I have spent quite a bit of time brainstorming through accessors, and there's still a lot to finalize there, and lots of bikeshed potential. It's a non-simple problem space. However, asymmetric visibility is a good stand-alone concept that can work without accessors, and has ample benefits of its own without accessors, and has far less bikeshed potential. (There's minor debates that might be had over syntax, but the behavior is fairly "it is or it isn't".) So the primary reason is that this syntax allows us to have asymmetric visibility without having to think about accessors. We can then, at some point in the future, have a different discussion about accessors and not have to worry about asymmetric visibility in the process. The C# syntax by nature only works if they come together in the same RFC, or at least one RFC is built such that it locks in some decisions for the other RFC. As an example, one thing we've been discussing is if accessors should be inline with the property (a la Swift and C#) or annotated methods (as in Javascript and Python). There's good arguments for both approaches, as well as good arguments against. But those would have *extremely* different implications for how we represent asymmetric visibility. So if we took the C# style for asymmetric visibility, that would realistically lock us into C# style accessors even before we've had the discussion about if we want that or not. That's not good. (Please don't anyone try to debate which accessor approach to take here; the whole point is we want to introduce asymmetric visibility without having that debate yet. We'll be able to have it in the future, I'm sure. There's other knock-on effects as well that I'm skipping for now as not relevant.) Keeping them separate also makes extending visibility rules in different directions (as noted in the Future Scope section) at some point in the future can also be done without bumping into accessors. Whether or not those directions are useful is a topic for another time, but it again allows us to discuss those (like a "once" operation or a "package" scope, etc.) without worrying about the impact on accessors. Secondarily, the C# style means you have to look both before and after the property itself to know what its visibility rules are. That's increased cognitive load. This way, all of the various "flags" on a property remain in the same place, and presumably 99.999% of developers will put `public private(set)` right next to each other like that. That makes the visibility readable all from one spot, and if all you're using is that and not accessors in the future, the impact on your existing reading skills is minimized. This one is admittedly a bit more subjective, but readability and cognitive load are certainly considerations to, er, consider. --Larry Garfield

Paul Dragoonis

4 years ago
On Fri, 5 Aug 2022, 20:53 Larry Garfield, <larry@garfieldtech.com> wrote:
> On Fri, Aug 5, 2022, at 2:27 PM, Michał Marcin Brzuchalski wrote: > > Hi Larry, > > > > pt., 5 sie 2022, 19:09 użytkownik Larry Garfield <larry@garfieldtech.com > > > > napisał: > > > >> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: > >> Asymmetric Visibility. > >> > >> https://wiki.php.net/rfc/asymmetric-visibility > >> > >> Details are in the RFC, but it's largely a copy of Swift's support for > the > >> same. > >> > > > > Thanks for taking efforts on new RFC. > > > > Why not C# style property accessors? It feels more natural for me while > > Swift solution IMHO looks weird/odd composed with PHP class definition > > syntax. The arguments in favor from the RFC look forcibly (couldn't find > > better word). > > > > Cheers, > > Michał Marcin Brzuchalski > > There's a couple of reasons. Ilija and I have spent quite a bit of time > brainstorming through accessors, and there's still a lot to finalize there, > and lots of bikeshed potential. It's a non-simple problem space. However, > asymmetric visibility is a good stand-alone concept that can work without > accessors, and has ample benefits of its own without accessors, and has far > less bikeshed potential. (There's minor debates that might be had over > syntax, but the behavior is fairly "it is or it isn't".) > > So the primary reason is that this syntax allows us to have asymmetric > visibility without having to think about accessors. We can then, at some > point in the future, have a different discussion about accessors and not > have to worry about asymmetric visibility in the process. The C# syntax by > nature only works if they come together in the same RFC, or at least one > RFC is built such that it locks in some decisions for the other RFC. > > As an example, one thing we've been discussing is if accessors should be > inline with the property (a la Swift and C#) or annotated methods (as in > Javascript and Python). There's good arguments for both approaches, as > well as good arguments against. But those would have *extremely* different > implications for how we represent asymmetric visibility. So if we took the > C# style for asymmetric visibility, that would realistically lock us into > C# style accessors even before we've had the discussion about if we want > that or not. That's not good. > > (Please don't anyone try to debate which accessor approach to take here; > the whole point is we want to introduce asymmetric visibility without > having that debate yet. We'll be able to have it in the future, I'm sure. > There's other knock-on effects as well that I'm skipping for now as not > relevant.) > > Keeping them separate also makes extending visibility rules in different > directions (as noted in the Future Scope section) at some point in the > future can also be done without bumping into accessors. Whether or not > those directions are useful is a topic for another time, but it again > allows us to discuss those (like a "once" operation or a "package" scope, > etc.) without worrying about the impact on accessors. > > Secondarily, the C# style means you have to look both before and after the > property itself to know what its visibility rules are. That's increased > cognitive load. This way, all of the various "flags" on a property remain > in the same place, and presumably 99.999% of developers will put `public > private(set)` right next to each other like that. That makes the > visibility readable all from one spot, and if all you're using is that and > not accessors in the future, the impact on your existing reading skills is > minimized. This one is admittedly a bit more subjective, but readability > and cognitive load are certainly considerations to, er, consider. >
Feedback: 1. The idea is cool 2. when I opened the RFC and looked at the syntax, I wasnt able to intuitively (self documenting code) figure out what and why was going on. 3. The syntax is heavily implicit as to what each keyword and syntax means, so the general PHP developer would struggle or mis understand what is going on. 4. MWOP's point of there being a lot of keywords and prefixes at class property declaration is not really cluttered (lots going on), is accurate. Perhaps splitting it into an explicit Attribute might yield a better DX? Ilina and Larry, all in all I know this is a WIP and you're looking for early feedback, so this is my feedback, to be considered. Looking forward to reviewing future revisions of this. Thanks for the effort and time you've put in so far <3

Rowan Collins

4 years ago
On 5 August 2022 21:04:34 BST, Paul Dragoonis <dragoonis@gmail.com> wrote:
>2. when I opened the RFC and looked at the syntax, I wasnt able to >intuitively (self documenting code) figure out what and why was going on. > >3. The syntax is heavily implicit as to what each keyword and syntax means, >so the general PHP developer would struggle or mis understand what is going >on.
Can you expand on where you think the ambiguity / implicitness is? As I understand it, the RFC is proposing exactly three new combined access levels: - "public private(set)" - "public protected(set)" - "protected private(set)" Although aesthetically it will take a bit of getting used to, it seems to me pretty clear that the first means "mostly public, but private if you want to set it", and so on. The only thing I can think of that could be described as "implicit" is that accessing a property by reference is considered a "set" operation, which I'm not sure how any implementation could avoid. Regards,
-- Rowan Tommins [IMSoP]

Lynn

4 years ago
On Sun, Aug 7, 2022 at 12:34 PM Rowan Tommins <rowan.collins@gmail.com> wrote:
> Can you expand on where you think the ambiguity / implicitness is? As I > understand it, the RFC is proposing exactly three new combined access > levels: > > - "public private(set)" > - "public protected(set)" > - "protected private(set)" > > Although aesthetically it will take a bit of getting used to, it seems to > me pretty clear that the first means "mostly public, but private if you > want to set it", and so on. > > The only thing I can think of that could be described as "implicit" is > that accessing a property by reference is considered a "set" operation, > which I'm not sure how any implementation could avoid.
Personally for me it's the syntax. Reading "public private", "public protected", or "protected private" reads really weird `public private(set) static self $property`. In the end it makes sense if you know what it means, otherwise it's probably confusing. I really like this RFC and I feel like this might just be the way to go forward, but I have my doubts about how many more keywords can be realistically added before it becomes a problem.

Larry Garfield

4 years ago
On Sun, Aug 7, 2022, at 5:54 AM, Lynn wrote:
> On Sun, Aug 7, 2022 at 12:34 PM Rowan Tommins <rowan.collins@gmail.com> > wrote: > >> Can you expand on where you think the ambiguity / implicitness is? As I >> understand it, the RFC is proposing exactly three new combined access >> levels: >> >> - "public private(set)" >> - "public protected(set)" >> - "protected private(set)" >> >> Although aesthetically it will take a bit of getting used to, it seems to >> me pretty clear that the first means "mostly public, but private if you >> want to set it", and so on. >> >> The only thing I can think of that could be described as "implicit" is >> that accessing a property by reference is considered a "set" operation, >> which I'm not sure how any implementation could avoid. > > > Personally for me it's the syntax. Reading "public private", "public > protected", or "protected private" reads really weird `public private(set) > static self $property`. In the end it makes sense if you know what it > means, otherwise it's probably confusing. I really like this RFC and I feel > like this might just be the way to go forward, but I have my doubts about > how many more keywords can be realistically added before it becomes a > problem.
What syntax would avoid having the visibility keyword repeated? if you want "public get, private set" behavior, I don't know how that could be done without having the words "public" and "private" both appear somewhere. Also note, since using private(set) is mutually exclusive with readonly, that the resulting keyword list is about the same length as the existing "public readonly string $foo" that has cropped up frequently with PHP 8.1. So... yes it's another keyword, but it's not more keywords that can exist simultaneously. And moving the keyword to the right (a la C#) wouldn't make it any shorter; if anything it would be even more keystrokes. Very close in size, same in complexity: public readonly string $foo public private(set) string $foo --Larry Garfield

Andreas Heigl

4 years ago
Hey Larry, hey all On 07.08.22 21:01, Larry Garfield wrote:
> On Sun, Aug 7, 2022, at 5:54 AM, Lynn wrote: >> On Sun, Aug 7, 2022 at 12:34 PM Rowan Tommins <rowan.collins@gmail.com> >> wrote: >> >>> Can you expand on where you think the ambiguity / implicitness is? As I >>> understand it, the RFC is proposing exactly three new combined access >>> levels: >>> >>> - "public private(set)" >>> - "public protected(set)" >>> - "protected private(set)" >>> >>> Although aesthetically it will take a bit of getting used to, it seems to >>> me pretty clear that the first means "mostly public, but private if you >>> want to set it", and so on. >>> >>> The only thing I can think of that could be described as "implicit" is >>> that accessing a property by reference is considered a "set" operation, >>> which I'm not sure how any implementation could avoid. >> >> >> Personally for me it's the syntax. Reading "public private", "public >> protected", or "protected private" reads really weird `public private(set) >> static self $property`. In the end it makes sense if you know what it >> means, otherwise it's probably confusing. I really like this RFC and I feel >> like this might just be the way to go forward, but I have my doubts about >> how many more keywords can be realistically added before it becomes a >> problem. > > What syntax would avoid having the visibility keyword repeated? if you want "public get, private set" behavior, I don't know how that could be done without having the words "public" and "private" both appear somewhere. > > Also note, since using private(set) is mutually exclusive with readonly, that the resulting keyword list is about the same length as the existing "public readonly string $foo" that has cropped up frequently with PHP 8.1. So... yes it's another keyword, but it's not more keywords that can exist simultaneously. And moving the keyword to the right (a la C#) wouldn't make it any shorter; if anything it would be even more keystrokes. > > Very close in size, same in complexity: > > public readonly string $foo > public private(set) string $foo
In my opinion those are actually two different things. One is allowing a variable to be publicly readably and writeable. But it can only be set once. Every subsequent call to setting a new value will fail. The other is allowing different scopes for reading and writing the variable. The variable can be read publicly but can only be set from within the current class-context. But setting it from the current class context can happen multiple times. So it should indeed be possible to combine both possibilities: public(get) private(set) readonly string $foo; My 0.02€ Cheers Andreas
-- ,,, (o o) +---------------------------------------------------------ooO-(_)-Ooo-+ | Andreas Heigl | | mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" | | https://andreas.heigl.org | +---------------------------------------------------------------------+ | https://hei.gl/appointmentwithandreas | +---------------------------------------------------------------------+

Casper Langemeijer

4 years ago
Hi all, In the discussion I sometimes see the terminology 'readonly' and 'writable' being used. This is confusing because when the property is an object that itself is mutable, there is nothing read-only about it. The terminology in the RFC seems right to me, and overall it seems solid. However, I'm not convinced this RFC is solving a real issue. I could not find any reasoning in the RFC, except that Swift has a very similar language feature. Grtz, Casper

Andreas Heigl

4 years ago
Hey Casper. On 08.08.22 09:54, Casper Langemeijer wrote:
> Hi all, > > In the discussion I sometimes see the terminology 'readonly' and 'writable' being used. This is confusing because when the property is an object that itself is mutable, there is nothing read-only about it. > > The terminology in the RFC seems right to me, and overall it seems solid. > > However, I'm not convinced this RFC is solving a real issue. I could not find any reasoning in the RFC, except that Swift has a very similar language feature. >
To me it solves the topic of making a property readable but not writeable from the public while still allowing it to be written to within the private or protected context. So enforcing usage of a public setter-metbhod but not having to use a getter. Soemthing like this final class Foo { public private(set) string $username; public function changeUsernameTo(string $newUsername): self { if (! newUsernameIsUnique($newUsername) { throw new RuntimeException('The username is not unique'); } $this->username = $newUsername; return $this; } } readonly only solves that for immutable properties but there currently is no way of solving that for mutable properties. Just my 0.02€ Cheers Andreas
-- ,,, (o o) +---------------------------------------------------------ooO-(_)-Ooo-+ | Andreas Heigl | | mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" | | https://andreas.heigl.org | +---------------------------------------------------------------------+ | https://hei.gl/appointmentwithandreas | +---------------------------------------------------------------------+

Michał Brzuchalski

4 years ago
Hi Larry, niedz., 7 sie 2022 o 21:02 Larry Garfield <larry@garfieldtech.com> napisał(a):
> On Sun, Aug 7, 2022, at 5:54 AM, Lynn wrote: > > On Sun, Aug 7, 2022 at 12:34 PM Rowan Tommins <rowan.collins@gmail.com> > > wrote: > > > >> Can you expand on where you think the ambiguity / implicitness is? As I > >> understand it, the RFC is proposing exactly three new combined access > >> levels: > >> > >> - "public private(set)" > >> - "public protected(set)" > >> - "protected private(set)" > >> > >> Although aesthetically it will take a bit of getting used to, it seems > to > >> me pretty clear that the first means "mostly public, but private if you > >> want to set it", and so on. > >> > >> The only thing I can think of that could be described as "implicit" is > >> that accessing a property by reference is considered a "set" operation, > >> which I'm not sure how any implementation could avoid. > > > > > > Personally for me it's the syntax. Reading "public private", "public > > protected", or "protected private" reads really weird `public > private(set) > > static self $property`. In the end it makes sense if you know what it > > means, otherwise it's probably confusing. I really like this RFC and I > feel > > like this might just be the way to go forward, but I have my doubts about > > how many more keywords can be realistically added before it becomes a > > problem. > > What syntax would avoid having the visibility keyword repeated? if you > want "public get, private set" behavior, I don't know how that could be > done without having the words "public" and "private" both appear somewhere. > > Also note, since using private(set) is mutually exclusive with readonly, > that the resulting keyword list is about the same length as the existing > "public readonly string $foo" that has cropped up frequently with PHP 8.1. > So... yes it's another keyword, but it's not more keywords that can exist > simultaneously. And moving the keyword to the right (a la C#) wouldn't > make it any shorter; if anything it would be even more keystrokes. > > Very close in size, same in complexity: > > public readonly string $foo > public private(set) string $foo >
Since the radio request show already started wanted to push my 0.50€ Instead of thinking how to improve visibility on the left side, let's move all the visibility modifiers to the right but enclosed with parentheses, this way a future stays open for improvements and introduction of accessors. // instead of class Foo { public private(set) static int|string $id; } // let's do this class Bar { static int|string $id { public; private set; }; } A future improvement may add an accessor body on the right side of "set". Cheers :D, Michał Marcin Brzuchalski

Rowan Collins

4 years ago
On 07/08/2022 11:54, Lynn wrote:
> Reading "public private", "public protected", or "protected private" > reads really weird `public private(set) static self $property`.
Interesting, it seems that you've unconsciously broken it up as "public private" followed by "(set)", rather than "public" followed by "private(set)". Perhaps it's because of the position of the parentheses, which do feel awkward to me at first glance. Would it read more naturally to you with different punctuation? public (private set) static self $property; Or: (public; private set) static self $property; Or: public private-set static self $property; On 05/08/2022 19:08, Matthew Weier O'Phinney wrote:
> I'm wondering if this sort of behavior could be indicated via attributes > instead? Something like `#[PropertySetBehavior(PROPERTY_SET_PRIVATE)]`. > Attributes have the benefit of being separate from the property > declaration, arguably more readable (one per line), and composable.
Attributes are no more intrinsically "separate" or "one per line" than keywords; the following would be perfectly valid with the RFC's proposed syntax: class Foo {    public private(set)        static int|string $id; } And the following would be valid with an attribute like you suggest: class Foo {    #[PropertySetBehavior(PROPERTY_SET_PRIVATE)] public static int|string $id; } Even a shorter attribute name would have the extra punctuation, and the restriction that it comes before the normal visibility keyword: class Foo {    #[PrivateSet] public static int|string $id; } Regards,
-- Rowan Tommins [IMSoP]

Ollie Read

4 years ago
On Sun, Aug 7, 2022, at 9:38 PM, Rowan Tommins wrote:
> On 07/08/2022 11:54, Lynn wrote: > > Reading "public private", "public protected", or "protected private" > > reads really weird `public private(set) static self $property`. > > > Interesting, it seems that you've unconsciously broken it up as "public > private" followed by "(set)", rather than "public" followed by > "private(set)". Perhaps it's because of the position of the parentheses, > which do feel awkward to me at first glance. Would it read more > naturally to you with different punctuation? > > public (private set) static self $property; > > Or: > > (public; private set) static self $property; > > Or: > > public private-set static self $property; > > > > On 05/08/2022 19:08, Matthew Weier O'Phinney wrote: > > I'm wondering if this sort of behavior could be indicated via attributes > > instead? Something like `#[PropertySetBehavior(PROPERTY_SET_PRIVATE)]`. > > Attributes have the benefit of being separate from the property > > declaration, arguably more readable (one per line), and composable. > > > Attributes are no more intrinsically "separate" or "one per line" than > keywords; the following would be perfectly valid with the RFC's proposed > syntax: > > class Foo { > public private(set) > static int|string $id; > } > > And the following would be valid with an attribute like you suggest: > > class Foo { > #[PropertySetBehavior(PROPERTY_SET_PRIVATE)] public static > int|string $id; > } > > Even a shorter attribute name would have the extra punctuation, and the > restriction that it comes before the normal visibility keyword: > > class Foo { > #[PrivateSet] public static int|string $id; > } > > > Regards, > > -- > Rowan Tommins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
Just to throw in my ten cents: I'm not sure if this is because I was recently reading about the fact that PHP doesn't have proper support for a comma operator, but I quite like the idea of providing them as a comma-separated list. // Public for get and set public int $number // Public get, private set public, private int $number If a single visibility is provided, same old same old, but if two are provided, they are in the order of get, followed by set. While not being as verbose as providing (set) or other markings to differentiate them, it feels more natural, and it doesn't overcomplicate it. --- Best Regards, *Ollie Read*

Larry Garfield

4 years ago
On Sun, Aug 7, 2022, at 3:46 PM, Ollie Read wrote:
> Just to throw in my ten cents: > > I'm not sure if this is because I was recently reading about the fact > that PHP doesn't have proper support for a comma operator, but I quite > like the idea of providing them as a comma-separated list. > > // Public for get and set > public int $number > > // Public get, private set > public, private int $number > > If a single visibility is provided, same old same old, but if two are > provided, they are in the order of get, followed by set. While not > being as verbose as providing (set) or other markings to differentiate > them, it feels more natural, and it doesn't overcomplicate it. > > --- > Best Regards, > *Ollie Read*
I'm not wild about implicit ordering as a syntax, for two main reasons: 1) It requires implicit knowledge on the part of the reader what's going on. Any kind of explicit syntax makes it clear "public, except for this operation". ("this operation" for now being just set, but then the set is right there and self-documenting.) 2) It makes extending it to other operations like "once" or "init" in the future impossible. (See Future Scope.) If we went with public, private string $foo, which implicitly meant "public(get), private(set)", and then in the future we add a "once" operation to mimic the behavior of readonly... what would that look like? With (set), it's self-evident how to extend the syntax. With it being fully implicit, I cannot think of a way to do so. Something like public private:set (colon instead of parens) would work just as well, if the () are confusing somehow, but that doesn't feel like a common problem. And it would lose the parallelism with Swift. --Larry Garfield

Rowan Collins

4 years ago
On 07/08/2022 22:48, Larry Garfield wrote:
> Something like public private:set (colon instead of parens) would work just as well, if the () are confusing somehow, but that doesn't feel like a common problem. And it would lose the parallelism with Swift.
I wonder if the reasoning for Swift's syntax choice is publicly available, or if there's anyone with inside knowledge we could ask. It would be good to know if there's a compelling argument the RFC could mention, or if it was actually chosen for reasons that don't apply to PHP. Regards,
-- Rowan Tommins [IMSoP]

Lynn

4 years ago
On Sun, Aug 7, 2022 at 10:38 PM Rowan Tommins <rowan.collins@gmail.com> wrote:
> On 07/08/2022 11:54, Lynn wrote: > > Reading "public private", "public protected", or "protected private" > > reads really weird `public private(set) static self $property`. > > > Interesting, it seems that you've unconsciously broken it up as "public > private" followed by "(set)", rather than "public" followed by > "private(set)". Perhaps it's because of the position of the parentheses, > which do feel awkward to me at first glance. Would it read more > naturally to you with different punctuation? > > public (private set) static self $property; > > Or: > > (public; private set) static self $property; > > Or: > > public private-set static self $property; >
The more I think about it, the more I agree with Marco's point of view on this. I don't think there's a way to add this functionality without it looking confusing one way or another. Once I get to use php8+, I'll probably end up using readonly for most code. I think the feature itself is nice for the scenario where you want to have private or protected mutability, but even this is a scenario I would avoid myself. I'm afraid that based on the future possibilities `package(set)` will make it even more confusing when allowing external modifications by specific classes, which would make it even more complicated to find out who or what may or may not modify a property from a reader perspective (like code reviews).

Claude Pache

4 years ago
> Le 5 août 2022 à 19:08, Larry Garfield <larry@garfieldtech.com> a écrit : > > Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the same. > > -- > Larry Garfield > larry@garfieldtech.com >
Hi, I like the RFC, and in particular allowing property visibility to remain orthogonal to property accessors. There are few missing details in the RFC (which is otherwise good): 1. Are static properties supported? 2. What about properties that are overridden/redeclared in a subclass? Given that extending the visibility is generally allowed: ```php class A { protected int $x; } class B extends A { public int $x; // ok } ``` I expect that the following should be allowed as well: ```php class A { public protected(set) int $x; } class B extends A { public int $x; // ok } ``` On the other hand, I expect that the two following cases should *not* be allowed, because it would break encapsulation of class A: ```php class A { public private(set) int $x; } class B extends A { public int $x; // error: cannot change a private property to public (or protected) } ``` and: ```php class A { public private(set) int $x; } class B extends A { public private(set) int $x; // error: cannot make a private property in A as private in B } ``` —Claude

Alexandru Pătrănescu

4 years ago
On Fri, Aug 5, 2022 at 8:09 PM Larry Garfield <larry@garfieldtech.com> wrote:
> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: > Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the > same. > > -- > Larry Garfield > larry@garfieldtech.com > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
Hi Larry, Ilija, Review the proposal and it looks great. I have a few subjects to point and maybe discuss further if you think so: 1. The first subject is visible even from the first phrase of the RFC that uses both the terms "get and set operations" and "read and write operations". I'm wondering if you thought about using protected(write) or private(write). 2. The term "asymmetric visibility" is not that accurate and I believe "asymmetric access" or "asymmetric accessibility" would fit better. As noted in the relation with __set(), a public private(set) property that is attempted to be written from global context will not act like it's not visible but that it's not accessible for writing. 3. I assume the target is both static and non-static properties. This should be mentioned in the RFC. Regards, Alex

Larry Garfield

4 years ago
On Sun, Aug 7, 2022, at 1:59 PM, Alexandru Pătrănescu wrote:
> On Fri, Aug 5, 2022 at 8:09 PM Larry Garfield <larry@garfieldtech.com> > wrote: > >> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: >> Asymmetric Visibility. >> >> https://wiki.php.net/rfc/asymmetric-visibility >> >> Details are in the RFC, but it's largely a copy of Swift's support for the >> same. >> >> -- >> Larry Garfield >> larry@garfieldtech.com >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: https://www.php.net/unsub.php >> >> > Hi Larry, Ilija, > > Review the proposal and it looks great. > > I have a few subjects to point and maybe discuss further if you think so: > > 1. The first subject is visible even from the first phrase of the RFC that > uses both the terms "get and set operations" and "read and write > operations". > I'm wondering if you thought about using protected(write) or private(write).
Not really. get/set are the terms used in every other language with such a feature, and the words are shorter. There's really no compelling reason to use a longer, less common keyword.
> 3. I assume the target is both static and non-static properties. > This should be mentioned in the RFC.
That's the intent, although, now that you mention it, that part hasn't been implemented yet. :-) Ilija will add that to the patch and we'll update the RFC when it's been added. Thanks for the catch. --Larry Garfield

Unnamed Person

4 years ago
On 5-8-2022 19:08, Larry Garfield wrote:
> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the same. >
For what it's worth, here's my initial thoughts after reading the RFC: 1. In "Permitted visibility, it explicitly states that the "set" scope MUST be more restrictive than the "get" scope. However, no reason for this is provided. Please make the argumentation to support this choice explicit. 2. In "Interaction with __set" and "Relationship with readonly" the RFC states it wants to be consistent with the readonly behaviour, but that is a different keyword. IMHO, these modifiers should work independently of each other and are now improperly closely binded. If one wants readonly behaviour, use the `readonly` keyword. 3. I only see a hat-tip to the property accessors RFC regarding the chosen syntax. I'd like to see a more extensive discussion and consideration of various possible syntaxes. Some potential alternatives: - "visibility(get) visibility(set) type $prop" - which makes it explicit what each visibility applies to - "visibility(set: visibility) type $prop" - where it is clear that the "main" visibility is the default and anything within the brackets a deviation. This syntax would also more easily allow for expanding, like "visibility(set: visibility, unset: visibility) type $prop" I understand the desire for parallelism with Swift, but I don't think that's a valid argument. The syntax should be right for PHP and if that would happen to overlap/sync with a syntax used elsewhere: great, but that's not an argument to only look at that syntax without a proper discussion and due consideration for alternatives. 4. While I see no mention of "multi-property declarations", I presume the visibility would apply to all ? I think it would be good to mention this explicitly in the RFC as well. * By "multi-property declarations", I mean: `public string $type, $value = 'me', $somethingElse; 5. Are there tokenizer implications ? How will `set` (and possibly `get`) be tokenized ? 6. As this is something which can already be handled via magic methods, is there sufficient reason to make the language/engine more complicated to support this via a syntax ? Overall, I mainly just see an idea in the RFC, but no justification for the need for it, let alone for any of the choices made. Smile, Juliette

Mike Schinkel

4 years ago
> On Aug 5, 2022, at 1:08 PM, Larry Garfield <larry@garfieldtech.com> wrote: > > Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility
Conceptually, I believe this would add useful capabilities to PHP that are impossible to implement without: a.) Likely-sizable performance penalties, and b.) Unfortunate edge-cases; e.g. property_exists(), get_object_vars(), etc. Still, here are my concerns with the RFC as it currently exists: 1.) The syntax of "<keyword>(<keyword>)" feels inconsistent with the rest of the language. PHP has function(args...) and (<type>), both of which can be viewed as "a function of 'args'." However, "private(set)" is basically a constraint, not a "function of 'set'." PHP also has function(args...):<type> which is a constraint so I propose we consider the following syntax instead: <visibility>:<operation> Thus all of these: public:get public:set private:get private:set protected:get protected:set 2.)The idiom "public private:set" is hard to reason about when reading, at least for me. It also feels inconsistent in that it requires one type of visibility to be constrained, but not the other. Speaking of "explicit" from one of Larry's latest replies, I propose we consider requiring asymmetric visibility *explicitly* declared, in all cases. Requiring explicit declaration would: a.) Make asymmetric visibility easier to reason about, and b.) Ensure consistency when asymmetric visibility is used. Specifically I would propose we make these INVALID: public protected:set <type> $<var>; public private:set <type> $<var>; protected private:set <type> $<var>; But all of these following VALID: These as-is for backward compatibility and an obvious default behavior: public [<type>] $<var>; protected [<type>] $<var>; private [<type>] $<var>; And these where behavior is explicit: public:get protected:set <type> $<var>; public:get private:set <type> $<var>; protected:get private:set <type> $<var>; As well as these: public:set protected:get <type> $<var>; public:set private:get <type> $<var>; protected:set private:get <type> $<var>; And of course these: public:get public:set <type> $<var>; private:get private:set <type> $<var>; protected:get protected:set <type> $<var>; 3.) I have concerns about the proposed methods isProtectedSet() and isPrivateSet(). These names feels like we are asking if some thing "Set" is "Protected" or "Private" where no such "thing" exists in this context. In other words it reads as if you are asking "Is it a *protected* Set?" or "Is it a *private* Set?" Such naming also does not relate to the Property itself which is what the prefix verb "is" is acting on. I would propose instead we consider the following are these instead would be asking the *ability* to "Set" the Property is "Protected" or "Private" where The Property is again what the prefix verb "is" is acting on: isSetProtected() isSetPrivate() isGetProtected(), and isGetPrivate() #jmtcw -Mike

Ollie Read

4 years ago
On Mon, Aug 8, 2022, at 3:54 AM, Mike Schinkel wrote:
> 3.) I have concerns about the proposed methods isProtectedSet() and isPrivateSet(). > > These names feels like we are asking if some thing "Set" is "Protected" or "Private" where no such "thing" exists in this context. > > In other words it reads as if you are asking "Is it a *protected* Set?" or "Is it a *private* Set?" Such naming also does not relate to the Property itself which is what the prefix verb "is" is acting on. > > I would propose instead we consider the following are these instead would be asking the *ability* to "Set" the Property is "Protected" or "Private" where The Property is again what the prefix verb "is" is acting on: > > isSetProtected() > isSetPrivate() > isGetProtected(), and > isGetPrivate()
I feel almost as if we shouldn't pollute ReflectionProperty with these additional methods but should instead have something like ReflectionAsymmetricProperty, which would in turn extend ReflectionProperty. I guess it depends on how it's handled. If all property handling is updated so that "public int $number" is identical to "public:get public:set int $number", there's no issue, but if we treat those with separate visibilities to be additional, it probably makes sense to be somewhat separate. While isGetProtected() is technically false for "public int $number", if it's not inferred, it's semantically incorrect. A good example of this is ReflectionMethod::getPrototype, which throws an exception if there's no prototype. Although this is somewhat bizarre, it is at least a thing we do. --- Best Regards, *Ollie Read*

Mike Schinkel

4 years ago
Hi Ollie,
> On Aug 8, 2022, at 6:12 AM, Ollie Read <php@ollie.codes> wrote: > > On Mon, Aug 8, 2022, at 3:54 AM, Mike Schinkel wrote: >> 3.) I have concerns about the proposed methods isProtectedSet() and isPrivateSet(). >> >> These names feels like we are asking if some thing "Set" is "Protected" or "Private" where no such "thing" exists in this context. >> >> In other words it reads as if you are asking "Is it a *protected* Set?" or "Is it a *private* Set?" Such naming also does not relate to the Property itself which is what the prefix verb "is" is acting on. >> >> I would propose instead we consider the following are these instead would be asking the *ability* to "Set" the Property is "Protected" or "Private" where The Property is again what the prefix verb "is" is acting on: >> >> isSetProtected() >> isSetPrivate() >> isGetProtected(), and >> isGetPrivate() > > I feel almost as if we shouldn't pollute ReflectionProperty with these additional methods but should instead have something like ReflectionAsymmetricProperty, which would in turn extend ReflectionProperty.
On my 3rd point that you quoted my primary concern was that I felt the RFC solution of "is<Visbility>Set()" was hard to reason about but my own suggestion almost incidental. I felt if I voiced my concerns then it would be incumbent upon me to at least suggest an alternative. OTOH, I'm not sure I would embrace yet another reflection class. I think that would make an already (IMO) too-complex reflection class hierarchy even more complex. Maybe a better solution could be to add an optional $flags parameter to the existing is<Visbility>() methods, something like? isPublic(ReflectionProperty::GETTABLE) isProtected(ReflectionProperty::GETTABLE) isPrivate(ReflectionProperty::GETTABLE) isPublic(ReflectionProperty::SETTABLE) isProtected(ReflectionProperty::SETTABLE) isPrivate(ReflectionProperty::SETTABLE) isPublic(ReflectionProperty::GETTABLE & ReflectionProperty::SETTABLE) isProtected(ReflectionProperty::GETTABLE & ReflectionProperty::SETTABLE) isPrivate(ReflectionProperty::GETTABLE & ReflectionProperty::SETTABLE) #fwiw -Mike

Stephen Reay

4 years ago
> On 6 Aug 2022, at 00:08, Larry Garfield <larry@garfieldtech.com> wrote: > > Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the same. > > -- > Larry Garfield > larry@garfieldtech.com > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >
Hi Larry, I like most of this, but I have one disagreement. Can you expand a bit more on the reasoning for restricting the ability to combine __set() here? The RFC states that it’s to keep consistency with `readonly`, because __set on a readonly property that’s initialised throws an error - but isn’t that because of the nature of it being readonly, rather than because of the visibility rules? The error given is "Cannot modify readonly property” thrown from within the __set() method, not "Cannot access protected property” thrown from the outside calling context, when the __set() method is not implemented. If I set a property to readonly I would expect it to not be settable after initialisation, regardless of visibility, whether it’s via direct access or via __set, etc. (i.e. what readonly does now) But if I set a property to `public private(set)` for example, I would **expect** that the __set method on that class (or in the hierarchy if using protected(set)) would be called when setting it from an inaccessible context, just as it would be called when setting a regular protected property. It seems very unintuitive to me, that the first example here would work, but that the second would not work: class Foo { protected readonly int $bar; public function __set(string $name, mixed $value): void { $this->{$name} = $value; } } $a = new Foo; $a->bar = 1; class Bar { public protected(set) int $baz; public function __set(string $name, mixed $value): void { $this->{$name} = $value; } } $b = new Bar; $b->baz = 1; Cheers Stephen

Rowan Collins

4 years ago
On 08/08/2022 10:09, Stephen Reay wrote:
> The RFC states that it’s to keep consistency with `readonly`, because __set on a readonly property that’s initialised throws an error - but isn’t that because of the nature of it being readonly, rather than because of the visibility rules? The error given is "Cannot modify readonly property” thrown from within the __set() method, not "Cannot access protected property” thrown from the outside calling context, when the __set() method is not implemented.
If a property is "public readonly", the __set method is never called if you try to reassign it: https://3v4l.org/8PioE The proposal is that the same applies if it is "public private(set)": the assignment immediately fails, rather than falling back to the __set method. The logic, I think, is that a completely private property is "invisible", so __set acts like it doesn't exist at all and runs *instead of* the assignment; but a "public readonly" or "public private(set)" property is "visible", so the assignment is attempted and fails. However, I am concerned that the rules around where __set and __get are called are becoming increasingly complex with a lot of different concepts interacting - "declared", "defined", "typed", "initialized", "visible", "modifiable", etc. As I mentioned in a previous discussion, I think we need to work towards simplifying this - e.g. merge "typed" and "untyped" properties by making "public $foo" equivalent to "public mixed $foo", which would remove the distinction between "defined" and "initialized". Regards,
-- Rowan Tommins [IMSoP]

Stephen Reay

4 years ago
> On 8 Aug 2022, at 20:21, Rowan Tommins <rowan.collins@gmail.com> wrote: > > On 08/08/2022 10:09, Stephen Reay wrote: >> The RFC states that it’s to keep consistency with `readonly`, because __set on a readonly property that’s initialised throws an error - but isn’t that because of the nature of it being readonly, rather than because of the visibility rules? The error given is "Cannot modify readonly property” thrown from within the __set() method, not "Cannot access protected property” thrown from the outside calling context, when the __set() method is not implemented. > > > If a property is "public readonly", the __set method is never called if you try to reassign it: https://3v4l.org/8PioE The proposal is that the same applies if it is "public private(set)": the assignment immediately fails, rather than falling back to the __set method. > > The logic, I think, is that a completely private property is "invisible", so __set acts like it doesn't exist at all and runs *instead of* the assignment; but a "public readonly" or "public private(set)" property is "visible", so the assignment is attempted and fails. > > However, I am concerned that the rules around where __set and __get are called are becoming increasingly complex with a lot of different concepts interacting - "declared", "defined", "typed", "initialized", "visible", "modifiable", etc. > > As I mentioned in a previous discussion, I think we need to work towards simplifying this - e.g. merge "typed" and "untyped" properties by making "public $foo" equivalent to "public mixed $foo", which would remove the distinction between "defined" and "initialized". > > Regards, > > -- > Rowan Tommins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >
Hi Rowan, Thanks for trying to explain the logic here. @Larry is Rowan’s assessment accurate? Cheers Stephen

juan carlos morales

4 years ago
Quite a feature to the language. Nice I wonder if can be done using a different wording. If I can get an idea to contribute I Will write again. Keep it up!

Deleu

4 years ago
On Fri, Aug 5, 2022 at 7:09 PM Larry Garfield <larry@garfieldtech.com> wrote:
> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: > Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the > same. > > -- > Larry Garfield > larry@garfieldtech.com > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
Just wanted to point out that I loved syntax proposals by Juliette and Mike. Any of them would be better. Making a better syntax for PHP would be better than consistency with Swift, IMO. But I'm also inclined to agree with Marco Pivetta that this change is a lot less exciting now that we have `readonly`. In extremely rare cases where I need to modify an attribute more than once, I just fallback to private + getter and the nice thing about it is that it creates a consistent mindset that if a property is being accessed via a getter, then the reader should be advised that the property may change depending on WHEN it's accessed.
-- Marco Deleu

Nicolas Grekas

4 years ago
> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: > Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the > same. >
Thank you Larry and Ilija for this RFC. After reading it, I'm wondering about inheritance rules. What would be the rules when redeclaring an existing asymmetric property in a child class? The RFC should mention them I believe. But I'm also wondering about the use case where asymmetry would be useful in practice now that we have readonly? I voted against readonly at the time because it didn't address cloning, and also because it deprives end-users vs code authors IMHO (alike "final"). I would have very much prefered asymmetric visibility back then. But now that we are here, what are the use cases? If it's cloning only, then I would very much prefer finding a way to fix it rather than providing two subtly different ways to achieve the same thing. Eg can't we allow __clone methods to bypass the readonly restrictions? The engine already handles "guards" when using magic property accessors. Can't we use something similar for clone? And if yes, what would remain of the motivations for the asymmetric RFC? Cheers, Nicolas

Larry Garfield

4 years ago
On Thu, Aug 11, 2022, at 8:48 AM, Nicolas Grekas wrote:
>> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: >> Asymmetric Visibility. >> >> https://wiki.php.net/rfc/asymmetric-visibility >> >> Details are in the RFC, but it's largely a copy of Swift's support for the >> same. >> > > Thank you Larry and Ilija for this RFC. > > After reading it, I'm wondering about inheritance rules. What would be the > rules when redeclaring an existing asymmetric property in a child class? > The RFC should mention them I believe.
Yeah, Ilija is working on that still, I believe. I believe the plan is, as with properties now, to allow widening access but not narrowing. static properties will also be supported, but that implementation is WIP.
> But I'm also wondering about the use case where asymmetry would be useful > in practice now that we have readonly? > I voted against readonly at the time because it didn't address cloning, and > also because it deprives end-users vs code authors IMHO (alike "final"). I > would have very much prefered asymmetric visibility back then. But now that > we are here, what are the use cases? If it's cloning only, then I would > very much prefer finding a way to fix it rather than providing two subtly > different ways to achieve the same thing. > > Eg can't we allow __clone methods to bypass the readonly restrictions? The > engine already handles "guards" when using magic property accessors. Can't > we use something similar for clone? > > And if yes, what would remain of the motivations for the asymmetric RFC? > > Cheers, > Nicolas
There's a couple of use cases I see off hand: 1) Cloneability, as you've noted. 2) Fixing inheritance with readonly. Right now, readonly is only settable from private scope, not protected. So child classes are SOL. 3) A property that is rebuild based on other properties. Eg, public private(set) $fullName, which is updated internally whenever $o->setFirstName() or $o->setLastName() are called. (Or with accessor hooks in the future, which is on the roadmap.) There's likely others, too. I would also have rather had AV in the first place rather than readonly, as readonly is now getting in the way. :-( But there are things readonly cannot reasonably do, and that's made worse by bad assumptions that static analysis tools have made about its usage. We're working on a response to some other points made, but want to get on the same page with some more details first. Stay tuned for a follow up. --Larry Garfield

André Hänsel

4 years ago
Nicolas Grekas wrote:
> But I'm also wondering about the use case where asymmetry would be > useful in practice now that we have readonly? > I voted against readonly at the time because it didn't address > cloning, and also because it deprives end-users vs code authors IMHO > (alike "final"). I would have very much prefered asymmetric > visibility back then. But now that we are here, what are the use cases?
Isn't it (obviously?) to have setters that ensure all invariants of the properties are met? I'd still rather have Property Accessors as described in https://wiki.php.net/rfc/property_accessors, but I think AV is a step in the right direction. As for readonly, I don't understand why that exists at all. Yes, it's good that readonly properties can only be set from within the class, but only once, so you get setters that throw when called a second time, which I think is very, VERY unexpected behavior. Calling them "readonly" is also somewhat misleading because of course the objects assigned to those "readonly" properties can still be mutated, they can just not be reassigned.

Mike Schinkel

4 years ago
> On Aug 11, 2022, at 7:38 PM, André Hänsel <andre@webkr.de> wrote: > > Nicolas Grekas wrote: > >> But I'm also wondering about the use case where asymmetry would be >> useful in practice now that we have readonly? >> I voted against readonly at the time because it didn't address >> cloning, and also because it deprives end-users vs code authors IMHO >> (alike "final"). I would have very much prefered asymmetric >> visibility back then. But now that we are here, what are the use cases? > > Isn't it (obviously?) to have setters that ensure all invariants of the > properties are met? I'd still rather have Property Accessors as > described in https://wiki.php.net/rfc/property_accessors, but I think > AV is a step in the right direction. > > As for readonly, I don't understand why that exists at all.
For immutability. Here is one reference on immutability offered up by the google gods: https://hackernoon.com/5-benefits-of-immutable-objects-worth-considering-for-your-next-project-f98e7e85b6ac <https://hackernoon.com/5-benefits-of-immutable-objects-worth-considering-for-your-next-project-f98e7e85b6ac> Immutability goes hand-in-hand with Functional Programming (FP.) While PHP is not a functional programming language, PHP developers can gain benefits from an FP style for which immutability is beneficial. Also from the google gods, if you are unfamiliar with FP: https://alvinalexander.com/scala/fp-book/benefits-of-functional-programming/ <https://alvinalexander.com/scala/fp-book/benefits-of-functional-programming/> Hope this helps. -Mike P.S. Also, the Internals list's very own Larry Garfield is a huge advocate for FP in PHP, voilà: https://leanpub.com/thinking-functionally-in-php https://www.garfieldtech.com/taxonomy/term/95

Larry Garfield

4 years ago
On Fri, Aug 5, 2022, at 12:08 PM, Larry Garfield wrote:
> Ilija Tovilo and I are happy to present the first new RFC for PHP 8.3: > Asymmetric Visibility. > > https://wiki.php.net/rfc/asymmetric-visibility > > Details are in the RFC, but it's largely a copy of Swift's support for the same.
Hi folks. Ilija and I have been discussing and experimenting extensively based on the feedback to the first draft. Here's our thoughts so far, some additional data, and a request for feedback. (These changes haven't been made to the RFC just yet, as there's outstanding questions for which we want feedback below.) 1. Rather than `readonly` being forbidden on asymmetric properties, it's better to redefine it as "write once, and if no `set` visibility is defined, make it private." That would still allow for `public protected(set) readonly string $blah` (to work around existing limitations in `readonly`), but also more or less preserve the current syntax in a logical way. 2. Given that... we're not sure that it makes sense to have any visibility scope other than `get`/`set`. `once` becomes unnecessary with that understanding of `readonly`, and we cannot think of any other examples that are not a stretch. So that axis of extensibility is of minimal importance. 3. Since there has been some confusion, our goal *is* to implement property accessors in the future as a follow-on to this RFC. However, conceptually property "hooks" and asymmetric visibility are separate features. They also both have ample bikeshed potential. That's why we're proposing them as two separate RFCs. (Technically property hooks could also be done first, but this one is smaller.) That said, it's become obvious that we cannot avoid discussing property hooks at the same time since the syntax does overlap. So in the discussion below I will include property hooks, but deliberately gloss over parts that are not relevant right now. Please humor me and do the same. :-) 4. In concept, there's two main syntactic models for property hooks/accessors: Javascript/Python style (annotated methods) and Swift/C# style (method-esque code blocks on properties). We were trying to keep our options open and avoid a syntax that locks us into one or the other. However, we've concluded that the annotated methods style just wouldn't work at all in a language with explicit types and visibility controls. We've therefore made the executive decision that only the Swift/C# style is an option. 5. That leaves the basic question of "where to put the visibility," as Swift and C# put them in different places. (There are other differences not relevant for now.) Swift: ```swift class User { public private(set) var first: String public private(set) var last: String public private(set) fullName: String { get { ... } set { ... } } } ``` C#: ```csharp class User { public string first { get; private set; } public string last { get; private set; } public string fullName { get { ... } private set { ... } } } ``` The relevant difference is that in Swift, all visibility is on the left of the property while in C# it is split between the left and right. The main argument for Swift-style: Keep everything in one place, and if there's no property hooks we don't even have to think about it. The main argument for C#-style: It avoids repeating a keyword (eg, `set`) if using both features. It *may* be possible to omit the `get` in the C# example above if it's just the default. Ilija is still working to figure that out. ## References make everything worse One other caveat to consider is (naturally) references. A `get` hook cannot return by reference, as that would allow setting the value via its reference and bypassing a `set` hook. A side effect of that restriction is that arrays cannot be modified in place on properties with a `get` hook. That is, assuming the `$bar` property is an array that leverages the extra hooks above (by whatever syntax), the following is not possible: ```php $f = new Foo(); $f->bar[] = 'beep'; ``` That would require returning `$bar` by reference so that the `[]` operation could apply to it directly. However, that then bypasses any restrictions imposed by a `set` hook (such as the values had to be integers). The following, however, would be legal: ```php $f = new Foo(); $b = $f->bar; $b[] = 'beep'; $f->bar = $b; ``` That problem wouldn't happen if all we're changing is visibility, however. That means in the C# style, we would need to differentiate whether the block on the right is intended to disable references or not. Ilija has proposed `raw` to indicate that. That would mean: ```php class Test { public $a { private set; } public $b { private raw; } public function inScope() { echo $this->a; // echo $this->getA(); $this->a = 'a'; // $this->setA('a'); echo $this->b; // echo $this->b; $this->b = 'b'; // $this->b = 'b'; } } $test = new Test(); echo $test->a; // echo $test->getA(); $test->a = 'a'; // Error, set operation not accessible echo $test->b; // echo $test->getB(); $test->b = 'b'; // Error, set operation not accessible ``` The take-away for the asymmetric visibility only case is that we would need to use `raw` instead of `set`, in order to avoid confusion later with accessor hooks and whether or not to disable references. The Swift-style syntax, as it does not require any hook-like syntax to just control visibility, does not have this problem. There is no ambiguity to resolve, so no need for a `raw` keyword. ## Common use cases While there are a myriad of possible ways to use both asymmetric visibility and property accessor hooks, we anticipate three usage patterns to predominate: 1. Public read, private/protected write, but that's it. 2. Public read, public write, but some extra validation on set. 3. Public lazily-derived value, no write. In particular, it seems to us (though we have no precise data to back it up) that it is unlikely that both asymmetric visibility and property hooks would be used at the same time, in the typical cases. That should inform the discussion about syntax. ## Comparison For the moment I'm just going to use Swift-style as-is. There's a discussion later to be had about minor variations on it, as suggested by a few people in the previous thread. Stand by. Length comparison: ```php // Current code, for comparison public readonly string $first; // Swift-ish public private(set) string $first; // C#-ish, note "raw" because there's no hook public string $first { get; private raw; } ``` Both options suggest possible shorthands: ```php // Current code, for comparison public readonly string $first; // Swift-ish private(set) string $first; // C#-ish, assuming it can be done public string $first { private raw; } ``` If combining with `readonly`, we get: ```php // Current code, for comparison public readonly string $first; // Swift-ish public protected(set) readonly string $first; protected(set) readonly string $first; // C#-ish public readonly string $first { get; private set; } public readonly string $first { private raw; } ``` If we mix in for-reals accessor hooks, we get: ```php // Swift-ish public private(set) string $first { get { ... } set { ... } } // C#-ish, note now we're using "set". public string $first { get { ... } private set { ... } } ``` If other hooks are added (Swift has `beforeSet` and `didSet`, and we intend to include those as well for PHP), they don't really make sense to have visibility modifiers on them. It's arguable if `isset` or `unset` (to mirror the existing magic methods) would want those. Does it make sense to have "public read, public set, but private unset"? Or is `unset` just controlled by `set`? Unclear. Another complication is that, for forward compatibility, we would need a way to disable references on no-hook properties so that hooks could be added later without a change in reference behavior. Given the postfix syntax for hooks, we believe the most natural way to do that would be: ```php // Swift-ish public string $foo {} // C#-ish public string $foo {} // which is shorthand for: public string $foo { public raw; } ``` Again, the C# model implies a bit more complexity and nuance. ## Questions Question 1: Which general model (Swift or C#) is preferable, and why? (At the moment, Ilija and I disagree on which one is preferable. Hence, poll.) ## If Swift If we go with Swift-style, several people suggested alternative syntax options. This is *mostly* a bikeshed question, so I will just list them: ```php public private(set) string $first; public private:set string $first; public:private string $first; public private string $first; public(set: private) string $first; ``` Some of which have natural shorthands: ```php private(set) string $first; private:set string $first; :private string $first; N/A N/A ``` Question 2: IF we go with Swift-style, which of the above syntaxes would you prefer, and why? Please answer both questions only via this poll so can collect actual data. https://docs.google.com/forms/d/e/1FAIpQLSefq15VvGNIXSnQaMTl3RW451w0E8oesny8c4PLqmKl8HhQ-Q/viewform Thank you all for your time and input. --Larry Garfield and Ilija Tovilo

Tim Düsterhus

4 years ago
Hi On 8/31/22 22:43, Larry Garfield wrote:
> ## References make everything worse > > […] > > That problem wouldn't happen if all we're changing is visibility, however. That means in the C# style, we would need to differentiate whether the block on the right is intended to disable references or not. Ilija has proposed `raw` to indicate that. That would mean: > > ```php > class Test > { > public $a { private set; } > public $b { private raw; } > > public function inScope() > { > echo $this->a; // echo $this->getA(); > $this->a = 'a'; // $this->setA('a'); > > echo $this->b; // echo $this->b; > $this->b = 'b'; // $this->b = 'b'; > } > } > > $test = new Test(); > > echo $test->a; // echo $test->getA(); > $test->a = 'a'; // Error, set operation not accessible > > echo $test->b; // echo $test->getB(); > $test->b = 'b'; // Error, set operation not accessible > ``` > > The take-away for the asymmetric visibility only case is that we would need to use `raw` instead of `set`, in order to avoid confusion later with accessor hooks and whether or not to disable references. > > The Swift-style syntax, as it does not require any hook-like syntax to just control visibility, does not have this problem. There is no ambiguity to resolve, so no need for a `raw` keyword. >
I've read through that explanation three times, but don't understand why the C#-style syntax would require to differentiate between `set` and `raw`. Isn't the difference between "visibility only" and "accessor" not clear [1] by having either '{' or ';' after the 'set'? [1] Read: Clear to the engine, not talking about the human reader here. Best regards Tim Düsterhus

Claude Pache

4 years ago
> Le 31 août 2022 à 23:27, Tim Düsterhus <tim@bastelstu.be> a écrit : > I've read through that explanation three times, but don't understand why the C#-style syntax would require to differentiate between `set` and `raw`. Isn't the difference between "visibility only" and "accessor" not clear [1] by having either '{' or ';' after the 'set'? > > [1] Read: Clear to the engine, not talking about the human reader here.
Even if they manage to make the difference clear for the engine without resorting to “raw”, I think that it is important to have a syntactical marker for the sake of humans. Otherwise, someone would want to incrementally improve their class property, and at some unkwown point in time, it becomes unexpectedly unusable for references and array mutations. —Claude

Larry Garfield

4 years ago
On Wed, Aug 31, 2022, at 4:26 PM, Tim Düsterhus wrote:
> Hi > > On 8/31/22 22:43, Larry Garfield wrote: >> ## References make everything worse >> >> […] >> >> That problem wouldn't happen if all we're changing is visibility, however. That means in the C# style, we would need to differentiate whether the block on the right is intended to disable references or not. Ilija has proposed `raw` to indicate that. That would mean: >> >> ```php >> class Test >> { >> public $a { private set; } >> public $b { private raw; } >> >> public function inScope() >> { >> echo $this->a; // echo $this->getA(); >> $this->a = 'a'; // $this->setA('a'); >> >> echo $this->b; // echo $this->b; >> $this->b = 'b'; // $this->b = 'b'; >> } >> } >> >> $test = new Test(); >> >> echo $test->a; // echo $test->getA(); >> $test->a = 'a'; // Error, set operation not accessible >> >> echo $test->b; // echo $test->getB(); >> $test->b = 'b'; // Error, set operation not accessible >> ``` >> >> The take-away for the asymmetric visibility only case is that we would need to use `raw` instead of `set`, in order to avoid confusion later with accessor hooks and whether or not to disable references. >> >> The Swift-style syntax, as it does not require any hook-like syntax to just control visibility, does not have this problem. There is no ambiguity to resolve, so no need for a `raw` keyword. >> > > I've read through that explanation three times, but don't understand why > the C#-style syntax would require to differentiate between `set` and > `raw`. Isn't the difference between "visibility only" and "accessor" not > clear [1] by having either '{' or ';' after the 'set'? > > [1] Read: Clear to the engine, not talking about the human reader here.
Yeah, it's complicated, and Ilija had to explain it to me several times. :-) The tricky part is that there's *three* different toggles involved here. 1. Asymmetric visibility. 2. Accessor hooks. 3. Disabling references on properties. Invoking 2 necessarily implies also doing 3, for reasons given above. However, we also want people to be able to enable 3 on its own, so that IF they enable accessor hooks later, it is transparent and doesn't imply breaking references unexpectedly. (Viz, you could have a value object full of `public string $foo {}` type properties, which disables references, and then slip in a get/set hook later and no one notices.) So we need a way to indicate "I'm using a-viz, and disabling references" as well as "I'm using a-viz, and not disabling references." With the Swift style syntax, that's super straightforward. public string $foo; // No a-viz, references work. public string $foo {} // No a-viz, references disabled. public private(set) string $foo; // a-viz, references work. public private(set) string $foo {} // a-viz, references disabled. And the {} makes a convenient marker because you'd be adding it in the future anyway if you add hooks, which would force disabling of references. So it's a natural flow from one option to the next. With the C# style, a-viz means you're *always* putting braces on the right, so we cannot do that. Instead, you need public string $foo; // No a-viz, references work. public string $foo {} // No a-viz, references disabled. public string $foo { private raw; } // a-viz, references work. public string $foo { private set; } // a-viz, references disabled. So then the toggle for disabling references is "there's a set or get keyword inside the braces", but we then need a different keyword for "I'm *just* doing a-viz, and I don't want to disable references." Hence, "raw". (I'm assuming in the above that Ilija is able to make omitting the "get; set;" if there's no body for them. He's reasonably sure we can but hasn't confirmed it.) Is that clearer? --Larry Garfield