rfc:propertygetsetsyntax-as-implemented
Differences
This shows you the differences between two versions of the page.
rfc:propertygetsetsyntax-as-implemented [2012/10/12 05:29] – [Overloading Properties] cpriest | rfc:propertygetsetsyntax-as-implemented [2017/09/22 13:28] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Request for Comments: Property | + | ====== Request for Comments: Property |
- | * Version: 1.2 | + | |
* Date: 2011-12-21 | * Date: 2011-12-21 | ||
- | * Updated: 2012-10-12 | + | * Updated: 2012-12-30 |
* Author: Clint Priest <phpdev at zerocue dot com> | * Author: Clint Priest <phpdev at zerocue dot com> | ||
- | * Status: Ready for Review & Discussion, Code Mostly Complete | + | |
+ | | ||
- | ==== Fork ==== | + | **This document is here for organizational purposes, please see the links below for current and past proposals.** |
- | | + | |
- | ==== Introduction | + | ===== Links ===== |
- | This document describes the accessor syntax As Implemented. | + | |
+ | * [[rfc/ | ||
+ | * Version 1.2 Document: [[https:// | ||
+ | * [[https:// | ||
- | ==== What Are Properties? ==== | + | |
- | + | ||
- | Properties provide a clean, easy to understand and unified syntax for get/set accessors. | + | |
- | + | ||
- | ===== Syntax ===== | + | |
- | + | ||
- | ==== Basic Syntax ==== | + | |
- | + | ||
- | This is the property syntax for accessors as implemented. | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | private $Seconds; | + | |
- | + | ||
- | // Accessor properties are implemented just like you would define an actual property | + | |
- | public $Hours { | + | |
- | get() { return $this-> | + | |
- | + | ||
- | // In the set accessor, the variable $value holds the incoming value to be " | + | |
- | set($value) { $this-> | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | <code php> | + | |
- | // Accessing the property is the same as accessing a class member | + | |
- | $time = new TimePeriod(); | + | |
- | $time-> | + | |
- | echo $time-> | + | |
- | </ | + | |
- | + | ||
- | Note that " | + | |
- | + | ||
- | === Type Hinting === | + | |
- | Type hinting for the set() accessor will be allowed, for example: | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | private $Seconds; | + | |
- | + | ||
- | // Accessor properties are implemented just like you would define an actual property | + | |
- | public $Hours { | + | |
- | get() { return $this-> | + | |
- | + | ||
- | // In the set accessor, the variable $value holds the incoming value to be " | + | |
- | set(TestClass $value) { $this-> | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | </ | + | |
- | + | ||
- | ==== Overloading Properties ==== | + | |
- | + | ||
- | Properties can be overloaded in extending classes. | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | protected $Seconds = 3600; | + | |
- | + | ||
- | public $Hours { | + | |
- | get() { return $this-> | + | |
- | set($value) { $this-> | + | |
- | } | + | |
- | + | ||
- | // This property is only readable | + | |
- | public $Minutes { | + | |
- | get() { return $this-> | + | |
- | } | + | |
- | + | ||
- | /* public getter, protected setter */ | + | |
- | public $Milliseconds { | + | |
- | get() { return $this-> | + | |
- | protected set($value) { $this-> | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | class HalfTimePeriod extends TimePeriod { | + | |
- | /* Overload getter, inherit setter */ | + | |
- | public $Hours { | + | |
- | get() { return ($this-> | + | |
- | + | ||
- | /* The base setter method will be inherited */ | + | |
- | } | + | |
- | + | ||
- | public $Minutes { | + | |
- | // A set method is added, turning this property into a readable and writable property instead of only readable | + | |
- | set($value) { $this-> | + | |
- | } | + | |
- | + | ||
- | public $Milliseconds { | + | |
- | // A property method can have its visibility increased in a child class, just like regular PHP methods | + | |
- | // This method is now public instead of protected | + | |
- | public set($value) { | + | |
- | // You can access a base class property explicitly, just like accessing a base class member or method | + | |
- | parent:: | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Note that if parent:: scope access would be overloaded, resolution priorities follow: | + | |
- | - Check for parent non-static accessor, call that if defined. | + | |
- | - Check for parent static accessor, call that if defined. | + | |
- | - Check for parent static property, use that value if defined. | + | |
- | + | ||
- | This does not conflict with existing functionality which is presently only #3. | + | |
- | + | ||
- | ==== Asymmetric Accessor Accessibility ==== | + | |
- | Properties can have different levels of visibility for the get and set methods. | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | private $Seconds = 3600; | + | |
- | + | ||
- | public $Hours { | + | |
- | get() { return $this-> | + | |
- | protected set($value) { $this-> | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | <code php> | + | |
- | $o = new TimePeriod(); | + | |
- | echo $o-> | + | |
- | $o-> | + | |
- | </ | + | |
- | + | ||
- | In the above example the getter inherits the public access level of the property definition. | + | |
- | + | ||
- | ==== isset / unset ==== | + | |
- | To facilitate complete functionality with accessors it is necessary to provide accessor functions to act on isset() and unset() calls. | + | |
- | + | ||
- | <code php> | + | |
- | + | ||
- | class TimePeriod { | + | |
- | private $Seconds = 3600; | + | |
- | + | ||
- | public $Hours { | + | |
- | get() { return $this-> | + | |
- | set($value) { $this-> | + | |
- | isset() { return isset($this-> | + | |
- | unset() { unset($this-> | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | ==== Automatic Implementations ==== | + | |
- | isset and unset automatic implementations | + | |
- | + | ||
- | The isset automatic implementation tests for the property to be non-null. | + | |
- | The unset automatic implementation sets the property to be null. (See php equivalent below) | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | // Accessor properties are implemented just like you would define an actual property | + | |
- | public $Hours { | + | |
- | get() { return 45; } | + | |
- | set($value) { $this-> | + | |
- | isset(); | + | |
- | unset(); | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Translates to this: | + | |
- | <code php> | + | |
- | class TimePeriod { | + | |
- | // Accessor properties are implemented just like you would define an actual property | + | |
- | protected $__Hours; | + | |
- | public $Hours { | + | |
- | get() { return 45; } | + | |
- | set($value) { $this-> | + | |
- | isset() { return $this-> | + | |
- | unset() { $this-> | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Note that isset/unset implementations will always be provided (where appropriate) if they are not defined or if they are explicitly auto-defined (as above). | + | |
- | + | ||
- | "Where appropriate" | + | |
- | + | ||
- | Lastly, isset/unset may be explicitly defined as indicated in prior sections, the "will always be provided" | + | |
- | + | ||
- | ==== Final Properties ==== | + | |
- | Properties declared final are not allowed to be overloaded in a child class, just like final methods. | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | private $Seconds; | + | |
- | + | ||
- | public final $Hours { | + | |
- | get() { return $this-> | + | |
- | set($value) { $this-> | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | class HalfTimePeriod extends TimePeriod { | + | |
- | private $Seconds; | + | |
- | + | ||
- | // This attempt to overload the property " | + | |
- | public $Hours { | + | |
- | get() { return ($this-> | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | **__Final property methods__** | + | |
- | + | ||
- | The get or set method of a property can be declared " | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | private $Seconds; | + | |
- | + | ||
- | // Notice there is no " | + | |
- | public $Hours { | + | |
- | final get() { return $this-> | + | |
- | set($value) { $this-> | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | class HalfTimePeriod extends TimePeriod { | + | |
- | private $Seconds; | + | |
- | + | ||
- | public $Hours { | + | |
- | // This attempt to overload the get method of the " | + | |
- | // because it was declared final in the base class | + | |
- | get() { return ($this-> | + | |
- | + | ||
- | // This would be accepted | + | |
- | set($value) ( $this-> | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | ==== Static Properties ==== | + | |
- | Static properties act identically to regular properties, except in a static context. | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | private static $Seconds; | + | |
- | + | ||
- | public static $Hours { | + | |
- | get() { return self:: | + | |
- | set($value) { self:: | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Accessing a static property is the same as accessing a static class member | + | |
- | + | ||
- | <code php> | + | |
- | TimePeriod:: | + | |
- | echo TimePeriod:: | + | |
- | </ | + | |
- | + | ||
- | Parent accessors may also be used | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod2 extends TimePeriod { | + | |
- | public static $Hours { | + | |
- | get() { return parent:: | + | |
- | set($value) { parent:: | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | ==== References ==== | + | |
- | Functions such as sort() require a reference to the underlying data storage value in order to modify them, in these cases you can place the & before the get to indicate the returning of a reference variable. | + | |
- | + | ||
- | <code php> | + | |
- | + | ||
- | class SampleClass { | + | |
- | private $_dataArray = array(1, | + | |
- | + | ||
- | public $dataArray { | + | |
- | &get() { return $this-> | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | $o = new SampleClass(); | + | |
- | sort($o-> | + | |
- | /* $o-> | + | |
- | </ | + | |
- | + | ||
- | ==== Operators ==== | + | |
- | The following operators have tests written for them and work as though it were any other variable. | + | |
- | + | ||
- | The following operators have code tests written already: | + | |
- | + | ||
- | ==== Controlling access to subclasses ==== | + | |
- | Rather than the originally proposed read-only and write-only new keywords, people were more in favor of defining a final property, for example: | + | |
- | + | ||
- | <code php> | + | |
- | class TimePeriod { | + | |
- | private $Seconds; | + | |
- | + | ||
- | // This property is only readable to those outside the class | + | |
- | public $Hours { | + | |
- | get() { return $this-> | + | |
- | private final set($value) { $this-> | + | |
- | } | + | |
- | } | + | |
- | // A subclass which attempts to re-define a final setter (as above) would be met with a compile error. | + | |
- | + | ||
- | // Results in scope permission violation | + | |
- | + | ||
- | $o = new TimePeriod(); | + | |
- | $o-> | + | |
- | </ | + | |
- | + | ||
- | + | ||
- | ==== Interface Properties ==== | + | |
- | + | ||
- | Interfaces may define property declarations, | + | |
- | + | ||
- | When a class implements an interface that defines a getter, it can add in a set method to turn the property into a read-write property. | + | |
- | + | ||
- | <code php> | + | |
- | interface iSampleInterface { | + | |
- | public $MyProperty { | + | |
- | get(); | + | |
- | set($value); | + | |
- | isset(); | + | |
- | unset(); | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | ==== Traits ==== | + | |
- | Accessors work as expected with traits including automatic accessor properties. | + | |
- | + | ||
- | <code php> | + | |
- | trait SampleTrait { | + | |
- | private $Seconds = 3600; | + | |
- | + | ||
- | public $Hours { | + | |
- | get() { return $this-> | + | |
- | set($value) { $this-> | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | ==== Error Messaging ==== | + | |
- | All messaging that would refer to a function call will be translated to show access to a property. | + | |
- | + | ||
- | + | ||
- | ==== Reflection ==== | + | |
- | + | ||
- | === Changes === | + | |
- | + | ||
- | * ReflectionClass:: | + | |
- | * ReflectionClass:: | + | |
- | + | ||
- | === Additions === | + | |
- | + | ||
- | == ReflectionPropertyAccessor | + | |
- | + | ||
- | The new class has all of the same functions as the ReflectionProperty as well as: | + | |
- | + | ||
- | * getGetter(): | + | |
- | * getSetter(): | + | |
- | * getIssetter(): | + | |
- | * getUnsetter(): | + | |
- | + | ||
- | All other functions have been updated to return appropriate values and/or provide appropriate actions for an accessor. | + | |
- | + | ||
- | == ReflectionMethod class == | + | |
- | + | ||
- | * isAccessor(): | + | |
- | + | ||
- | A fairly extensive test-suite has been created to test the functionality as well. | + | |
- | + | ||
- | ==== Backward Compatibility ==== | + | |
- | There are no known backward compatibility issues. | + | |
- | + | ||
- | ===== Implementation ===== | + | |
- | + | ||
- | Accessor information is stored in a new zend_accessor_info struct. | + | |
- | + | ||
- | <code c> | + | |
- | typedef struct _zend_accessor_info { | + | |
- | zend_uint flags; | + | |
- | const char *doc_comment; | + | |
- | int doc_comment_len; | + | |
- | zend_function *getter; | + | |
- | zend_function *setter; | + | |
- | zend_function *isset; | + | |
- | zend_function *unset; | + | |
- | } zend_accessor_info; | + | |
- | </ | + | |
- | + | ||
- | Internally the accessors are still ordinary functions with appropriate access levels but are not accessible by directly calling their internal names, they are only accessible by way of property syntax (echo $o-> | + | |
- | + | ||
- | Two new function flags have been defined: | + | |
- | <code c> | + | |
- | #define ZEND_ACC_READONLY | + | |
- | #define ZEND_ACC_WRITEONLY | + | |
- | </ | + | |
- | + | ||
- | An additional byte was added to zend_internal_function: | + | |
- | <code c> | + | |
- | zend_uchar purpose; | + | |
- | </ | + | |
- | + | ||
- | This was in lieu of using 4 additional flag values for which there was not room. There are presently five states purpose can be in, they are: | + | |
- | <code c> | + | |
- | #define ZEND_FNP_UNDEFINED 0 / | + | |
- | #define ZEND_FNP_PROP_GETTER 1 / | + | |
- | #define ZEND_FNP_PROP_SETTER 2 / | + | |
- | #define ZEND_FNP_PROP_ISSETTER 3 / | + | |
- | #define ZEND_FNP_PROP_UNSETTER 4 / | + | |
- | </ | + | |
- | + | ||
- | * %%__get()%%, | + | |
- | + | ||
- | * Error producing lines have been modified to check the function for ZEND_ACC_IS_ACCESSOR mask with more appropriate error report occurring. | + | |
- | + | ||
- | ==== Static Accessors ==== | + | |
- | There was no built-in mechanism to handle custom get/ | + | |
- | + | ||
- | This yielded the possibility that a getter call was being made while it should not be allowed (if there was no getter defined) and so pass_two() was changed to look for these non-backpatched illegal static getter calls and a compile time error is produced. | + | |
- | + | ||
- | ===== Tests ===== | + | |
- | * 2011-12-21 : 21 Test Cases Created | + | |
- | * 2012-10-06 : Numerous additional test cases created, 79 in total now. | + | |
- | + | ||
- | ===== References ===== | + | |
- | * [[https:// | + | |
- | + | ||
- | ===== Changelog ===== | + | |
- | - 2011-12-21 Clint Priest: "As Implemented" | + | |
- | - 2012-03-28 Clint Priest: Accessors being called where no get/set would be handled by the original __get()/ | + | |
- | - 2012-03-28 Clint Priest: Auto-backing fields are now protected rather than public | + | |
- | - 2012-03-30 Clint Priest: git fork at https:// | + | |
- | - 2012-03-31 Clint Priest: Cleaned up read-only, write-only and over-riding to be in line with original RFC, they now are inherited and immutable, child accessors must define read-only/write-only as parent has done. | + | |
- | - 2012-10-06 Clint Priest: Added isset/unset accessors, static and object based. | + | |
- | - 2012-10-07 Clint Priest: Reorganized document sections for easier, cumulative concept conception. | + | |
- | - 2012-10-11 Clint Priest: After much discussion on the internals list, the following changes were desired: | + | |
- | - Eliminate the ability for an accessor to be called via $o-> | + | |
- | - Change syntax to use public set($value) {}, public get(), etc. Along with that means no " | + | |
- | - If possible, allow type hinting in the definition | + | |
- | - Eliminate automatically implemented get; set;, no automatic backing field creation will occur. | + | |
- | - read-only / write-only keywords will be eliminated, the use of the word final on a setter/ | + | |
- | - Exceptions thrown from an accessor reveal underlying implementation details, this is to be avoided, checking debug_backtrace() information as well | + | |
rfc/propertygetsetsyntax-as-implemented.1350019771.txt.gz · Last modified: 2017/09/22 13:28 (external edit)