rfc:property_write_visibility

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
rfc:property_write_visibility [2020/06/25 18:56]
andrerom created
rfc:property_write_visibility [2020/07/07 07:33] (current)
andrerom
Line 1: Line 1:
-====== PHP RFC: Property write visibility ====== +====== PHP RFC: Property write/set visibility ====== 
-  * Version: 0.3.1 +  * Version: 0.4.6 
-  * Date: 2020-06-25+  * Date: 2020-06-29
   * Author: André Rømcke <andre.romcke+php@gmail.com>   * Author: André Rømcke <andre.romcke+php@gmail.com>
-  * Proposed PHP version: PHP 8.0 +  * Proposed PHP version: PHP 8.1 
-  * Status: Draft+  * Status: Under Discussion
   * First Published at: http://wiki.php.net/rfc/readonly_and_immutable_properties   * First Published at: http://wiki.php.net/rfc/readonly_and_immutable_properties
-  * Discussion: https://externals.io/message/ //coming//+  * Discussion: https://externals.io/message/110768
  
 ===== Introduction ===== ===== Introduction =====
  
-With the introduction of typed properties in PHP 7.4, properties have become far more powerful. However it is currently __not__ possible to specify disconnected write vs read visibility for properties, such as for readonly, writeonly or immutable //like// semantic, without having to resort to magic methods (getters and setters). This requires unnecessary boilerplate, makes usage less ergonomic and hurts performance.+With the introduction of [[rfc:typed_properties_v2|typed properties]] in PHP 7.4, properties have become far more powerful. However there are still common scenarios where you'll need to use magic methods or methods for properties in order to deal with disconnected write/set vs read/get visibility for properties, like readonly, writeonly or immutable //like// semantic. This requires unnecessary boilerplate, makes usage less ergonomic and in the case of magic methods hurts performance.
  
-This RFC resolves this issue by proposing to allow classes to optionally declare property write visibility, disconnected from read visibility.+This RFC resolves this issue by proposing to allow classes to optionally declare property write/set visibility, disconnected from read/get visibility.
  
  
-Under this RFC, code like+Under this RFC, code like the following using magic methods:
  
 <code php> <code php>
 +/**
 + * @property-read int $id
 + * @property-read string $name
 + */
 class User { class User {
     private int $id;     private int $id;
Line 57: Line 61:
 </code> </code>
  
-might be written as+might be written as //(Language syntax Option A)//:
  
 <code php> <code php>
Line 70: Line 74:
 } }
 </code> </code>
 +
 +
 +or //(Language Syntax Option B)//:
 +
 +<code php>
 +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;
 +    }
 +}
 +</code>
 +
  
  
Line 75: Line 95:
 ===== Main differences to previous proposals ===== ===== Main differences to previous proposals =====
  
-This RFC is inspired by past RFCs and the discussions that happened around them. Especially credit to [[rfc:readonly_properties|Readonly properties]] by Andrea Faulds, and discussions that happened around the different flavors of [[rfc:propertygetsetsyntax-v1.2|Property Accessors Syntax]].+This RFC is inspired by what was proposed on internals mailing list in "RFC Proposal - Attributes read/write visibility" by Amaury Bouchardon 15 July 2012. And the syntax and semantics found in [[https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID18|Swift]]
 + 
 +In both cases the purpose is to provide for a wider set of use cases.
  
 ==== Readonly ==== ==== Readonly ====
  
-This RFC allows for among others semantics proposed in [[rfc:readonly_properties|Readonly properties]] (2014, Withdrawn), by using ''public:protected''.+This RFC allows for among others semantics proposed in [[rfc:readonly_properties|Readonly properties]] (2014, Withdrawn), by setting read/get as ''public'' and write/set as ''protected''.
  
 +This RFC does however **not** introduce any native readonly keyword/attribute which would be more readable, however this provides the underlying language concepts needed for introducing a Readonly attribute later.
  
 ==== Immutability ==== ==== Immutability ====
  
-This RFC allows for use cases **similar** to what was proposed in [[rfc:immutability|Immutability]] (2018, Stale), by using for instance ''public:private''.+This RFC allows to //simulate// what was proposed in [[rfc:immutability|Immutability]] (2018, Stale), by setting read/get as ''public'' and write/set as ''private''. 
 + 
 +This RFC does however **not** introduce any immutable knowhow in the language which JIT can optimize for, however the features here can be built upon for a native immutable keyword/attribute in the future. 
 + 
 + 
 +==== Write once ====
  
 This RFC does __not__ align with the semantics of the recent [[rfc:write_once_properties|Write once properties]] (2020, Declined), which is targeting a different problem. This RFC does __not__ align with the semantics of the recent [[rfc:write_once_properties|Write once properties]] (2020, Declined), which is targeting a different problem.
