Currently on PHP's internals - Property Accessors
My last article discussing the current status of PHP's internals was quite well read so it is time for another update. Today I will be discussing a feature that at this moment is called "Property Accessor". It is a method of defining getters and setters. Originally an RFC was defined as early as september 2009, but recently new discussion took place and an actual patch was created. There is no certainty this feature will ever make a PHP version but discussion seems to target implementation details and not the feature itself, so things are looking bright for this feature.
I took this specific topic on the internals list because it deals with something totally new and because it is well documented and discussed. What I would like to do is explain the new feature and hopefully start a discussion regarding the feature outside the internals list itself. I'd like to see what the userland thinks of this feature.
PHP''s pet. Every now and then an Elephpant image is required :)
I'll explain the feature and give some examples, but the RFC is very well written and I won't cover all of it. If you'd rather read the RFC itself, visit the original RFC here and an RFC for the current patch implementing properties here. Be sure to come back and post your feelings about it though.
This article uses syntax that corresponds to the second RFC, describing how the current patch is implemented.
What are properties?
If you're still here lets first establish what properties are (at least for the sake of the RFC):
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.
The way PHP currently solves this is using the magic __get and __set methods:
class Period { private $seconds; public function __set($name, $value) { switch ($name) { case "minutes": $this->seconds = $value / 60; break; case "hours": $this->seconds = $value / 3600; break; } } public function __get($name) { switch ($name) { case "minutes": return $this->seconds * 60; break; case "hours": return $this->seconds * 3600; break; } } }
It is easy to see how this would get cluttered and cumbersome with large classes. Apart from this maintanence hell it is also a possible way of breaking inheritance. If a child class overloads the __get and __set methods this can alter properties of the class which you would not expect from a child class.
The RFC we're dealing with today offers a different way of solving this problem:
class Period { private $seconds; public minutes { get { return $this->seconds / 60; } set { $this->seconds = $value * 60; } } public hours { get { return $this->seconds / 3600; } set { $this->seconds = $value * 3600; } } }
The code snippet above introduces a new concept called "property". The get and set keywords define the respective accessors. If you're wondering where $value came from, it is the variable that is incoming from the "set" call and is "magically" accessible. The syntax above would work like the magic __get and __set do, enabling the following code snippet to work:
$Period = new Period(); $Period->minutes = 10;// stored in $this->seconds as 600 $Period->hours = 10;// stored in $this->seconds as 36000 echo $Period->minutes;// outputs 10 echo $Period->hours;// outputs 10
Read-only and write-only accessors
Now that the basic syntac is clear I would like to look at properties that are either read-only or write-only. The second RFC dealing with the current actual implementation only provides a single way, using a keyword. The original RFC also provided an implicit way, I'll show both:
class Period { private $seconds; //implicit read-only public minutes { get { return $this->seconds / 60; } } //keyword read-only public read-only hours { get { return $this->seconds / 3600; } } }
Here I like the implicit method best, a property is simply read-only because there is no set keyword present. I also really dislike the - in the read-only keyword. It may be proper english, but it seems akward in code to me. Ofcourse the same rules that apply to the above read-only example apply to write-only properties as well.
Other features
As I already wrote, I won't be covering all of the RFC, I mainly hope to see a discussion starting here. I do want to name some other features the properties RFC will bolster however:
- Asymmetric Accessor Accessibility (i.e.: a protected get and a public set for a property)
- Interface properties (Interfaces may declare properties with empty get and set methods. In this way the interface forces implementing classes to define the property)
- Static properties (self explanatory I presume)
- Trait support
- and more....
Conclusion
I personally like this feature. I don't really know if it is much better than defining the getSomething and setSomething methods without the language specific syntax, but it is thought trough, well described and a patch is already there. For me this kind of stuff is the reason to follow the internals list :)
Once again, I am very curious to your opinion, so don't hesistate to give it!
Comments
-
I'd love to have this feature in PHP. Property accessors would be an awesome addition (and one that makes sense) to the language.
I agree with you with regard to the read-only and write-only keywords, they are unneccessary, plus this would be consistent with the way properties are implemented in other languages, e.g. C#.
-
Propery accessors are the legacy from the previous century.
They were invented for a distibuted framework which was badly written (The goal was to "save" the state of public values during "distribution"). J2EE golden age.
I do not see why accessor should be promoted in PHP. Each time I see a real exemple (not a "car"/"motor" like exemple), a cleaner and testable counter exemple can be provide. Also, there is less useless code to write.
KISS
-
Isn't this proposal just adding another means of creating class members? This seems to be functionally equivalent to the set/get functions, just with a different syntax.
For me, the only reason to use the magic get and set methods in the first place is to set class members of which I can't know or predict the existence in advance.
Therefore, since it is specifically described as a better or different way to provide getters and setters, I'm wondering what the actual added value is.
Can someone enlighten me?
-
I think this offers a great improvement as it completely removes the need to deduplicate the locations in class definitions where properties are defined, and where their getting/setting is handled.
Ever tried to hack your way into a well meant but horrible (may i repeat horrible) >30 switch block for a large class, which was something i have come across before, or a class with > 15 get/set methods only to find out the property definition was in a subclass?You can easily blame this on lazy programming, sure, but i think this RFC at least gives some indication of where the preferred place of this kind of code could be, which improves predictability and readability.
My only concern would be howto override these declarations, because that is something usually easily done with get/set-methods.
-
I think this would be a great addition to the language. While it doesn't add anything that isn't already possible I think the major benefit is in grouping together getters and setters with the properties they manipulate. As a developer it's then much easier to see at a glance how the code works. It also makes the main body of the class less cluttered, improving the readability of the code.
-
@idont
To me it seems that property accessors are a formally defined way of setting up your get and set methods, nothing less or more.@jacob kiers
I think you're right, it is not much more than an improvement of the current get and set methods combined with a language defined way of writing your getSomething and getSomething methods.@HdR
If I understand you correct you worry about overloading of Properties? A child class can simply overload its parents property, and thus enlarge visibility, redefine read-only or write-only and so on. -
I'd started an RFC on this a couple years ago, but never finished it. Kudos on finishing it and getting it to this level. I do feel, though, that it's perhaps going about this in a somewhat convoluted way, compared to an alternative approach.
My other fave outside of PHP is Groovy, and the approach there is that the compiling process creates get/set methods for each member, and every call to something is implicitly transformed in to a get/set method call. If you want to change behaviour, just add your own custom get/set method.
class foo { public $name; public $email; public function setName($val) { // do something else with the name } public function getEmail() { // format $this->email before returning it } }
I feel that having methods declared right up next to the member declarations will lead to clunkier and harder to read code, as opposed to being forced to have the implementation details in separate get/set methods. Get/set methods are understood already and used in lots of places - making common get/setters redundant but custom ones possible would be great. This C# approach is... I can live with it, but would have preferred the alternative approach I detailed above. I should have pushed for this several years ago, but didn't get any initial support from the few people I showed it to.
-
We can already do a lot of nice stuff with get() and set(). This is what's available with my framework:
https://github.com/ICanBoogie/ICanBoogie/blob/master/lib/core/object.php
namespace ICanBoogie; class A extends Object { // create the property "a" on first access protected function __get_a() { return "a"; } // only return the value of "b" protected function __volatile_get_b() { return "b"; } // sets $value in "a", only called once protected function __set_a($value) { return $value; } // we can store the value however we want. The method is called as long as the property is inaccessible. protected function __volatile_set($value) { } }
Because these getters and setters are simple methods one can override a setter or a getter however he wants. In my opinion your proposal doesn't make it very clear how one can override a getter OR a setter. It appears as one can only override the property definition as a whole.
Maybe we could have something like:
class A { public get $a() { return 1; } // there is not 'set $a' the property is only gettable } class B extends A { // we override the "$a" getter public get $a() { return 2; } // we define a setter for "$a" public set $a($value) { $this->a; } }
My framework defines the Exception\PropertyNotReadable and Exception\PropertyNotWrittable to qualify exception. That could be nice too to have similar exceptions.
-
I would like it if the getter and setter are together. Also agree that the read-only is ugly, implicit feels better.
-
I like the idea of class properties. It takes less code to write get/set logic using properties vs. writing functions for getter/setter access. They make classes more organized by defining the set/get logic where the member variables are defined, and using them feels more intuitive eg if ($obj->Seconds) vs. if ($obj->getSeconds()). Incidentally, that's also less code.
I'm really curious why anyone introduced read-only and write-only keywords in the first place (vs. implicit read only and write only access), and I don't want to dig through the internals list to find out why. The one example on the RFC seems flimsy, and can only possibly lead to confusing code. On the surface this feels like another example of PHP forcing developers to be overly verbose. Like forcing us to use the word "function" when defining a function. But I'm sure someone had a good reason.
-
@Olivier Laviale
To get things straight, it is not my idea at all. I just liked to show it to the world. In respect to overloading just a getter or a setter, that would be perfectly possible with the proposal.