rfc:write_once_properties

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
rfc:write_once_properties [2020/03/03 08:29] kocsismaterfc:write_once_properties [2020/03/17 11:40] kocsismate
Line 1: Line 1:
 ====== PHP RFC: Write-Once Properties ====== ====== PHP RFC: Write-Once Properties ======
-  * Version: 0.1+  * Version: 1.0
   * Date: 2020-02-18   * Date: 2020-02-18
-  * Author: Máté Kocsis<kocsismate@php.net>+  * Author: Máté Kocsis <kocsismate@php.net>
   * Target Version: PHP 8.0   * Target Version: PHP 8.0
-  * Status: Under Discussion+  * Status: Voting
   * Implementation: https://github.com/php/php-src/pull/5186   * Implementation: https://github.com/php/php-src/pull/5186
  
Line 11: Line 11:
  
 ===== Proposal ===== ===== Proposal =====
-"Write-once" properties in PHP (the actual keyword is to be decided) could be initialized either by an explicit default value, or by an assignment operation. Unlike to ''final'' properties in Java, this RFC proposes to allow the initialization of object properties after object construction. The main purpose of choosing this approach is to make lazy loading possible - which is an important aspect for many PHP applications. In addition to object properties, class properties can also use the modifier in question with the same rules. 
  
-As soon as initialization is done, any other attempt to assign a value to "write-once" properties results in an exception. Besides assignment, the increment, decrement, and unset operations are also forbidden. As arrays are an immutable data structure in PHP, any attempt to mutate a property of array type (adding/removing/changing items) is forbidden. However, properties of object or resource types still remain mutable internally (see example below). In order to avoid possible problems, references on "write-once" properties are forwidden as well.+==== Run-time behaviour ==== 
 + 
 +"Write-once" properties in PHP (the actual keyword is to be decided) could be initialized by an assignment operation. Contrary to how ''final'' properties in Java work, this RFC proposes to allow the initialization of object properties after object construction. The main purpose of choosing this approach is to make lazy loading possible - which is an important aspect for many PHP applications. In addition to object properties, class properties can also use the modifier in question with the same rules. 
 + 
 +As soon as initialization is done, any other attempt to assign a value to "write-once" properties results in an exception. Besides assignment, the increment, decrement, and unset operations are also forbidden. As arrays are an immutable data structure in PHP, any attempt to mutate a property of array type (adding/removing/changing items) is forbidden. However, properties that have an object type still remain mutable internally (see example below). In order to avoid possible problems, references on "write-once" properties are forbidden as well.
  
 <code php> <code php>
Line 19: Line 22:
 class Foo class Foo
 { {
-    <keyword> public int $a = 1+    <keyword> public int $a; 
-    <keyword> public string $b; +    <keyword> public array $b; 
-    <keyword> public array $c = ["foo"]; +    <keyword> public object $c;
-    <keyword> public object $d;+
  
     public function __construct()     public function __construct()
     {     {
-        $this->b = "foo";+        $this->a = 1; 
 +        $this->b = ["foo"];
     }     }
 } }
Line 33: Line 36:
  
 $foo->a = 2; // EXCEPTION: property a has already been initialized $foo->a = 2; // EXCEPTION: property a has already been initialized
-$foo->b = "bar"; // EXCEPTION: property b has already been initialized 
 $foo->a++; // EXCEPTION: incrementing/decrementing is forbidden $foo->a++; // EXCEPTION: incrementing/decrementing is forbidden
-unset($foo->c); // EXCEPTION: unsetting is forbidden +unset($foo->b); // EXCEPTION: unsetting is forbidden 
-$foo->c[] = "bar"; // EXCEPTION: array values can't be modified +$foo->b[] = "bar"; // EXCEPTION: array values can't be modified 
-next($foo->c); // EXCEPTION: internal pointer of arrays can't be modified as well +next($foo->b); // EXCEPTION: internal pointer of arrays can't be modified as well 
-$var = &$this->c; // EXCEPTION: reference isn't allowed +$var = &$this->b; // EXCEPTION: reference isn't allowed 
-$this->c; = &$var; // EXCEPTION: reference isn't allowed+$this->b; = &$var; // EXCEPTION: reference isn't allowed
  
