rfc:locked-classes

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
rfc:locked-classes [2019/03/10 18:04] imsoprfc:locked-classes [2019/06/04 18:56] (current) – Withdrawn imsop
Line 1: Line 1:
 ====== PHP RFC: Locked Classes ====== ====== PHP RFC: Locked Classes ======
-  * Version: 0.+  * Version: 1.0 
-  * Date: 2019-03-06+  * Date: 2019-03-10
   * Author: Rowan Collins [IMSoP], rowan.collins@gmail.com   * Author: Rowan Collins [IMSoP], rowan.collins@gmail.com
-  * Status: Draft+  * Status: Withdrawn
   * First Published at: http://wiki.php.net/rfc/locked-classes   * First Published at: http://wiki.php.net/rfc/locked-classes
  
Line 10: Line 10:
 Object properties in PHP are primarily defined in class definitions; however, they can also be added to or removed from individual instances at any time. This RFC proposes an opt-in method for a class definition to disable this behaviour for all instances of that class. Object properties in PHP are primarily defined in class definitions; however, they can also be added to or removed from individual instances at any time. This RFC proposes an opt-in method for a class definition to disable this behaviour for all instances of that class.
  
-While setting properties which were not declared in a class definition, or unsetting properties which were declared, can be a useful tool, like many of PHP's dynamic features, it also makes certain mistakes easier. For instance, if a class defines a property ''public $normalise=true;'', and a user writes ''$instance->normalize=false;'', a new property will be silently added. Static analysis can highlight this mistake, but the language itself issues no warning.+While setting properties which were not declared in a class definition, or unsetting properties which were declared, can be a useful tool, like many of PHP's dynamic features, it also makes certain mistakes easier. For instance, if a class defines a property ''public $normalise=true;'', and a user writes ''%%$instance->normalize=false;%%'', a new property will be silently added. Static analysis can highlight this mistake, but the language itself issues no warning.
  
 Changing this behaviour for all objects would be a significant change to the language, with the potential to break a large amount of existing code. However, code written with no intention of using this dynamic behaviour would benefit from a way to switch it off. Changing this behaviour for all objects would be a significant change to the language, with the potential to break a large amount of existing code. However, code written with no intention of using this dynamic behaviour would benefit from a way to switch it off.
 +
 +While this can be achieved through strategic use of the ''%%__set%%'', ''%%__get%%'', and ''%%__unset%%'' magic methods, this is long-winded, hard to optimise, and interferes with other uses of those methods.
  
 ===== Proposal ===== ===== Proposal =====
Line 64: Line 66:
 ===== Naming ===== ===== Naming =====
  
-Newer versions of ECMAScript / JavaScript have [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal|similar functionality, under the name "sealed objects"]]. However, the term "sealed class" has an unrelated meaning in languages such as Kotlin and C#, to do with limiting the class's participation in inheritance.+Newer versions of ECMAScript / JavaScript have [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal|similar functionality, under the name "sealed objects"]]. However, the term "sealed class" has an unrelated meaning in languages such as C# and Kotlin, to do with limiting the class's participation in inheritance.
  
 Since the proposed modifier applies to a class, not an instance, it would be confusing to use the keyword "sealed" in this sense, so the synonym "locked" has been chosen instead. Since the proposed modifier applies to a class, not an instance, it would be confusing to use the keyword "sealed" in this sense, so the synonym "locked" has been chosen instead.
 +
 +The name "strict" was also considered, but this could mean a variety of things, and might mislead users into thinking other types of "strictness" will apply to the class.
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
-No internal classesor classes in unmodified codewill change behaviour.+The keyword "locked" will become "semi-reserved"on the same list as "final" and "abstract"
 + 
 +No existing code will change behaviour, since the modifier must be explicitly added.
  
 Since it is a new keyword, it will not be possible to "polyfill" this functionality, or declare a "locked class" in code which needs to be compatible with earlier PHP versions. Since it is a new keyword, it will not be possible to "polyfill" this functionality, or declare a "locked class" in code which needs to be compatible with earlier PHP versions.
