rfc:horizontalreuse
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:horizontalreuse [2010/06/09 07:20] – updated status gron | rfc:horizontalreuse [2017/09/22 13:28] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Request for Comments: Horizontal Reuse for PHP ====== | ====== Request for Comments: Horizontal Reuse for PHP ====== | ||
- | * Version: 2.0 | + | * Version: 2.1.1 |
- | * Date: 2008-10-12 | + | * Date: 2008-10-12, last update 2011-01-09 |
* Author: Stefan Marr < | * Author: Stefan Marr < | ||
- | * Status: | + | * Status: |
* First Patch: http:// | * First Patch: http:// | ||
* First Published at: http:// | * First Published at: http:// | ||
Line 93: | Line 93: | ||
The following proposal will introduce both flavors of horizontal reuse and | The following proposal will introduce both flavors of horizontal reuse and | ||
compares them in the context of PHP. | compares them in the context of PHP. | ||
- | + | ====== Traits - Reuse of Behavior | |
- | ====== Traits - Reuse of Behavior ====== | + | |
//Traits// is a mechanism for code reuse in single inheritance languages such | //Traits// is a mechanism for code reuse in single inheritance languages such | ||
Line 293: | Line 292: | ||
</ | </ | ||
- | In case of the above definition of '' | + | In case of the above definition of '' |
- | have been conflicts | + | have been conflicts. It will name the methods '' |
- | as the reason of this conflict. | + | as the reason of this conflict. |
- | will be available in the class. | + | A fatal error is issued since the conflict indicates that an incompatible change was made to the code that potentially breaks |
- | Instead, the developer can exactly define which methods | + | To solve the conflict, the developer can exactly define which of the colliding |
- | conflict is resolved. | + | |
<code php> | <code php> | ||
Line 352: | Line 350: | ||
==== Traits Composed from Traits ==== | ==== Traits Composed from Traits ==== | ||
- | Not explicitly mentioned | + | Not explicitly mentioned |
composition of Traits from Traits. | composition of Traits from Traits. | ||
Since Traits are fully flattened away at compile time it is possible to use | Since Traits are fully flattened away at compile time it is possible to use | ||
Line 390: | Line 388: | ||
of the inheritance tree i.e., it is not possible to inherit from a Trait to | of the inheritance tree i.e., it is not possible to inherit from a Trait to | ||
avoid confusion and misuse of Traits. | avoid confusion and misuse of Traits. | ||
- | |||
==== Express Requirements by Abstract Methods ==== | ==== Express Requirements by Abstract Methods ==== | ||
Line 425: | Line 422: | ||
dynamic method resolution and property creation in the context of complex | dynamic method resolution and property creation in the context of complex | ||
projects for the sake of readability. | projects for the sake of readability. | ||
+ | |||
+ | |||
+ | ==== Static Variables ==== | ||
+ | |||
+ | The flattening property implies that all methods are independent from each other at their usage point, even so, they might origin from the same method/ | ||
+ | Imagine you would like to implement a counter by using a static variable and use that at various places. Each of the counters should be independent. | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | trait Counter { | ||
+ | public function inc() { | ||
+ | static $c = 0; | ||
+ | $c = $c + 1; | ||
+ | echo " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class C1 { | ||
+ | use Counter; | ||
+ | } | ||
+ | |||
+ | class C2 { | ||
+ | use Counter; | ||
+ | } | ||
+ | |||
+ | $o = new C1(); $o-> | ||
+ | $p = new C2(); $p-> | ||
+ | ?> | ||
+ | </ | ||
+ | |||
+ | ==== Static Methods ==== | ||
+ | |||
+ | Following the idea that traits are almost like compiler assisted copy' | ||
+ | This is useful for instance to provide a reusable implementation for the singleton pattern: | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | trait Singleton { | ||
+ | public static function getInstance() { ... } | ||
+ | } | ||
+ | |||
+ | class MySingleton extends SomeUnrelatedSuperClass { | ||
+ | use Singleton; | ||
+ | } | ||
+ | |||
+ | MySingleton:: | ||
+ | ?> | ||
+ | </ | ||
+ | |||
===== Traits Semantics Summarized ===== | ===== Traits Semantics Summarized ===== | ||
Line 442: | Line 488: | ||
===== Visibility ===== | ===== Visibility ===== | ||
- | |||
Visibility modifiers have not been discussed so far. Since Traits are meant as | Visibility modifiers have not been discussed so far. Since Traits are meant as | ||
Line 467: | Line 512: | ||
</ | </ | ||
- | The final modifier is supported, too. The static modifier | + | The final modifier is supported, too. The static modifier |
- | because it would change the methods semantics and references to '' | + | |
- | would break. | + | |
+ | ===== Handling of Properties/ | ||
+ | |||
+ | Traits do not provide any provisioning for handling state. | ||
+ | They are meant to provide a light-weight mechanism for flexible code reuse, | ||
+ | with the main goal being to avoid code duplication. | ||
+ | Moreover, traits should not be confused with typical use cases of classes. | ||
+ | When a strong coherence/ | ||
+ | and invariants have to be maintained on the state, this is a good | ||
+ | indication that a class is the right abstraction to implement that problem | ||
+ | with. | ||
+ | |||
+ | However, every behavior needs state to operate on, otherwise it could be just | ||
+ | a static functional helper method. | ||
+ | Thus, trait code will either need to use accessors, which is favorite way to | ||
+ | go since it provides full traits semantics, or they use properties, which | ||
+ | is possible but rather a convenience feature. | ||
+ | |||
+ | Since state is a complex problem, and the knowledge about compatibility of | ||
+ | state form different traits is only present in a concrete composition, | ||
+ | state handling | ||
+ | beyond the scope of what is necessary for PHP. (See [[http:// | ||
+ | |||
+ | Thus, the goal for a consistent language design is to raise awareness of the | ||
+ | problem, promote the use of accessors, and break early in case the changes to | ||
+ | a trait is potentially problematic for a class using it. This results in the | ||
+ | following rules: | ||
+ | |||
+ | - Properties are considered incompatible if they differ in their definition. This means, they differ in the applied modifiers (static, public, protected, private) or their initial value. | ||
+ | - Incompatible properties result in a fatal error. | ||
+ | - In all other cases, i.e., when the definitions are identical, an E_STRICT notice is shown to raise awareness about the potentially problematic, | ||
+ | - For those checks, all properties are treated equal. Properties from the base class and the composing class have to be compatible with properties from traits as well as the properties between all traits have to be compatible. | ||
+ | - Non-coliding properties, and properties which are not considered incompatible behave exactly the same as if they would have been defined in the composing class. | ||
+ | |||
+ | This property handling was implemented in [[http:// | ||
+ | |||
+ | |||
+ | ===== Reflection ===== | ||
+ | |||
+ | Status: The Reflection API is not completely adapted for traits yet. | ||
+ | |||
+ | The following functions are added to match the existing class- or interface-specific functions. | ||
+ | * trait_exists() | ||
+ | * get_declared_traits() | ||
- | ====== Grafts - Class Composition ====== | + | ====== Grafts - Class Composition |
A //Graft// is a class composed into another class to reuse it avoiding | A //Graft// is a class composed into another class to reuse it avoiding | ||
Line 915: | Line 1003: | ||
Renaming would imply that it would not be possible to use this or similar techniques with Traits. | Renaming would imply that it would not be possible to use this or similar techniques with Traits. | ||
At least, it would not be stable. It would be possible to hack around it, but these hacks would be brittle, and lead to additional maintenance work. | At least, it would not be stable. It would be possible to hack around it, but these hacks would be brittle, and lead to additional maintenance work. | ||
+ | |||
+ | |||
+ | ===== Requiring Composing Class to Implement Interface ===== | ||
+ | |||
+ | Traits are able to express required methods by using abstract method declarations. | ||
+ | An abstract method can be satisfied in varios ways, for instance by implementing it | ||
+ | in the composing class or by bringing it in from another Trait. | ||
+ | |||
+ | However, for traits that require complex interfaces to be satisfied, this approach is tedious | ||
+ | and fragile, since it requires the developer to state all used methods explicitly. | ||
+ | |||
+ | Another solution is that a Trait expresses a requirement for the composing class to implement | ||
+ | a certain interface. This is not entirely identical to using abstract methods, however. | ||
+ | First, it imposes a requirement on interface level and thus will have the same fragility | ||
+ | with respect to interface changes as all other clients of an interface. | ||
+ | On the other hand, it avoids duplications of abstract method definitions and makes | ||
+ | the interface the main entity of responsibility as for normal client-interface uses | ||
+ | in current code. | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | // IteratorUser works with $this using the Iterator interface | ||
+ | trait IteratorUser require Iterator { | ||
+ | function foo() { | ||
+ | $next = $this-> | ||
+ | ... | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // composed into a class that needs to implement the interface | ||
+ | class Example implements Iterator { | ||
+ | use IteratorUser; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | Note: brought up in the discussion of http:// | ||
===== Grafts ===== | ===== Grafts ===== | ||
Line 1147: | Line 1272: | ||
====== Changelog ====== | ====== Changelog ====== | ||
+ | |||
+ | 2010-11-18: Added clarification on static modifier in the context of aliasing/ | ||
2008-10-13: initial version for discussion | 2008-10-13: initial version for discussion |
rfc/horizontalreuse.1276068023.txt.gz · Last modified: 2017/09/22 13:28 (external edit)