[RFC] Property write visibility

php.internals

André Rømcke

6 years ago
Good morning Internals, I'd like to start discussion on a new RFC proposing a way to be able to (optionally) specify property write visibility, separate from read: https://wiki.php.net/rfc/property_write_visibility This enables readonly, write-only and immutable like semantics for PHP's properties. These are the common use cases where users tend to resort to magic methods or setter/getter methods. This proposal will as such avoid unnecessary boilerplate, makes coding easier and more ergonomic, and in the case of magic method usage improve performance. Best, André Rømcke

Max Semenik

6 years ago
On Mon, Jun 29, 2020 at 12:42 PM André Rømcke <andre.romcke@gmail.com> wrote:
> This enables readonly, write-only and immutable like semantics for PHP's > properties. >
I agree that there is a use case for it, however I don't think the proposed syntax `public:private` is intuitive. Maybe we can come up with something better?
-- Best regards, Max Semenik

Deleu

6 years ago
I don't dislike the syntax and I think I can get used to it extremely fast, tbh. Are there any other languages that have a similar syntax? Has any other language syntax been considered? On Mon, Jun 29, 2020 at 12:01 PM Max Semenik <maxsem.wiki@gmail.com> wrote:
> On Mon, Jun 29, 2020 at 12:42 PM André Rømcke <andre.romcke@gmail.com> > wrote: > > > This enables readonly, write-only and immutable like semantics for PHP's > > properties. > > > > I agree that there is a use case for it, however I don't think the proposed > syntax `public:private` is intuitive. Maybe we can come up with something > better? > > > -- > Best regards, > Max Semenik >
-- Marco Aurélio Deleu

André Rømcke

6 years ago
man. 29. jun. 2020 kl. 12:06 skrev Deleu <deleugyn@gmail.com>:
> Are there any other languages that have a similar syntax? Has any other
language syntax been considered The only one I could find that matches is Swift: public private(set) var numberOfEdits = 0 https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID18 Beyond that there are some notable related examples: C# public readonly Color Black = new Color(0, 0, 0); The semantics of readonly is closer to what we refer to as immutable here: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#readonly-fields If you want other semantics in C# you'll need to resort to Accessors: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#accessors Rust: // This field can be read (but not written) by super. #[readonly] pub(super) readable: i32, Essentially a feature using "attributes": https://docs.rs/readonly/0.1.6/readonly/
-- Best André Rømcke

André Rømcke

6 years ago
> > >> I agree that there is a use case for it, however I don't think the > proposed syntax `public:private` is intuitive. Maybe we can come up with > something better? >
Would something closer to Swift be better? If so I expanded the RFC with that syntax option as well: class User { public private(set) int $id; public protected(set) string $name; public function __construct(int $id, string $name) { $this->id = $id; $this->name = $name; }}

Mark Randall

6 years ago
On 29/06/2020 15:13, André Rømcke wrote:
> Would something closer to Swift be better? If so I expanded the RFC with > that syntax option as well:
Borrowing from the various accessors RFCs: public int $x { protected set; protected unset; } Then a future RFC can build upon it by adding the override functions while keeping the same base syntax. Mark Randall

Mark Randall

6 years ago
On 29/06/2020 10:41, André Rømcke wrote:
> https://wiki.php.net/rfc/property_write_visibility
I'd like this feature as a stop-gap for accessors. However, I don't like the syntax at all, I couldn't reasonably vote for it in its current suggested state. I think it's just too messy. I'm not sure what the alternative should be though. It seems like the accessor syntax would be the only long-term clean way. Mark Randall

Marco Pivetta

6 years ago
Hey André, This does indeed seem to cut into the traditional property accessors scope. I'm no longer seeing a reason to have property accessors, since immutable state (and promotion thereof) is much more relevant to fight bugs and improve application performance and maintainability. Therefore, I would endorse re-visiting the RFC at https://wiki.php.net/rfc/write_once_properties instead. Máté also linked today ( https://twitter.com/kocsismate90/status/1277625002720968704) that C# 9.0 is getting init-only properties, which looks similar to the RFC linked above. Assuming we solve annoyances around cloning, that would be a more sensible way forward, while property accessors seem to perpetuate the bad practice of getters and setters, which are the main cause for me having to fire up a debugger in any environment. Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/ On Mon, Jun 29, 2020 at 11:42 AM André Rømcke <andre.romcke@gmail.com> wrote:

Rowan Collins