Line 79: Line 85:
  
 ==== To Opcache ==== ==== To Opcache ====
 +
 To be determined: are there any optimisations which interact with the behaviours being changed? To be determined: are there any optimisations which interact with the behaviours being changed?
  
Line 85: Line 92:
 The following additions will be made to expose the new flag via reflection: The following additions will be made to expose the new flag via reflection:
  
-* New constant ReflectionClass::IS_LOCKED to expose the bit flag used for locked classes +  * New constant ReflectionClass::IS_LOCKED to expose the bit flag used for locked classes 
-* The return value of ReflectionClass::getModifiers() will have this bit set if the class being reflected is locked +  * The return value of ReflectionClass::getModifiers() will have this bit set if the class being reflected is locked 
-* Reflection::getModifierNames() will include the string "locked" if this bit is set +  * Reflection::getModifierNames() will include the string "locked" if this bit is set 
-* A new ReflectionClass::isLocked() method will allow directly checking if a class is locked+  * A new ReflectionClass::isLocked() method will allow directly checking if a class is locked
  
 ===== Unaffected PHP Functionality ===== ===== Unaffected PHP Functionality =====
 +Calling ''unset'' on a typed property will still succeed, and result in an "undefined property" on next access, unless the class is also marked ''locked''.
  
 ===== Future Scope ===== ===== Future Scope =====
-TODO+Classes defined by extensions could be marked "locked" if it was considered beneficial. This RFC does not propose any such changes.
  
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
Line 100: Line 107:
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
-https://github.com/php/php-src/compare/PHP-7.4...IMSoP:rfc-locked-classes+A pull request containing an initial implementation with basic tests is available on github: https://github.com/php/php-src/pull/3931 
 + 
 +===== Reasons for Withdrawal ===== 
 + 
 +This RFC was discussed on the Internals list in March 2019See archive here: https://externals.io/message/104620 
 + 
 +While the reasoning behind the feature was welcomed, the general consensus was that it was not quite right in its current form.
  
-===== Implementation ===== +Specific concerns raised included:
-TODOAfter the project is implemented, this section should contain  +
-  - the version(s) it was merged into +
-  - a link to the git commit(s) +
-  - a link to the PHP manual entry for the feature +
-  - a link to the language specification section (if any)+
  
-===== References ===== +  * The handling of ''%%__set%%'', ''%%__get%%'', and ''%%__unset%%'' on locked classes is unnecessarily complicated. Perhaps these should just be prohibited instead. 
-TODO+  * The restriction on ''unset()'' prevents clearing of reference bindings set with ''$foo->bar =& $baz''. One possibility is to reset the property to ''null'', but not remove it from the list of properties on the object. 
 +  * Contrary to both of the above suggestions, there was interest in using locked classes with "lazy-loading", where properties are deliberately removed to trigger ''%%__unset%%''
 +  * The extra keyword was considered "ugly" by some. While subjective, this does point to a lack of long-term vision: if nearly all PHP classes should behave this way, requiring the boilerplate on each declaration is cumbersome. 
 +  * If the primary purpose is to prevent //accidental// use of dynamic properties, the flag would be better set where the object is //used// (e.g. with a ''declare()'' flag) rather than where it is //defined//. That way, use of third-party objects can be subject to strict checking without modifying shared code; and deliberate "trap doors" can be used to implement tricks that rely on dynamic properties.
  
-===== Rejected Features ===== +The feature proposed in this RFC was deliberately conservative to keep the implementation simple, and bring it to users as soon as possible. However, it may be sensible to revisit the idea in combination with other concepts, such as packages/namespace-scoped declares, and a roadmap for making strictness the default.
-None yet+
rfc/locked-classes.1552241088.txt.gz · Last modified: 2019/03/10 18:04 by imsop