rfc:propertygetsetsyntax-as-implemented
Differences
This shows you the differences between two versions of the page.
rfc:propertygetsetsyntax-as-implemented [2012/10/18 00:04] – old revision restored 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.1 | + | |
* Date: 2011-12-21 | * Date: 2011-12-21 | ||
- | * Updated: 2012-10-06 | + | * 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 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? ==== | + | * Fork: Available at github https:// |
- | 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 { $this-> | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | <code php> | ||
- | // Accessing the property is the same as accessing a class member | ||
- | $time = new TimePeriod(); | ||
- | $time-> | ||
- | echo $time-> | ||
- | </ | ||
- | |||
- | Note that " | ||
- | |||
- | ==== Overloading Properties ==== | ||
- | |||
- | Properties can be overloaded in extending classes. | ||
- | |||
- | <code php> | ||
- | class TimePeriod { | ||
- | protected $Seconds = 3600; | ||
- | |||
- | public $Hours { | ||
- | get { return $this-> | ||
- | set { $this-> | ||
- | } | ||
- | |||
- | // This property is read-only | ||
- | public $Minutes { | ||
- | get { return $this-> | ||
- | } | ||
- | |||
- | /* public getter, protected setter */ | ||
- | public $Milliseconds { | ||
- | get { return $this-> | ||
- | protected set { $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 read-write property instead of read-only | ||
- | set { $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 { | ||
- | // 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 { $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 { $this-> | ||
- | isset { return isset($this-> | ||
- | unset { unset($this-> | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | ==== Automatic Implementations ==== | ||
- | You may also use automatic implementations of property accessors by not defining a body to the accessor. | ||
- | |||
- | 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; | ||
- | set; | ||
- | 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 $this-> | ||
- | set { $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 { $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 { $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 ( $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 { 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 { 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: | ||
- | |||
- | ==== Read-Only And Write-Only Properties ==== | ||
- | Defining accessors with only a getter or only a setter will make them read only and write only respectively but this does not enforce anything with subclasses. | ||
- | |||
- | You could declare a get-only accessor final in order to enforce rules upon subclasses but this would also prevent any changes to that getter by a subclass, which may not be the desired outcome. | ||
- | |||
- | What may be needed instead is to allow a subclass to alter the getter but still enforce that the property must remain read-only. | ||
- | |||
- | === read-only keyword === | ||
- | //new keyword// | ||
- | |||
- | You can use the read-only keyword in the definition of the property to enforce the property as read only. No setter may be defined and an attempt to set the property results in a fatal error. | ||
- | |||
- | <code php> | ||
- | class TimePeriod { | ||
- | private $Seconds; | ||
- | |||
- | // This property has specified the read-only keyword and therefore is read-only | ||
- | public read-only $Hours { | ||
- | get { return $this-> | ||
- | // Setter may not be defined | ||
- | } | ||
- | } | ||
- | |||
- | // Results in Fatal Error | ||
- | |||
- | $o = new TimePeriod(); | ||
- | $o-> | ||
- | </ | ||
- | |||
- | === write-only keyword === | ||
- | //new keyword// | ||
- | |||
- | You can use the write-only keyword in the definition of the property to enforce the property as write only. No getter may be defined and an attempt to get the property results in a fatal error. | ||
- | |||
- | <code php> | ||
- | class TimePeriod { | ||
- | private $Seconds; | ||
- | |||
- | // This property has specified the write-only keyword and therefore is write only | ||
- | public write-only $Hours { | ||
- | // Getter may not be defined | ||
- | set { $this-> | ||
- | } | ||
- | } | ||
- | |||
- | // Results in Fatal Error | ||
- | |||
- | $o = new TimePeriod(); | ||
- | echo $o-> | ||
- | </ | ||
- | |||
- | ===Overriding read-only & write-only=== | ||
- | The read-only and write-only keywords are inherited and must be declared by child classes. | ||
- | |||
- | <code php> | ||
- | class TimePeriod { | ||
- | private $Seconds; | ||
- | |||
- | // This property has specified the read-only keyword and therefore is read-only | ||
- | public read-only $Hours { | ||
- | get { return $this-> | ||
- | // Setter may not be defined | ||
- | } | ||
- | } | ||
- | |||
- | class TimePeriod2 extends TimePeriod { | ||
- | /* Results in fatal error, $Hours must be declared as read-only, | ||
- | * as in parent which would further disallow the set {} from be declared. */ | ||
- | public $Hours { | ||
- | get { return $this-> | ||
- | set { $this-> | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | ==== 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; | ||
- | 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 { $this-> | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | ==== Reflection ==== | ||
- | |||
- | === Changes === | ||
- | |||
- | * ReflectionClass:: | ||
- | * ReflectionClass:: | ||
- | * ReflectionClass:: | ||
- | |||
- | === Additions === | ||
- | |||
- | == ReflectionPropertyAccessor | ||
- | |||
- | The new class has all of the same functions as the ReflectionProperty as well as: | ||
- | |||
- | * getGetter(): | ||
- | * getSetter(): | ||
- | * getIssetter(): | ||
- | * getUnsetter(): | ||
- | * isReadOnly(): | ||
- | * isWriteOnly(): | ||
- | |||
- | All other functions have been updated to return appropriate values and/or provide appropriate actions for an accessor. | ||
- | |||
- | == ReflectionMethod class == | ||
- | |||
- | * isAccessor(): | ||
- | * isAutoImplemented(): | ||
- | |||
- | 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 implemented as ordinary functions (with appropriate access levels) with specialized names. | ||
- | |||
- | 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. | ||
- | |||
- | ==== Safety Checks ==== | ||
- | |||
- | **read-only and write-only keywords** | ||
- | |||
- | These keywords may not be specified multiple times, nor may they be used with regular properties (non-accessor) or methods. | ||
- | |||
- | ===== 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/ | ||
- | - 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. |
rfc/propertygetsetsyntax-as-implemented.1350518678.txt.gz · Last modified: 2017/09/22 13:28 (external edit)