6 years ago
On Mon, 29 Jun 2020 at 16:44, Marco Pivetta <ocramius@gmail.com> wrote:
> property accessors seem to perpetuate the bad practice > of getters and setters >
Outside of access restrictions, a common use case I've seen for property accessors is lazy-loading, which I know is something you use in other forms.
> Máté also linked today (
https://twitter.com/kocsismate90/status/1277625002720968704) that C# 9.0 is getting init-only properties, which looks similar to the RFC linked above. Note that in C# the new "init" keyword is explicitly an extension of property accessors [1], and relates specifically to "object initializers", which PHP does not have; a proposal for them last year [2] was rejected. An existing "readonly" keyword [3] allows you to write a variable only in the constructor, and "init" is mostly needed because object initializers are run too late for this. C# 9.0 also has a bunch of features around immutable data structures in general, including a variant of object initializers called "with-expressions" [4] to solve the "modified clone" problem. [1] https://github.com/dotnet/csharplang/blob/master/proposals/init.md#detailed-design [2] https://wiki.php.net/rfc/object-initializer [3] https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly [4] https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/#with-expressions
-- Rowan Tommins [IMSoP]

Marco Pivetta

6 years ago
Hey Rowan, On Mon, Jun 29, 2020, 18:19 Rowan Tommins <rowan.collins@gmail.com> wrote:
> On Mon, 29 Jun 2020 at 16:44, Marco Pivetta <ocramius@gmail.com> wrote: > > > property accessors seem to perpetuate the bad practice > > of getters and setters > > > > > Outside of access restrictions, a common use case I've seen for property > accessors is lazy-loading, which I know is something you use in other > forms. >
I had already tested the write-once properties RFC with lazy-loading: works just fine 👍

Rowan Collins

6 years ago
On 29/06/2020 17:21, Marco Pivetta wrote:
> Hey Rowan, > > On Mon, Jun 29, 2020, 18:19 Rowan Tommins <rowan.collins@gmail.com > <mailto:rowan.collins@gmail.com>> wrote: > > On Mon, 29 Jun 2020 at 16:44, Marco Pivetta <ocramius@gmail.com > <mailto:ocramius@gmail.com>> wrote: > > > property accessors seem to perpetuate the bad practice > > of getters and setters > > > > > Outside of access restrictions, a common use case I've seen for > property > accessors is lazy-loading, which I know is something you use in > other forms. > > > I had already tested the write-once properties RFC with lazy-loading: > works just fine 👍 >
I meant that property accessors can be used to implement lazy-loading, removing the need for magic methods and dynamic references to variables, e.g.: class Whatever {     private string $_name;     public string $name {          get {              if( ! isset($_name) ) { $this->loadData(); }              return $this->_name;          }     } } Regards,
-- Rowan Tommins (né Collins) [IMSoP]

Nicolas Grekas

6 years ago
> > I'd like to start discussion on a new RFC proposing a way to be able to > (optionally) specify property > write visibility, separate from read: > > https://wiki.php.net/rfc/property_write_visibility > > This enables readonly, write-only and immutable like semantics for PHP's > properties. > > These are the common use cases where users tend to resort to magic methods > or setter/getter > methods. This proposal will as such avoid unnecessary boilerplate, makes > coding easier and more > ergonomic, and in the case of magic method usage improve performance. >
Thanks, André, I really like it. I feel like it perfectly fits the runtime visibility model of PHP. The RFC could have some words to tell how this would behave when considering the existing magic accessors. We should also define the behavior when creating a reference (write access should be granted, isn't it?) and when unsetting a property. I'd like to share three more points to the discussion: 1. The previous RFC(s) shocked on a conflict between purity declarations (immutable/readonly) and the effective need for mutability in e.g. cloning operations. My experience in PHP is that keywords are strong guidelines but should never be hard blockers: PHP is a runtime scripting language and some of its power and flexibility comes from that, e.g. being able to use reflection sometime to read/write a property in a "friend" scope. "final" is the canonical contra-example of an imperative keyword that blocks legit use cases (e.g. inheritance proxies) I understand why some ppl want immutable/readonly hard guarantees. I think those are nice goals, and I also think that they are better solved by attributes/annotations. These features fit compiled languages. In PHP, they fit static analyzers. There are great ones already and they do a really good job. 2. In other answers, I'm seeing questions about private(isset)/private(unset) in addition to just read+write dissociation. I'm not sure this makes sense: once a property is declared, "isset-accessibility" is fully covered by "read-accessibility". Same for "unset-accessibility": it's included in "write-accessibility". Clarifying how magic accessors would work in the RFC should help solve this branch of the discussion. 3. About accessors, they look like another topic to me. I'm not seeing the need for accessors at all actually. You might disagree, but my point is: that's another topic. We don't need to agree on the usefulness of accessors to decide that this RFC provides actionable value that would benefit the ecosystem. Being able to enforce an externally-immutable-but-internally-mutable state is already a lot of value, I know where I would leverage it immediately. About the syntax, the proposed one looks quite effective to me. It's new, we need to get used to it. This should come fast given how simple it is. TL;DR, I fully support the RFC :) Nicolas

Dik Takken

6 years ago
On 29-06-2020 11:41, André Rømcke wrote:
> Good morning Internals, > > I'd like to start discussion on a new RFC proposing a way to be able to > (optionally) specify property > write visibility, separate from read: > > https://wiki.php.net/rfc/property_write_visibility
Perhaps another option could be to use attributes: <<ReadOnly>> public int $id; Assuming named arguments get accepted as well, we could use optional arguments to specify special behavior by writing: <<ReadOnly(protected: True)>> public int $id; These optional arguments could also be introduced later using a followup RFC, after introducing a basic ReadOnly attribute. This attribute-based approach could be extended later to implement property accessors. That would provide property accessors similar to how it is done in Python: <<Property>> public function someValue(): int { return $this->someValue; } which could then be accessed as: $instance->someValue The above accessor method would be the equivalent of: <<ReadOnly>> public int $someValue; That makes the ReadOnly attribute a short hand for the Property attribute. This short hand is something that Python does not have, by the way. Using accessor attributes to create a writable property could look like this: <<PropertySetter>> public function someValue(int $newValue) { $this->someValue = $newValue; } Making the property writable only in the protected or private scope could be done by using the existing mechanism of declaring the accessor method as protected or private. So perhaps an attribute-based approach provides a nice alternative path for gradually improving object ergonomics. Regards, Dik Takken

André Rømcke

6 years ago
> Perhaps another option could be to use attributes: > > <<ReadOnly>> > public int $id;
I’d actually also like a syntax like that as well for ReadOnly and Immutable in the end. It's more readable and it would be possible to annotate on the class level as well (to affect all properties). So I initially explored* in this direction, however if we go that way first we'll likely end up with any reflection code having to deal with attributes and understand their underlying visibility semantics. If we rather go this route of first introducing the concept of disconnected visibility. The reflection API is far clearer / understandable, more future proof, and adding the attributes as immediate syntax sugar followup is far more straightforward. * initial explorative draft rfc: https://wiki.php.net/rfc/readonly_and_immutable_properties

Илья Сомов

6 years ago
I do agree with Marco that this is a step in a wrong direction. Our goal should be not ensuring that a prop is not rewritten outside, but rather that it is not re-written at all. It makes no sense to rewrite a property in majority of cases: 1. fields of an DTO - nay. Should be immutable instead. You never change DTO, don't you? 2. injected services (like DB connection or an HTTP client) - nay. Shouldn't ever be overwritten once an object containing them is instantiated. Write-once / immutable is preferred. 3. data fields of an Entity-like object - looks like the only reasonable case. However adding dedicated syntax for this is arguably not much better that what we have now. It is already solvable with standard getX() pattern, or with a single method returning a required chunk of state as DTO. So for me this does only solve the problem of having getters (or dedicated methods) in #3, which is rare enough compared to other code, and is already solvable with syntax existing nowadays already. Introducing the new syntax, however, will have the following downsides: - adding proper immutable / write-once properties to the language will be met with much more resistance: "Why adding immutable if we have private set already!" - this will encourage using them in places where immutability (e.g. via static analysis tool like Psalm) should be used instead (see #1 and #2). - adding more advanced property accessors feature will be also likely met with more resistance.

Nikita Popov

6 years ago
On Mon, Jun 29, 2020 at 11:43 AM André Rømcke <andre.romcke@gmail.com> wrote:
> Good morning Internals, > > I'd like to start discussion on a new RFC proposing a way to be able to > (optionally) specify property > write visibility, separate from read: > > https://wiki.php.net/rfc/property_write_visibility > > This enables readonly, write-only and immutable like semantics for PHP's > properties. > > These are the common use cases where users tend to resort to magic methods > or setter/getter > methods. This proposal will as such avoid unnecessary boilerplate, makes > coding easier and more > ergonomic, and in the case of magic method usage improve performance. >
I think this is a good direction to explore, but would recommend delaying it until PHP 8.1. As the RFC and discussion note, the design space here overlaps significantly with both readonly/immutability and property accessors. We should take enough time to consider these alternatives/extensions as well, to ensure that we don't end up with multiple distinct and possibly incompatible mechanisms. We don't have that time before PHP 8.0 feature freeze. Regards, Nikita

André Rømcke

6 years ago
> > ergonomic, and in the case of magic method usage improve performance. >> > > I think this is a good direction to explore, but would recommend delaying > it until PHP 8.1. As the RFC and discussion note, the design space here > overlaps significantly with both readonly/immutability and property > accessors. We should take enough time to consider these > alternatives/extensions as well, to ensure that we don't end up with > multiple distinct and possibly incompatible mechanisms. We don't have that > time before PHP 8.0 feature freeze. >
I've updated the RFC to rather aim for 8.1, I fully agree this should in any form be made asure to align well with the related requests. Larrye's email is a good example of what kind the language might look like when several such features are potentially accepted in the future. May I propose that some of us interested in this do a short call over the summer on this topic to see if we can align on direction?

Larry Garfield

6 years ago
On Tue, Jul 7, 2020, at 2:35 AM, André Rømcke wrote:
> > > > ergonomic, and in the case of magic method usage improve performance. > >> > > > > I think this is a good direction to explore, but would recommend delaying > > it until PHP 8.1. As the RFC and discussion note, the design space here > > overlaps significantly with both readonly/immutability and property > > accessors. We should take enough time to consider these > > alternatives/extensions as well, to ensure that we don't end up with > > multiple distinct and possibly incompatible mechanisms. We don't have that > > time before PHP 8.0 feature freeze. > > > > > I've updated the RFC to rather aim for 8.1, I fully agree this should in > any form be made asure to align well with the related requests. Larrye's > email is a good example of what kind the language might look like when > several such features are potentially accepted in the future. > > May I propose that some of us interested in this do a short call over the > summer on this topic to see if we can align on direction?
I'm open to that. Explicit design process FTW. --Larry Garfield

Dan Ackroyd

6 years ago
On Mon, 29 Jun 2020 at 10:42, André Rømcke <andre.romcke@gmail.com> wrote:
> > I'd like to start discussion on a new RFC proposing a way to be able to > (optionally) specify property > write visibility, separate from read: > > https://wiki.php.net/rfc/property_write_visibility >
From the RFC: "Attempting to pass a property value outside of allowed writable scope as a reference, results in an error." This part could probably do with having some details for it. Also, any implementation of that rule might need to be checked for performance issues. cheers Dan Ack

Nikita Popov

6 years ago
On Tue, Jun 30, 2020 at 1:26 PM Dan Ackroyd <Danack@basereality.com> wrote:
> On Mon, 29 Jun 2020 at 10:42, André Rømcke <andre.romcke@gmail.com> wrote: > > > > I'd like to start discussion on a new RFC proposing a way to be able to > > (optionally) specify property > > write visibility, separate from read: > > > > https://wiki.php.net/rfc/property_write_visibility > > > > From the RFC: > "Attempting to pass a property value outside of allowed writable scope > as a reference, results in an error." > > This part could probably do with having some details for it. > > Also, any implementation of that rule might need to be checked for > performance issues. >
I think it would be technically possible, by extending the existing mechanism for references to typed properties. However, we definitely shouldn't do this, because it goes against existing language semantics. You can take a reference to a normal private property (i.e. private get, private set), pass it outside the class and then modify it. It would be inconsequent to assign stricter semantics to a weaker visibility requirement (public get, private set). Regards, Nikita

André Rømcke

6 years ago
>> "Attempting to pass a property value outside of allowed writable scope >> as a reference, results in an error." > > ... we definitely shouldn't do this, because it goes against existing language semantics. You can take a reference to a normal private property (i.e. private get, private set), pass it outside the class and then modify it.
This was copied from some other source actually. I’m all for staying as close as possible to how the language behaves on this, changing it should be different RFC. Should I just remove this section or do you have suggestions for what we need to specify when it comes references?

Nikita Popov

6 years ago
On Wed, Jul 1, 2020 at 12:58 PM André Rømcke <andre.romcke@gmail.com> wrote:
> > "Attempting to pass a property value outside of allowed writable scope >> as a reference, results in an error." > > > ... we definitely shouldn't do this, because it goes against existing > language semantics. You can take a reference to a normal private property > (i.e. private get, private set), pass it outside the class and then modify > it. > > > > This was copied from some other source actually. I’m all for staying as > close as possible to how the language behaves on this, changing it should > be different RFC. > > Should I just remove this section or do you have suggestions for what we > need to specify when it comes references? >
I think the only thing worth mentioning where references are concerned is that "$x =& $this->prop" is considered a write of $this->prop, not a read, so it is subject to the write visibility. But once the reference is acquired, visibility no longer factors into the behavior. Nikita

André Rømcke

6 years ago
> > I think the only thing worth mentioning where references are concerned is > that "$x =& $this->prop" is considered a write of $this->prop, not a read, > so it is subject to the write visibility. But once the reference is > acquired, visibility no longer factors into the behavior. > >
Ok, thanks! So instead of : "Attempting to pass a property value outside of allowed writable scope as a reference, results in an error." Instead maybe: "Assigning variable by reference is subject to write visibility, meaning ''$x =& $this->prop'' is considered a write of $this->prop, not a read" ?