-key($foo->c); // SUCCESS: internal pointer of arrays is possible to read +key($foo->b); // SUCCESS: internal pointer of arrays is possible to read 
-$foo->= new Foo(); // SUCCESS: property hasn't been initialized before +$foo->= new stdClass(); // SUCCESS: property hasn't been initialized before 
-$foo->d->foo = "foo"; // SUCCESS: objects are still mutable internally+$foo->c->foo = "foo"; // SUCCESS: objects are still mutable internally
  
 </code> </code>
- 
-As untyped properties have an implicit default value (''null'') in the absense of an explicit one, their usefulness would be very limited. In order to avoid the introduction of unintiutive workarounds, this RFC proposes to disable the property modifier in question for them. Contrarily to untyped properties, typed properties are in uninitialized state by default, so they play well with the write-once semantics. 
  
 Furthermore, cloning objects with "write-once" properties is supported, in which case properties remain in the state (initialized/uninitialized) they were before. In other words, once a "write-once" property is initialized, it can't be changed after cloning. If you still want to modify them, you have to create a new object. Although this behaviour might be inconvenient in some cases, I decided not to address the problem in the current RFC in order to reduce its scope. The solution could be to add support for either object initializers or property mutation **during** the ''clone'' operation. As these features are neither a prerequisite for "write-once" properties, nor a trivial problem, it's better to properly discuss the question on its own. Furthermore, cloning objects with "write-once" properties is supported, in which case properties remain in the state (initialized/uninitialized) they were before. In other words, once a "write-once" property is initialized, it can't be changed after cloning. If you still want to modify them, you have to create a new object. Although this behaviour might be inconvenient in some cases, I decided not to address the problem in the current RFC in order to reduce its scope. The solution could be to add support for either object initializers or property mutation **during** the ''clone'' operation. As these features are neither a prerequisite for "write-once" properties, nor a trivial problem, it's better to properly discuss the question on its own.
  
