rfc:friend-classes
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:friend-classes [2016/01/07 18:46] – mdwheele | rfc:friend-classes [2018/07/14 12:52] (current) – vote end mdwheele | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Class Friendship ====== | ====== PHP RFC: Class Friendship ====== | ||
- | * Version: 0.1 | + | * Version: |
- | * Date: 2015-12-10 | + | * Date: 2017-09-21 |
* Author: Dustin Wheeler, mdwheele@ncsu.edu | * Author: Dustin Wheeler, mdwheele@ncsu.edu | ||
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | Class Friendship allows a class to be better encapsulated by granting per-class access to protected | + | Class Friendship allows a class to be better encapsulated by granting per-class access to protected |
- | A common use-case for friendship here is separating presentation concerns from what would otherwise be a "tell don't ask" behavioral unit or write model. This can be applied in a variety of scenarios. Without this feature, developers are left with a trade-off of marking internal | + | A common use-case for friendship here is separating presentation concerns from what would otherwise be a "tell don't ask" behavioral unit or write model. This can be applied in a variety of scenarios. Without this feature, developers are left with a trade-off of marking internal |
- | The purpose of the feature should not be conflated | + | The purpose of the feature should not be conflated |
===== Proposal ===== | ===== Proposal ===== | ||
- | Support for class friendship is added through a new keyword, '' | + | Support for class friendship is added through a new keyword, '' |
==== Basic Usage ==== | ==== Basic Usage ==== | ||
- | A subject class may declare another class a friend through the use of a new '' | + | A subject class may declare another class a friend through the use of a new '' |
- | C++ implements Class Friendship such that friends have access to both private and protected | + | C++ implements Class Friendship such that friends have access to both private and protected |
Below, a class '' | Below, a class '' | ||
Line 59: | Line 59: | ||
{ | { | ||
// HumanResourceReport would not have access to protected | // HumanResourceReport would not have access to protected | ||
- | // properties | + | // members |
return $this-> | return $this-> | ||
} | } | ||
Line 132: | Line 132: | ||
</ | </ | ||
- | Characterization Tests are a form of white-box test useful for characterizing the **current actual** behaviour of a unit given knowledge of that unit's internals. They are usually a tactical measure used to verify that modifications made to a system to not have unintended or undesirable changes in **how** the system works. These tests are useful to initiate a refactoring loop. Friend designations are also a good marker for follow-up work to improve units. This may possibly eliminate the need for white-box tests after improving the behavioural API of the system under test. | + | Characterization Tests are a form of white-box test useful for characterizing the **current actual** behaviour of a unit given knowledge of that unit's internals. They are usually a tactical measure used to verify that modifications made to a system to not have unintended or undesirable changes in **how** the system |
Currently, in many examples, we have to either change visibility of properties that only exist for implementation, | Currently, in many examples, we have to either change visibility of properties that only exist for implementation, | ||
Line 168: | Line 168: | ||
$b = new B(); | $b = new B(); | ||
- | $b-> | + | $b-> |
- | $a-> | + | $a-> |
</ | </ | ||
Line 237: | Line 237: | ||
=== Access due to friendship is inherited === | === Access due to friendship is inherited === | ||
- | A friend of '' | + | A friend of '' |
<code php> | <code php> | ||
Line 264: | Line 264: | ||
class Friendly | class Friendly | ||
{ | { | ||
- | public function touch(Derived $derived) | + | public function touch(Derived $instance) |
{ | { | ||
- | var_dump($derived-> | + | var_dump($instance-> |
| | ||
- | var_dump($derived-> | + | var_dump($instance-> |
// While Friendly is not a friend of Base, it can still access this | // While Friendly is not a friend of Base, it can still access this | ||
// property because it is accessible to Derived through protected | // property because it is accessible to Derived through protected | ||
- | // property. | + | // property. |
- | + | ||
- | var_dump($derived-> | + | |
- | // While Friendly is not a friend of Base, it can still access this | + | |
- | // property because it is accessible to Derived through protected | + | |
- | // method. | + | |
- | var_dump($derived-> | + | var_dump($instance-> |
} | } | ||
} | } | ||
Line 291: | Line 286: | ||
==== Errors ==== | ==== Errors ==== | ||
- | In all cases above, error messages received are no different than if an object attempted to read or write private or protected | + | In all cases above, error messages received are no different than if an object attempted to read or write private or protected |
==== Additional Thoughts ==== | ==== Additional Thoughts ==== | ||
I have purposely kept this RFC fairly slim for a number of reasons. First and foremost, I want to make it clear that I do **not** see this feature in competition with any other RFC or suggestion for limited-visibility collaborators. Rather, I see it as a feature used in concert with something like namespace visibility or package-privacy. I feel that class friendship is about object modeling and making explicit privileged relationships between two or more classes. It is a form of tighter coupling to achieve better encapsulation of behaviour. | I have purposely kept this RFC fairly slim for a number of reasons. First and foremost, I want to make it clear that I do **not** see this feature in competition with any other RFC or suggestion for limited-visibility collaborators. Rather, I see it as a feature used in concert with something like namespace visibility or package-privacy. I feel that class friendship is about object modeling and making explicit privileged relationships between two or more classes. It is a form of tighter coupling to achieve better encapsulation of behaviour. | ||
- | Secondly, the RFC is purposefully (yet usefully) slim to "test the waters" | + | Secondly, the RFC is purposefully (yet usefully) slim to "test the waters" |
* Friendship to global functions | * Friendship to global functions | ||
Line 305: | Line 300: | ||
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
- | This proposal targets | + | I had intended to target |
===== RFC Impact ===== | ===== RFC Impact ===== | ||
==== To Opcache ==== | ==== To Opcache ==== | ||
This is an open issue pending code review. I am unfamiliar-enough with Opcache implementation to be able to appropriately assess impact. | This is an open issue pending code review. I am unfamiliar-enough with Opcache implementation to be able to appropriately assess impact. | ||
- | |||
- | ==== To Reflection API ==== | ||
- | New methods are added to '' | ||
- | |||
- | - '' | ||
- | - '' | ||
- | |||
- | ===== Open Issues ===== | ||
- | ==== Policy ==== | ||
- | * Implementation requires code review to advise on improvements as well as inform that opcache was appropriately considered | ||
- | * Verify current PHP functionality around class property visibility is undisturbed | ||
- | |||
- | ==== Reflection API ==== | ||
- | * Implement new methods on '' | ||
===== Future Scope ===== | ===== Future Scope ===== | ||
While this RFC specifies friendship between classes, there is opportunity to extend this implementation and syntax to include progressive enhancements. Snippets below are hypothetical implementations, | While this RFC specifies friendship between classes, there is opportunity to extend this implementation and syntax to include progressive enhancements. Snippets below are hypothetical implementations, | ||
- | ==== Friendship to global functions | + | * **Friendship to global functions**\\ A class may declare a global function as friend. This might be used if someone wanted to expose a procedural interface to an existing object model or begin to refactor a procedural model to become a façade over a new object model. Modeling best-practice aside, it functions much like standard class friendship. |
- | A class may declare a global function as friend. This might be used if someone wanted to expose a procedural interface to an existing object model or begin to refactor a procedural model to become a façade over a new object model. Modeling best-practice aside, it functions much like standard class friendship: | + | * **Friendship to class methods**\\ Friendship to class methods is a narrower expression of standard class friendship. Instead of declaring the entire class a friend, we declare that a method from a friended class can access the subject' |
+ | * **Friendship to namespace(s)**\\ A class might declare an entire namespace as friend. In this way, any class that is part of that namespace would be friended. | ||
- | <code php> | ||
- | class Person | ||
- | { | ||
- | friend fullname; // Syntax is debatable as part of separate RFC. Idea is same. | ||
- | |||
- | protected $first_name = ' | ||
- | protected $last_name = ' | ||
- | } | ||
- | |||
- | function fullname(Person $instance) | ||
- | { | ||
- | return $instance-> | ||
- | } | ||
- | |||
- | $alice = new Person(); | ||
- | var_dump(fullname($alice)); | ||
- | </ | ||
- | |||
- | ==== Friendship to class methods ==== | ||
- | Friendship to class methods is a narrower expression of standard class friendship. Instead of declaring the entire class a friend, we declare that a method from a friend class can access the subject' | ||
- | |||
- | <code php> | ||
- | class Person | ||
- | { | ||
- | friend Report:: | ||
- | |||
- | protected $first_name = ' | ||
- | protected $last_name = ' | ||
- | } | ||
- | |||
- | class Report | ||
- | { | ||
- | private $person; | ||
- | |||
- | public function __construct(Person $instance) | ||
- | { | ||
- | $this-> | ||
- | } | ||
- | | ||
- | public function getFullName() | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | | ||
- | public function getIdentifier() | ||
- | { | ||
- | return " | ||
- | } | ||
- | } | ||
- | |||
- | $report = new Report(new Person()); | ||
- | |||
- | var_dump($report-> | ||
- | var_dump($report-> | ||
- | </ | ||
- | |||
- | ==== Friendship to namespace(s), | ||
- | A class might declare an entire namespace as friends. In this way, any class that is part of that namespace would be friended. | ||
- | |||
- | ==== Combinations of Class, Class Method and Namespace Friendship ==== | ||
- | Theoretically, | ||
- | |||
- | <code php> | ||
- | namespace Vendor\Internals { | ||
- | class Person | ||
- | { | ||
- | friend __NAMESPACE__, | ||
- | | ||
- | protected $property = ' | ||
- | } | ||
- | | ||
- | class ImplementationDetail | ||
- | { | ||
- | public function touch(Person $instance) | ||
- | { | ||
- | return $instance-> | ||
- | } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | <code php> | ||
- | class SomeOtherClass | ||
- | { | ||
- | public function touch(Person $instance) | ||
- | { | ||
- | return $instance-> | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | <code php> | ||
- | function some_function(Person $instance) | ||
- | { | ||
- | return $instance-> | ||
- | } | ||
- | </ | ||
- | |||
- | <code php> | ||
- | $person = new \Vendor\Internals\Person(); | ||
- | $detail = new \Vendor\Internals\ImplementationDetail(); | ||
- | $other = new \SomeOtherClass(); | ||
- | |||
- | var_dump($detail-> | ||
- | var_dump($other-> | ||
- | var_dump(some_function($person)); | ||
- | </ | ||
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
- | This RFC proposes two voting | + | As this RFC adds syntax to the language, a 2/3 majority is required. (see [[voting]]) |
- | | + | Voting starts on 2018-07-06 21:00 UTC and closes on 2018-07-13 21:00 UTC. |
- | | + | |
- | As this is a language change, a 2/3 majority is required for whether to add Friend Classes to PHP. (see [[voting]]) | + | <doodle title=" |
+ | * Yes | ||
+ | * No | ||
+ | </doodle> | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | I have implemented the RFC as described with tests to verify all usage examples above. | + | I have implemented the RFC as described with tests to verify all usage examples above. |
+ | |||
+ | https:// | ||
As this is my first contribution to PHP, it is my opinion that my request should be placed under higher scrutiny and I am completely ready and willing to accept all feedback to improve implementation. | As this is my first contribution to PHP, it is my opinion that my request should be placed under higher scrutiny and I am completely ready and willing to accept all feedback to improve implementation. | ||
Line 463: | Line 342: | ||
===== Changelog ===== | ===== Changelog ===== | ||
* v0.1 - Created | * v0.1 - Created | ||
+ | * v0.2 - Copy-editing. Clarifications. Add more code examples. | ||
+ | * v0.2.1 - Fix copy+paste error from ReflectionClass documentation regarding trait methods. | ||
+ | * v0.2.2 - Remove voting choice on implementation detail. Remove example of combined future scope syntax. Correct lacking clarity that friendship applies to all protected members, not just properties. | ||
+ | * v1.0.0 - Final draft of RFC before re-opening discussion |
rfc/friend-classes.1452192380.txt.gz · Last modified: 2017/09/22 13:28 (external edit)