rfc:propertygetsetsyntax-as-implemented

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

rfc:propertygetsetsyntax-as-implemented [2012/10/18 00:04] – old revision restored cpriestrfc:propertygetsetsyntax-as-implemented [2017/09/22 13:28] (current) – external edit 127.0.0.1
Line 1: Line 1:
-====== Request for Comments: Property accessors syntax As Implemented ====== +====== Request for Comments: Property Accessors Organizational Document ======
-  * 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+  * Contributors: Nikita Popov 
 +  * Status: Ready for Review & Discussion
  
-==== Fork ==== +**This document is here for organizational purposes, please see the links below for current and past proposals.**
-  The changes for this RFC are available here: https://github.com/cpriest/php-src+
  
-==== Introduction ==== +===== Links ===== 
-This document describes the accessor syntax As Implemented.  The RFC which the implementation was crafted from is located here: https://wiki.php.net/rfc/propertygetsetsyntax+  * Version 1.1 Document: [[https://wiki.php.net/rfc/propertygetsetsyntax-v1.1|v1.1 RFC]] 
 +    * [[rfc/propertygetsetsyntax-as-implemented/change-requests|v1.1 -> v1.2 Changes]] 
 +  * Version 1.2 Document[[https://wiki.php.net/rfc/propertygetsetsyntax-v1.2|v1.2 RFC]] 
 +  * [[https://wiki.php.net/rfc/propertygetsetsyntax-alternative-typehinting-syntax|Alternative typehinting syntax proposal]]
  
-==== What Are Properties? ====+  * Fork: Available at github https://github.com/cpriest/php-src/tree/accessors
  
-Properties provide a clean, easy to understand and unified syntax for get/set accessors.  They allow incoming and outgoing requests (gets and sets) from a class member to be run through a method first.  This method can perform validation or a transformation, or update other areas of the class.  Properties do not even have to be associated with a class member, and can generate their own data on-the-fly. 
- 
-===== 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->Seconds / 3600; } 
-         
-        // In the set accessor, the variable $value holds the incoming value to be "set"  
-        set { $this->Seconds = $value * 3600; }  
-    } 
-} 
-</code> 
- 
-<code php> 
-// Accessing the property is the same as accessing a class member 
-$time = new TimePeriod(); 
-$time->Hours = 12;  // Stored as 43200 
-echo $time->Hours;  // Outputs 12 
-</code> 
- 
-Note that "get" and "set" were not made to be new keywords, they are processed as strings by the parser. 
- 
-==== Overloading Properties ==== 
- 
-Properties can be overloaded in extending classes.  An overloaded property can replace an existing get or set declaration without touching the other, replace both the get and set declarations, or add an omitted get or set declaration turning the property into a read-write property.  Additionally, a property may have its visibility increased through overloading.  Get or set declarations cannot be removed or hidden by the child class in any way. 
- 
-<code php> 
-class TimePeriod { 
-    protected $Seconds = 3600; 
- 
-    public $Hours { 
-        get { return $this->Seconds / 3600; } 
-        set { $this->Seconds = $value * 3600; } 
-    } 
- 
-    // This property is read-only 
-    public $Minutes { 
-        get { return $this->Seconds / 60; } 
-    } 
- 
-    /* public getter, protected setter */ 
-    public $Milliseconds { 
-        get { return $this->Seconds * 1000; } 
-        protected set { $this->Seconds = $value / 1000; } 
-    } 
-} 
- 
-class HalfTimePeriod extends TimePeriod { 
-    /* Overload getter, inherit setter */ 
-    public $Hours { 
-        get { return ($this->Seconds / 3600) / 2; } 
- 
-        /* 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->Seconds = $value * 60; } 
-    } 
- 
-    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::$Milliseconds = $value; 
-        } 
-    } 
-} 
-</code> 
- 
-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.  This is achieved by setting either the get or set method to a lower visibility than the property is set to. 
- 
-<code php> 
-class TimePeriod { 
-    private $Seconds = 3600; 
- 
-    public $Hours { 
-        get { return $this->Seconds / 3600; } 
-        protected set { $this->Seconds = $value * 3600; } 
-    } 
-} 
-</code> 
- 
-<code php> 
-$o = new TimePeriod(); 
-echo $o->Hours;    // Prints 1 
-$o->Hours = 12;    // Error, unable to set protected property 
-</code> 
- 
-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.  These operate just like their magic %%__isset() and __unset()%% functions but are definable within the accessor block. 
- 
-<code php> 
- 
-class TimePeriod { 
-    private $Seconds = 3600; 
- 
-    public $Hours { 
-        get { return $this->Seconds / 3600; } 
-        set { $this->Seconds = $value; } 
-        isset { return isset($this->Seconds); } 
-        unset { unset($this->Seconds); } 
-    } 
-} 
-</code> 
- 
-==== Automatic Implementations ==== 
-You may also use automatic implementations of property accessors by not defining a body to the accessor.  Doing so causes an automatic implementation to occur.  Automatic implementations create a protected backing field automatically, named the same as the property with a double underscore (%%__%%) preceding it.  The get and set implementations directly get and set from this automatic backing field.   
- 
-The isset automatic implementation tests for the property to be non-null.  (See php equivalent below) 
-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; 
-    } 
-} 
-</code> 
- 
-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->__Hours; } 
-        set { $this->__Hours = $value; }  
-        isset { return $this->Hours != NULL; } 
-        unset { $this->Hours = NULL; } 
-    } 
-} 
-</code> 
- 
-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" means that if you have only defined a getter then unset is not available, likewise if you have only defined a setter, then isset is not available. 
- 
-Lastly, isset/unset may be explicitly defined as indicated in prior sections, the "will always be provided" only applies to cases where they are not explicitly defined. 
- 
-==== 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->Seconds / 3600; } 
-        set { $this->Seconds = $value * 3600; } 
-    } 
-} 
- 
-class HalfTimePeriod extends TimePeriod { 
-    private $Seconds; 
- 
-    // This attempt to overload the property "Hours" will throw an error because it was declared final in the base class 
-    public $Hours { 
-        get { return ($this->Seconds / 3600) / 2; } 
-    } 
-} 
-</code> 
- 
-**__Final property methods__** 
- 
-The get or set method of a property can be declared "final" independently of each other.  This would allow for one of them to be overloaded, but not the other. 
- 
-<code php> 
-class TimePeriod { 
-    private $Seconds; 
- 
-    // Notice there is no "final" keyword on the property declaration 
-    public $Hours { 
-        final get { return $this->Seconds / 3600; }// Only the get method is declared final 
-        set { $this->Seconds = $value * 3600; } 
-    } 
-} 
- 
-class HalfTimePeriod extends TimePeriod { 
-    private $Seconds; 
- 
-    public $Hours { 
-        // This attempt to overload the get method of the "Hours" will throw an error 
-        // because it was declared final in the base class 
-        get { return ($this->Seconds / 3600) / 2; } 
-                                                    
-        // This would be accepted 
-        set ( $this->Seconds = ($value * 3600) * 2; ) 
-    } 
-} 
-</code> 
- 
-==== 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::$Seconds / 3600; } 
-        set { self::$Seconds = $value * 3600; } 
-    } 
-} 
-</code> 
- 
-Accessing a static property is the same as accessing a static class member 
- 
-<code php> 
-TimePeriod::$Hours = 12;  // Stored as 43200 
-echo TimePeriod::$Hours;  // Outputs 12 
-</code> 
- 
-Parent accessors may also be used 
- 
-<code php> 
-class TimePeriod2 extends TimePeriod { 
-    public static $Hours { 
-        get { return parent::$Seconds / 3600; } 
-        set { parent::$Seconds = $value * 3600; } 
-    } 
-} 
-</code> 
- 
-==== 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,2,5,3); 
-     
-    public $dataArray { 
-        &get { return $this->_dataArray; } 
-    } 
-} 
- 
-$o = new SampleClass(); 
-sort($o->dataArray); 
-/* $o->dataArray == array(1,2,3,5); */ 
-</code> 
- 
-==== Operators ==== 
-The following operators have tests written for them and work as though it were any other variable.  If the operator attempts to make a change to a property for which no setter is defined, it will produce an error such as "Cannot set property xxx, no setter defined."  If a setter is defined, then the assignment operator works as expected.   
- 
-The following operators have code tests written already:  Pre/Post Increment/Decrement, Negation, String Concatenation (.), +=, -=, *=, /=, &=, |=, +, -, *, /, %, &, |, &&, ||, xor, ~, ==, ===, !=, !==, >, <, >=, <=, .=, <<, >>, Array Union (array + array), instanceof 
- 
-==== 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.  That is the use case the following new keywords, read-only and write-only, address. 
- 
-=== 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->Seconds / 3600; } 
-        // Setter may not be defined  
-    } 
-} 
- 
-// Results in Fatal Error  
- 
-$o = new TimePeriod(); 
-$o->Hours = 4; 
-</code> 
- 
-=== 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->Seconds = $value * 3600; }  
-    } 
-} 
- 
-// Results in Fatal Error  
- 
-$o = new TimePeriod(); 
-echo $o->Hours; 
-</code> 
- 
-===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->Seconds / 3600; } 
-        // 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->Seconds / 3600; } 
-        set { $this->Seconds = $value; } 
-    } 
-} 
-</code> 
- 
-==== Interface Properties ==== 
- 
-Interfaces may define property declarations, without a body.  The purpose of this is to define properties that must exist in an implementing class, and may indicate if they are read-write, read-only, or write-only. 
- 
-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.  The inverse is also true for implementing an interface with a setter only.  This is because interfaces are designed to enforce what //should be// in a class, and not what //should not be// in a class. 
- 
-<code php> 
-interface iSampleInterface { 
-    public $MyProperty { 
-        get; 
-        set; 
-        isset; 
-        unset; 
-    } 
-} 
-</code> 
- 
-==== Traits ==== 
-Accessors work as expected with traits including automatic accessor properties.  You can use any feature with traits that you could with classes including asymmetrical access levels, read-only/write-only, isset, unset, etc. 
- 
-<code php> 
-trait SampleTrait { 
-    private $Seconds = 3600; 
-     
-    public $Hours { 
-        get { return $this->Seconds * 3600; } 
-        set { $this->Seconds = $value / 3600; } 
-    } 
-} 
-</code> 
- 
-==== Reflection ==== 
- 
-=== Changes === 
- 
-  * ReflectionClass::getProperties() will now return an array of ReflectionProperty and ReflectionPropertyAccessor classes. 
-  * ReflectionClass::getMethods() will not return accessor functions (hides implementation detail).   
-  * ReflectionClass::getProperty() will also return an appropriate class based on being a property or an accessor. **planned** 
- 
-=== Additions === 
- 
-== ReflectionPropertyAccessor  == 
- 
-The new class has all of the same functions as the ReflectionProperty as well as: 
- 
-  * getGetter(): Returns a ReflectionMethod object for the getter or false if no getter is defined. 
-  * getSetter(): Returns a ReflectionMethod object for the setter or false if no setter is defined. 
-  * getIssetter(): Returns a ReflectionMethod object for the isset accessor. **planned** 
-  * getUnsetter(): Returns a ReflectionMethod object for the unset accessor. **planned** 
-  * isReadOnly(): Returns true if the accessor is defined as read-only. 
-  * isWriteOnly(): Returns true if the accessor is defined as write-only. 
- 
-All other functions have been updated to return appropriate values and/or provide appropriate actions for an accessor. 
- 
-== ReflectionMethod class == 
- 
-  * isAccessor(): Returns true if the method is an accessor  **planned** 
-  * isAutoImplemented(): Returns true if the method is an accessor and was automatically implemented **planned** 
- 
-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.  These structures are stored in a new HashTable property in a zend_class_entry structure named accessors.  They are indexed by the hash_value of the property name and are thus quickly accessed during property resolution. 
- 
-<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; 
-</code> 
- 
-Internally the accessors are implemented as ordinary functions (with appropriate access levels) with specialized names.  get/set/isset/unset for a property named $Hours would be %%__getHours()%%, %%__setHours($value)%%, %%__issetHours()%% and %%__unsetHours()%% respectively. 
- 
-Two new function flags have been defined: 
-<code c> 
-#define ZEND_ACC_READONLY     0x20000000 
-#define ZEND_ACC_WRITEONLY    0x40000000 
-</code> 
- 
-An additional byte was added to zend_internal_function: 
-<code c> 
-zend_uchar purpose; 
-</code> 
- 
-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 /* No special purpose function */ 
-#define ZEND_FNP_PROP_GETTER 1 /* Special purpose accessor: getter */ 
-#define ZEND_FNP_PROP_SETTER 2 /* Special purpose accessor: setter */ 
-#define ZEND_FNP_PROP_ISSETTER 3 /* Special purpose accessor: issetter */ 
-#define ZEND_FNP_PROP_UNSETTER 4 /* Special purpose accessor: unsetter */ 
-</code> 
- 
- * %%__get()%%, %%__set()%%, %%__isset()%% and %%__unset()%% guards were used and the functionality is the same with the new accessors. 
- 
- * Error producing lines have been modified to check the function for ZEND_ACC_IS_ACCESSOR mask with more appropriate error report occurring.  For example: Cannot override final property getter TimePeriod::$Hours 
- 
-==== Static Accessors ==== 
-There was no built-in mechanism to handle custom get/set/isset/unset for static properties, these were handled by catching references to static properties, checking for the existence of a static accessor and converting the compilation into a function call.  When a static setter is being used, the compiled code first becomes a static getter call and the zend_do_assign backpatches the op_array to become a call to the setter, as appropriate. 
- 
-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://wiki.php.net/rfc/propertygetsetsyntax|Property Get/Set Syntax RFC by Dennis Robinson]] 
- 
-===== Changelog ===== 
-  - 2011-12-21 Clint Priest: "As Implemented" document created based in large part by original RFC 
-  - 2012-03-28 Clint Priest: Accessors being called where no get/set would be handled by the original __get()/__set() functions, this is no more.  If there is an accessor defined, the buck stops there for that variable name. 
-  - 2012-03-28 Clint Priest: Auto-backing fields are now protected rather than public 
-  - 2012-03-30 Clint Priest: git fork at https://github.com/cpriest/php-src 
-  - 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.  Added several dozen new tests.  Refactored the auto-generation of code to use php within such that an automatic implementation is compiled php from within the interpreter. 
-  - 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)