-At last, I'm proposing to add a new method for ''ReflectionProperty'' with which it would be possible to retrieve if a property has the modifier in question. Depending on the keyword choice, I'd suggest using the ''isImmutable()'', ''isLocked()'', ''isReadonly()'', or ''isWriteonce()'' method names.+==== Compile-type restrictions ==== 
 + 
 +As untyped properties have an implicit default value (''null'') in the absense of an explicit one, their usefulness would be very limited. In order to avoid the introduction of unintiutive workarounds, this RFC proposes to disable the property modifier in question for them. Contrarily to untyped properties, typed properties are in uninitialized state by default (meaning, they don't have a value yet), so they play well with the write-once semantics. 
 + 
 +This choice has a slightly inconvenient implication of not being able to use "write-once" properties together with resources - since PHP doesn't have the ''resource'' type declaration. Currently, a possible workaround is to wrap resources in objects, but another way to solve the issue could be provided from PHP's side by adding support for a ''mixed'' type. 
 + 
 +Another restriction of "write-once" properties is that they can't have a default value. Thus, the following syntax is forbidden: 
 + 
 +<code php> 
 + 
 +class Foo { 
 +    <keyword> public int $a = 0; 
 +
 + 
 +</code> 
 + 
 +Instead, property ''$a'' should be initialized via an assignment either in the constructor or somewhere else. The purpose of this restriction is to avoid offering two syntaxes for declaring class constants as well as keeping our freedom to add new features to PHP that would otherwise have the possibility to interfere with the semantics of default values of "write-once" properties. 
 + 
 + 
 +Furthermore, the introduction of "write-once" properties impose slight changes to property variance validation. Namely, "write-once" properties must not override regular properties because the parent class expects them to be mutable. That's why the following example results in a compilation error: 
 + 
 +<code php> 
 + 
 +class Foo { 
 +    public int $a; 
 +
 + 
 +class Bar extends Foo { 
 +    <keyword> public int $a; 
 +
 + 
 +</code> 
 + 
 +However, regular properties can override "write-once" properties like below: 
 + 
 +<code php> 
 + 
 +class Foo { 
 +    <keyword> public int $a; 
 +
 + 
 +class Bar extends Foo { 
 +    public int $a; 
 +
 + 
 +</code> 
 + 
 +==== Serialization ==== 
 + 
 +"Write-once" properties can be serialized just like other properties. However, a new rule will apply to them: malformed serialized data which sets a "write-once" property multiple times throws an exception. 
 + 
 +==== Reflection ==== 
 + 
 +At last, I'm proposing to add a new ''ReflectionProperty'' method with which it would be possible to retrieve if a property has the modifier in question. Depending on the keyword choice, I'd suggest using the ''isImmutable()'', ''isLocked()'', ''isReadonly()'', or ''isWriteonce()'' method names.
  
 ===== Alternative Approaches ===== ===== Alternative Approaches =====
Line 71: Line 124:
   * ''sealed'': This keyword affects inheritance rules in other languages (e.g. in C#), thus it is also not a good candidate in our case   * ''sealed'': This keyword affects inheritance rules in other languages (e.g. in C#), thus it is also not a good candidate in our case
   * ''immutable'': It's a descriptive name which sounds well, although it's also a little bit misleading since its usage with mutable data structures (objects, resources) are not restricted in any way   * ''immutable'': It's a descriptive name which sounds well, although it's also a little bit misleading since its usage with mutable data structures (objects, resources) are not restricted in any way
-  * ''locked'': Although ''locked'' sounds well, it'little bit vague term that doesn't tell much about the feature. But at least it's not misleading.+  * ''locked'': Although ''locked'' sounds well, it's little bit vague term that doesn't tell much about the feature. But at least it's not misleading.
   * ''writeonce'': It's the most technically accurate name, however, it sounds exotic, and it might be confusing from the end-user perspective, as they are generally not expected to write these properties   * ''writeonce'': It's the most technically accurate name, however, it sounds exotic, and it might be confusing from the end-user perspective, as they are generally not expected to write these properties
   * ''readonly'':  Although it's not as technically accurate as "writeonce", it represent the feature perfectly for end-users who are expected to only read these properties. C# also uses the same term for a similar purpose.   * ''readonly'':  Although it's not as technically accurate as "writeonce", it represent the feature perfectly for end-users who are expected to only read these properties. C# also uses the same term for a similar purpose.
  
-Considering the above, ''immutable'', ''locked'', ''writeonce'', and ''readonly'' is going to be proposed as voting choices of the decision about the keyword.+Considering the above, ''immutable'', ''locked'', ''writeonce'', and ''readonly'' are going to be proposed as voting choices of the decision about the keyword.
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 81: Line 134:
  
 ===== Future Scope ===== ===== Future Scope =====
-Adding support for "write-once" properties would lay the groundwork for immutable objects - for which I'm going to create a proposal should the current RFC be accepted. I also plan to address the problem with cloning mentioned in the proposal section.+Adding support for "write-once" properties would lay the groundwork for immutable objects - for which I'm going to create a proposal should the current RFC be accepted. I also plan to address the problem with cloning mentioned in the "Proposal" section. 
 + 
 +Additionally, as mentioned in the "Compile-Type Restrictions" section, adding support for the ''mixed'' type would make it possible to use "write-once" properties together with resources. Besides this, we could allow the definition of default values later on as soon as we have a good use-case for them. 
 + 
 +Finally, "write-once" properties could in principle support covariance. That is, a subclass would be allowed to tighten the property type that is inherited from the parent class, while other properties must stay invariant. All this would be possible because of the quasi-immutable nature of "write-once" properties: they are generally expected to be assigned to only once, in the constructor - which is exempt from LSP checks. There is a gotcha though: in practice, "write-once" properties could be written from places other than the constructor. Although there might not be many practical use-cases for it, the infamous setter injection is certainly one (as shown at https://3v4l.org/DQ3To), in which case property covariance would be a problem. 
 + 
 +===== Vote ===== 
 + 
 +The vote starts on 2020-03-17 and ends on 2020-03-31. The primary vote requires 2/3, while the secondary one requires a simple majority to be accepted. 
 + 
 +<doodle title="Do you want to add support for write-once properties?" auth="kocsismate" voteType="single" closed="false"> 
 +   * Yes 
 +   * No 
 +</doodle> 
 + 
 +----
  
-===== Proposed Voting Choices ===== +<doodle title="Which keyword to use?" auth="kocsismate" voteType="single" closed="false"> 
-The primary vote ("Do you want to add support for the proposed property modifier?") requires 2/3 majority, while the secondary one ("Which keyword to use"?) requires a simple majority.+   * immutable 
 +   * locked 
 +   * writeonce 
 +   * readonly 
 +</doodle>
  
 ===== References ===== ===== References =====
 Prior RFC proposing ''immutable'' properties: https://wiki.php.net/rfc/immutability Prior RFC proposing ''immutable'' properties: https://wiki.php.net/rfc/immutability
  
rfc/write_once_properties.txt · Last modified: 2020/03/31 07:51 by kocsismate