rfc:propertygetsetsyntax-v1.1
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
Previous revisionNext revision | |||
— | rfc:propertygetsetsyntax-v1.1 [2012/12/30 18:46] – cpriest | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Request for Comments: Property accessors syntax - As Implemented ====== | ||
+ | * Version: 1.1 | ||
+ | * Main Organizational Document: [[https:// | ||
+ | * Date: 2011-12-21 | ||
+ | * Updated: 2012-12-30 | ||
+ | * Author: Clint Priest <phpdev at zerocue dot com> | ||
+ | * Status: Old revision, see main organizational document for current proposal. | ||
+ | ==== Fork ==== | ||
+ | * The changes for this RFC are available here: https:// | ||
+ | |||
+ | |||
+ | ==== Introduction ==== | ||
+ | This document describes the accessor syntax As Implemented. | ||
+ | |||
+ | ==== 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 { $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-v1.1.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1