rfc:propertygetsetsyntax-as-implemented
Differences
This shows you the differences between two versions of the page.
rfc:propertygetsetsyntax-as-implemented [2012/10/12 05:00] – read-only -> private final set($x) 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? ==== | + | |
- | + | ||
- | 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: | + | |
- | + | ||
- | ==== Controlling access to subclassing ==== | + | |
- | 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 has specified the read-only keyword and therefore is read-only | + | |
- | public $Hours { | + | |
- | get { return $this-> | + | |
- | private final set($x) { ... } | + | |
- | // Setter may not be defined | + | |
- | } | + | |
- | } | + | |
- | // 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; | + | |
- | 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/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.1350018054.txt.gz · Last modified: 2017/09/22 13:28 (external edit)