Line 91: Line 119:
 ==== Property Accessors Syntax ==== ==== Property Accessors Syntax ====
  
-This RFC does not try to solve as wide use case as the different iterations of [[rfc:propertygetsetsyntax-v1.2|Property Accessors Syntax]] does.+This RFC does not try to solve as wide use case as the different iterations of [[rfc:propertygetsetsyntax-v1.2|Property Accessors Syntax]] (2012, Declined) does.
  
-However Accessors overcomplicates uses cases which only cares about having disconnected read and write visibility, and introduces completely different semantics to properties //(a change from C#'s "Fields" to "Properties")//.+However what being proposed here is aligned to make sure Accessors can cleanly be added later.
  
  
 ===== Proposal ===== ===== Proposal =====
  
-This proposal adds support for enforced write visibility checks for declared properties. The following example illustrates the basic syntax:+This proposal adds support for enforced write/set visibility checks for declared properties.  
 + 
 +==== Language syntax A: "public:private" ==== 
 + 
 +The following example illustrates the basic syntax:
  
 <code php> <code php>
Line 109: Line 141:
          
     // Property is write-only in public and protected scope     // Property is write-only in public and protected scope
-    private:public string $newName;+    private:public string $newPassword;
  
     public function __construct(int $id, string $name) {     public function __construct(int $id, string $name) {
Line 118: Line 150:
 </code> </code>
  
-The format is ''<read_visibility>:<write_visibility>'', and if you omit the last visibility value you will like before implicit set both read and write visibility at once.+The format is ''<get_visibility>:<set_visibility>'', and if you omit the last visibility value you will like before implicit set both read and write visibility at once.
  
-==== References ====+==== Language syntax B: "private(set)" ====
  
 +The following example illustrates the basic syntax:
 +
 +<code php>
 +class User {
 +    // Property is readonly in protected and public scope
 +    public private(set) int $id;
 +
 +    // Property is readonly in public scope
 +    public protected(set) string $name;
 +    
 +    // Property is write-only in public and protected scope
 +    private public(set) string $newPassword;
 +
 +    public function __construct(int $id, string $name) {
 +        $this->id = $id;
 +        $this->name = $name;
 +    }
 +}
 +</code>
 +
 +The format in this option is taken from [[https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID18|Swift]], and is perhaps more readable in terms of intent. It also aligns nicely with vocabulary of future Accessors proposal.
 +
 +Like in the other syntax proposal, if set visibility is not specified, as before the global visibility will define both read/get and write/set visibility.
 +
 +==== References ====
  
 Attempting to pass a property value outside of allowed writable scope as a reference, results in an error. Attempting to pass a property value outside of allowed writable scope as a reference, results in an error.
Line 128: Line 185:
 ==== Reflection ==== ==== Reflection ====
  
-When using reflection, methods such as ''ReflectionProperty::setAccessible()'' will work as before, it will implicit set visibility for both read and write.+When using reflection, methods such as ''ReflectionProperty::setAccessible()'' will work as before, it will implicit set visibility for both read/get and write/set.
  
-However with this proposal the following existing methods will represent only read visibility for cases where it differs: ''ReflectionProperty::isPrivate()'', ''ReflectionProperty::isProtected()'' and ''ReflectionProperty::isPublic()''.+In order to avoid backwards compatibility issue, the following methods will get updated behavior:
  
 +  * ''ReflectionProperty::isPrivate()'' - Checks if property is private **for get and set visibility, or one of them**
 +  * ''ReflectionProperty::isProtected()'' - Checks if property is protected **for get and set visibility, or one of them with the remaining being public**
 +  * ''ReflectionProperty::isPublic()'' - Checks if property is public **for get and set visibility**
  
-AS such the following methods should be added:+The following methods needs to be added to detect different read vs write visibility
  
-''ReflectionProperty::isWritePrivate()'' — Checks if property is writable in private +  * ''ReflectionProperty::isSetPrivate()'' Checks if property is writable in private 
-''ReflectionProperty::isWriteProtected()'' — Checks if property is writable in protected +  ''ReflectionProperty::isSetProtected()'' Checks if property is writable in protected 
-''ReflectionProperty::isWritePublic()'' — Checks if property is writable in public +  ''ReflectionProperty::isSetPublic()'' Checks if property is writable in public
-- ''ReflectionProperty::setReadAccessible()'' — Set property read accessibility +
-- ''ReflectionProperty::setWriteAccessible()'' — Set property write accessibility+
  
 +  * ''ReflectionProperty::isGetPrivate()'' - Checks if property is readable in private
 +  * ''ReflectionProperty::isGetProtected()'' - Checks if property is readable in protected
 +  * ''ReflectionProperty::isGetPublic()'' - Checks if property is readable in public
  
-//TODO: ''Reflection::getModifiers()'' and ''Reflection::getModifierNames()'' will need adaption too, for latter method whats most natural is that it returns format as written in language syntax.//+ 
 + 
 +//TODO: ''Reflection::getModifiers()'' and ''Reflection::getModifierNames()'' will need adaption too//
  
  
Line 149: Line 212:
 ==== Language Syntax ==== ==== Language Syntax ====
  
-The format being proposed here ''<read_visibility>:<write_visibility>'' is open for discussion if for instance this causes problems for parseror is deemed otherwise in-practical. A possible other alternative is to simply allow two separate visibility keywords on properties, where second one will denote write visibility.+The format options being proposed here are open for discussion, and additional proposal can be made on internals list.
  
 +Plain "public private $var" was on purpose skipped as it is less readable and could easily cause issues for ''Reflection::getModifierNames()''.
  
-==== Reflection API ====+==== Why not a Readonly keyword/attribute ====
  
-One clear alternative for this part of the proposal is to deprecate todays ''isPublic()'', ''isProtected()'' and ''isPrivate()'' methods. And introduce new methods both for write and read If so the following methods should also be added:+Several comments are pointing out that ''readonly'', ''writeonly''''immutable'' keywords/attributes would be more readable, and this is trueHowever what is proposed here is made in such a way that for instance a ''Readonly'' attribute can be introduced more easily in the future.
  
-- ''ReflectionProperty::isReadPrivate()'' — Checks if property is readable in private +AS in, if the language don't have a concept for write/set property visibility, then we'll end up with having to introduce reflection api that are tied in to the keyword/attribute introduced instead, as opposed to the underlying concept.
-''ReflectionProperty::isReadProtected()'' — Checks if property is readable in protected +
-''ReflectionProperty::isReadPublic()'' — Checks if property is readable in public+
  
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-Code that expects to be able to make properties writeable via reflection will in some cases have to adapt, once code is start to take advantage of this new feature. +Code that acts on Reflection to check for visibility, //should// be adapted to take advantage of the more fine grained read or write visibility check methods.
- +
-While ''ReflectionProperty::setAccessible()'' will still work like before, checks using ''isPublic()''', 'isProtected()'' or ''isPrivate()'' won't detect if class has other visibility for write.+
  
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
  
-Next PHP version, 8.suggested.+Next PHP version, 8.suggested.
  
  
 ===== Impact on extensions ===== ===== Impact on extensions =====
  
-More of future extension code, and possible SPL code, can be written in PHP instead. This is in-line with other features already accepted for PHP 8.0.+More future extension code, and possible SPL code, can be written in PHP instead. This is in-line with other features already accepted for PHP 8.0.
  
-Besides that existing PHP extensions working with visibility on properties will need adaptions.+Besides that existing PHP extensions working with visibility on properties will need to be adapted.
  
  
Line 192: Line 252:
 ===== References ===== ===== References =====
  
-  * [[https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#readonly-fields|C# readonly fields]], semantically similar to what is proposed as "immutable" here.+  * [[https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID18|Swift setter access level]] 
 +  * [[https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#readonly-fields|C# readonly fields]], semantically similar to what is referred to as "immutable" here.
   * [[https://docs.rs/readonly/0.1.6/readonly/|Rust readonly create]]   * [[https://docs.rs/readonly/0.1.6/readonly/|Rust readonly create]]
  
Line 205: Line 266:
 Significant changes to the RFC are noted here. Significant changes to the RFC are noted here.
  
 +
 +  * 2020-06-29 Adapt for initial feedback, add syntax proposal aligned with Swift
 +  * 2020-06-28 Simplify Reflection API proposal, add syntax alternatives for discussion
   * 2020-06-25 Focus on write visibility proposal   * 2020-06-25 Focus on write visibility proposal
   * 2020-06-20 Initial early draft to get feedback on direction between visibility, readonly/immutable keywords or attributes   * 2020-06-20 Initial early draft to get feedback on direction between visibility, readonly/immutable keywords or attributes
rfc/property_write_visibility.1593111409.txt.gz · Last modified: 2020/06/25 18:56 by andrerom