[RFC] Propety Accessors v1.1

php.internals

Clint M Priest

13 years ago
It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it. RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented Example Usage: class TimePeriod { private $Seconds = 3600; public $Hours { get { return $this->Seconds / 3600; } set { $this->Seconds = $value; } isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } } } Changes / Updates * isset/unset accessor functions now implemented (object & static context, auto implementations, etc) * static accessor now fully functional * Reference functionality validated, tests written * All operators have been tested, tests written * read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section * Tested for speed, approaches or meets __get() speed. Internally things have changed quite a bit * cleaned up and simplified * had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY) * the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed it. The code is available at the url below and is up to date with master, all tests pass. https://github.com/cpriest/php-src I'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated: * Check on reflection code written prior to major changes (tests still pass) * Add a few more reflection functions that were requested In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know. -Clint

Clint M Priest

13 years ago
As an update, just ran some performance testing: master Cycles Direct Getter __get v1.4 @ 10/8/2012 1m .05s .21s .20s php 5.5.0-dev Cycles Direct Getter __get v1.4 @ 10/8/2012 1m .04s n/a .21s Performance of property accessors was important to me as I'm sure it will be to many, on one million cycles of a simple getter, it's <.01s difference. Depending on the run it is sometimes exactly the same performance.

Paul Dragoonis

13 years ago
Good work Clint! Performance is probably one of the things people will complain about so it's good that your benchmark is proving that it's merely identical to the old __get() approach. What remains on your TODO list for this functionality? When are you planning to run an RFC vote on this? I think this would be a valuable addition to PHP 5.5. Cheers, Paul. On Mon, Oct 8, 2012 at 1:42 PM, Clint Priest <cpriest@zerocue.com> wrote:

Stas Malyshev

13 years ago
Hi!
> What remains on your TODO list for this functionality? > When are you planning to run an RFC vote on this? > > I think this would be a valuable addition to PHP 5.5.
I think we shouldn't rush with votes on this until all fine details aren't hashed out. This is a *huge* feature - one of the biggest ones recently, and has a lot of implications for various scenarios. Property access is what virtually every script in existence does, and doing changes there have implications that touch every corner of the engine. I think Clint is doing a great job with this RFC, but we need to carefully work through all the corners and side cases and relationships with all other features before we can declare it's ready for the prime time. Exactly because it is such a big deal it needs to be refined and polished.
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227

Clint M Priest

13 years ago
At this point, the last two weeks of deliberations on this RFC have pushed its release back quite a long ways. I don't know when 5.5 is due out but if it's within the next 6 months (and I'm the only one working on the code) then it will probably not be happening. Due to the dramatic changes that have been talked about, I may just scrap the fork and start over, since I've learned a lot about php-core since starting this a year ago.

Pierre Joye

13 years ago
On Oct 16, 2012 11:27 AM, "Stas Malyshev" <smalyshev@sugarcrm.com> wrote:
> > Hi! > > > What remains on your TODO list for this functionality? > > When are you planning to run an RFC vote on this? > > > > I think this would be a valuable addition to PHP 5.5. > > I think we shouldn't rush with votes on this until all fine details > aren't hashed out. This is a *huge* feature - one of the biggest ones > recently,
> Exactly because it is such a big deal it needs to be refined and > polished.
Amen.

Lester Caine

13 years ago
Stas Malyshev wrote:
>> >What remains on your TODO list for this functionality? >> >When are you planning to run an RFC vote on this? >> > >> >I think this would be a valuable addition to PHP 5.5. > I think we shouldn't rush with votes on this until all fine details > aren't hashed out. This is a*huge* feature - one of the biggest ones > recently, and has a lot of implications for various scenarios. Property > access is what virtually every script in existence does, and doing > changes there have implications that touch every corner of the engine. > > I think Clint is doing a great job with this RFC, but we need to > carefully work through all the corners and side cases and relationships > with all other features before we can declare it's ready for the prime > time. Exactly because it is such a big deal it needs to be refined and > polished.
But a vote NOT to include it should still be one of the options! The more edge cases I see on this discussion, the more I am convinced that this is simply not right for PHP ... it's not needed and only adds unnecessary bloat. The more the functionality of PHP gets buried in 'magic code' that can't even be debugged properly or viewed the less useful PHP becomes. I'm sorry if people are spending a lot of time trying to make something work, but that is their choice, and should not be a reason to include something. I still don't see how this improves anything when all we need to be doing is managing the existing variables, arrays and objects stored in the object. __get and __set aren't needed either but that is another matter. Does anybody actually use them, or are they waiting for the 'better alternative'? Is there any real reason not simply to be using $obj->var ? It is the fastest way of doing it anyway ...
-- Lester Caine - G8HFL ----------------------------- Contact - http://lsces.co.uk/wiki/?page=contact L.S.Caine Electronic Services - http://lsces.co.uk EnquirySolve - http://enquirysolve.com/ Model Engineers Digital Workshop - http://medw.co.uk Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

Cal

13 years ago
(Let me suggest an idea irrelevant. Sorry...) The performance of getters is critical. For me the best solution would be a new keyword, equivalent to a "var" without write access from outside the class: class TimePeriod { *property* $Hours = 1; public function setHours($h) { $this->Hours = $h; } } $myTP = new TimePeriod(); echo $myTP->Hours; // 1 $myTP->setHours(3); echo $myTP->Hours; // 3 $myTP->Hours = 5; // KO. PHP error here Le 08/10/2012 13:42, Clint Priest a écrit :

Leigh

13 years ago
On 11 October 2012 12:46, Cal <cal@icical.net> wrote:
> For me the best solution would be a new keyword, equivalent to a "var" > without write access from outside the class:
New keywords should not (will not) be introduced trivially, they can cause massive headaches with backwards compatibility breaks, and should be a last resort only when absolutely necessary. Re-using existing keywords is the most logical solution. (i.e. suggestions such as "public get" and "protected/private set")

Amaury Bouchard

13 years ago
Hi, This summer (july 15) I did another proposal, which has some connections with yours. For the main usage of getters/setters, my guess is that we need separate read/write visibilities. Your RFC goes beyond that, but I think both are complementary. Most of the time, we write getters/setters to ensure that a private attribute can be read but not modified (or modified only as we want to). Your RFC is pretty powerful in some use cases. Like your example, which is a "virtual" attribute, created from some processing. But create a new "read-only" keyword is not a good idea for me: introduce a new keyword should be avoided when it's not absolutely necessary, and it lacks meaning (exact visibility information). Correct me if I'm wrong, but your syntax is similar to the one used in C#. Not a bad idea, but I think we can imagine for PHP somethink a little more concise for the general usage. My idea was to write attribute's visibility like "read_visiblity:write_visibility $attr;" public:protected $foo; // public reading, protected writing public:private $bar; // public reading, private writing protected:private $aaa; // protected reading, private writing protected:const $bbb; // protected reading, no writing With your RFC it will be: public $foo { get; protected set; } public $bar { get; private set; } protected $aaa { get; private set; } protected read-only $bbb; When I did my proposal on the internals mailing-list, I got some pretty bad feedbacks, and some very good ones. So, maybe we can try to merge some ideas? My patch: http://github.com/Amaury/php-src Regards, Amaury 2012/10/8 Clint Priest <cpriest@zerocue.com>

Cal

13 years ago
Le 08/10/2012 14:27, Amaury Bouchard a écrit :
> My idea was to write attribute's visibility like > "read_visiblity:write_visibility $attr;" > public:protected $foo; // public reading, protected writing > public:private $bar; // public reading, private writing > protected:private $aaa; // protected reading, private writing > protected:const $bbb; // protected reading, no writing
I read all the messages on the property accessors. And I really agree with the solution of Amaury. It is a nice solution: not verbose, not slower, just covers most of developper's needs. Cal

Denis Portnov

13 years ago
08.10.2012 15:52, Clint Priest пишет:
> public $Hours { > get { return $this->Seconds / 3600; } > set { $this->Seconds = $value; } > isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } > unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } > }
Hi Clint, I've noticed some magic variable '$value' is introduced. And except for superglobals I guess there is no such thing in PHP, so it looks bit puzzling to me. I'd suggest on of the following: - variable for value has the same name as property public $Hours { set { $this->Seconds = $Hours * 3600; } } - magic constant public $Hours { set { $this->Seconds = __VALUE__ * 3600; } } - setter resambles setter method, wich also allows typehinting public $Hours { set ($value) { $this->Seconds = $value * 3600; } } public $Hours { set (DateTime $dateTime) { $this->Seconds = $dateTime->getTimestamp(); } } - or at least have it in same format as superglobals public $Hours { set { $this->Seconds = $_VALUE * 3600; } } What do you think? Thanks Denis

Aaron Holmes

13 years ago
On 10/8/12 1:07 PM, Denis Portnov wrote:
> 08.10.2012 15:52, Clint Priest пишет: >> public $Hours { >> get { return $this->Seconds / 3600; } >> set { $this->Seconds = $value; } >> isset<http://www.php.net/isset> { return >> isset<http://www.php.net/isset>($this->Seconds); } >> unset<http://www.php.net/unset> { >> unset<http://www.php.net/unset>($this->Seconds); } >> } > > > Hi Clint, > > I've noticed some magic variable '$value' is introduced. And except > for superglobals I guess there is no such thing in PHP, so it looks > bit puzzling to me. I'd suggest on of the following: > > > - setter resambles setter method, wich also allows typehinting > public $Hours { > set ($value) { $this->Seconds = $value * 3600; } > } > > public $Hours { > set (DateTime $dateTime) { $this->Seconds = > $dateTime->getTimestamp(); } > } >
This seems like the cleanest method, in my opinion. Javascript does this for object prototypes: http://ejohn.org/blog/javascript-getters-and-setters/

Benjamin Eberlei

13 years ago
The set() one is really nice with the typehints. On Tue, Oct 9, 2012 at 12:19 AM, Aaron Holmes <aaron@aaronholmes.net> wrote:

Jazzer Dane

13 years ago
I agree. It's more consistent than the $Hours solution and we don't have to add another superglobal or magic constant, which is quite nice. The typehinting is a big plus as well. On Mon, Oct 8, 2012 at 3:26 PM, Benjamin Eberlei <kontakt@beberlei.de>wrote:

Clint M Priest

13 years ago
Seems a fair amount of people would like it with a definable parameter name, though the original RFC I based mine off of is more than 4 years old (mine is over a year old already). The $value is precisely chosen because it is exactly the way C# operates and the original author thought to keep it the same as another well-known language (why re-invent new syntax for no reason). That being said, javascript does indeed allow it, my concern then would be would we have the parameterized syntax only for set() and not get, isset or unset? If we do have them for all of them, it's a lot of extra characters with no real need. I definitely favor set($value) over a magic $Hours for the $Hours property, but I personally see no problem with the $value, it's not magic it's a locally defined variable. Internally, this: public $Hours { get { ... } set { ... } } Is implemented as standard functions, while they are hidden through reflection, these functions exist (as a result of the above example): public __getHours() { ... } public __setHours($value) { ... } Lastly, with regards to JavaScript style getters/setters, I don't think I've ever cared what the variable name was, I typically just do something like: set blah(x) { ... } <-- x is fairly irrelevant and similarly the use of $value is fairly irrelevant. Thoughts?

Denis Portnov

13 years ago
09.10.2012 5:10, Clint Priest пишет:
> Seems a fair amount of people would like it with a definable parameter name, though the original RFC I based mine off of is more than 4 years old (mine is over a year old already). > > The $value is precisely chosen because it is exactly the way C# operates and the original author thought to keep it the same as another well-known language (why re-invent new syntax for no reason).
Well, in C# 'value' is a contextual keyword not a variable. Same as : get, set, yield. So I'd argue, copying the syntax of well-known language is not the case here.
> That being said, javascript does indeed allow it, my concern then would be would we have the parameterized syntax only for set() and not get, isset or unset? > > If we do have them for all of them, it's a lot of extra characters with no real need. > > I definitely favor set($value) over a magic $Hours for the $Hours property, but I personally see no problem with the $value, it's not magic it's a locally defined variable. > > Internally, this: > public $Hours { > get { ... } > set { ... } > } > > Is implemented as standard functions, while they are hidden through reflection, these functions exist (as a result of the above example): > > public __getHours() { ... } > public __setHours($value) { ... }
Didn't fully graps the internals yet, not that sharp in C, sorry. Is there a possibility of collision with user-defined __getHours() method?
> Lastly, with regards to JavaScript style getters/setters, I don't think I've ever cared what the variable name was, I typically just do something like: > > set blah(x) { ... } <-- x is fairly irrelevant and similarly the use of $value is fairly irrelevant. Thoughts?
I kinda share your concern for having parameterized syntax for set() only. One way out of this could be introducing new keyword complimentary to 'return' to define by what name value will be presented in local scope. Possible names whould be: keep, accept or value public $Hours { get { return $this->seconds / 3600; } set { // present value as $hours in local scope keep $hours; $this->seconds = $hours * 3600; } } I find people's need for typehinting plausible though Thanks Denis

Jazzer Dane

13 years ago
While I understand your concern with set being the only keyword using (), and even agree it's a bit problematic, I see a big problem with using $value. Even if $value internally makes sense due to something along the lines of * __setHours($value)* {} being equal to *set {}*, I think using $value without it ever being defined in the developer's code is not at all a good idea. If I see $value in the code, I'll logically look for where it was defined, and when I don't see it anywhere else in the code, things are going to very quickly get confusing. Our best option to combat this confusion is, in my eyes, putting a note in the documentation. That's not enough. A similar alternative to using $value that I'd argue would be much more sensible would be to, as Denis mentioned, use either a magic constant or a superglobal. As I mentioned previously, I would rather go with the set($value) {} syntax. Now, back to the part where I agree with you - the inconsistency wherein set has () that denote it is a method but get, isset, and unset do not. I see this inconsistency as something problematic enough to warrant a solution. We could go with the following: public $Hours { get() { ... } set($value) { ... } isset() { ... } unset() { ... } } Yes, we now have a little bit more meat on the syntax, but in this case, I don't think that it's all that bad. Here's two reasons why: 1) Adding parenthesis denotes that they are all functions - which they are! If anything, adding parenthesis to all of them makes the implementation * more* sensible. 2) It's *only* two more characters per function. On top of that, in my opinion, this syntax is not "ugly". In fact, as I just mentioned - this implementation is arguably *more* consistent with the rest of PHP. On Mon, Oct 8, 2012 at 6:10 PM, Clint Priest <cpriest@zerocue.com> wrote:

Clint M Priest

13 years ago
Great arguments for, anyone against this being the new way? If not then I’ll begin work on this change as well. I would like to keep this discussion going so that at the end of this two week discussion period I can submit it for voting. Jazzer, your points make a lot of sense in terms of making it clear that they are all functions, I’m all game. If it’s not difficult I may just make it compatible in both ways (auto-define $value) if parenthesized option is not specified. Flexibility is at the heart of php. -Clint From: Jazzer Dane [mailto:tbprogrammer@gmail.com] Sent: Monday, October 08, 2012 10:08 PM To: Clint Priest Cc: internals@lists.php.net; Aaron Holmes; Benjamin Eberlei Subject: Re: [PHP-DEV] [RFC] Propety Accessors v1.1 While I understand your concern with set being the only keyword using (), and even agree it's a bit problematic, I see a big problem with using $value. Even if $value internally makes sense due to something along the lines of __setHours($value) {} being equal to set {}, I think using $value without it ever being defined in the developer's code is not at all a good idea. If I see $value in the code, I'll logically look for where it was defined, and when I don't see it anywhere else in the code, things are going to very quickly get confusing. Our best option to combat this confusion is, in my eyes, putting a note in the documentation. That's not enough. A similar alternative to using $value that I'd argue would be much more sensible would be to, as Denis mentioned, use either a magic constant or a superglobal. As I mentioned previously, I would rather go with the set($value) {} syntax. Now, back to the part where I agree with you - the inconsistency wherein set has () that denote it is a method but get, isset, and unset do not. I see this inconsistency as something problematic enough to warrant a solution. We could go with the following: public $Hours { get() { ... } set($value) { ... } isset() { ... } unset() { ... } } Yes, we now have a little bit more meat on the syntax, but in this case, I don't think that it's all that bad. Here's two reasons why: 1) Adding parenthesis denotes that they are all functions - which they are! If anything, adding parenthesis to all of them makes the implementation more sensible. 2) It's only two more characters per function. On top of that, in my opinion, this syntax is not "ugly". In fact, as I just mentioned - this implementation is arguably more consistent with the rest of PHP. On Mon, Oct 8, 2012 at 6:10 PM, Clint Priest <cpriest@zerocue.com<mailto:cpriest@zerocue.com>> wrote: Seems a fair amount of people would like it with a definable parameter name, though the original RFC I based mine off of is more than 4 years old (mine is over a year old already). The $value is precisely chosen because it is exactly the way C# operates and the original author thought to keep it the same as another well-known language (why re-invent new syntax for no reason). That being said, javascript does indeed allow it, my concern then would be would we have the parameterized syntax only for set() and not get, isset or unset? If we do have them for all of them, it's a lot of extra characters with no real need. I definitely favor set($value) over a magic $Hours for the $Hours property, but I personally see no problem with the $value, it's not magic it's a locally defined variable. Internally, this: public $Hours { get { ... } set { ... } } Is implemented as standard functions, while they are hidden through reflection, these functions exist (as a result of the above example): public __getHours() { ... } public __setHours($value) { ... } Lastly, with regards to JavaScript style getters/setters, I don't think I've ever cared what the variable name was, I typically just do something like: set blah(x) { ... } <-- x is fairly irrelevant and similarly the use of $value is fairly irrelevant. Thoughts?

Christian Stoller

13 years ago
I personally like the idea by Denis Portnov with the magic constant __VALUE__ which goes along with other magic constants documented here http://www.php.net/manual/en/language.constants.predefined.php But the advantage of the "type hinting" in the setter when we use "set($value)" should be weighted stronger. This is a great plus! I would vote for Jazzer's
> public $Hours { > get() { ... } > set($value) { ... } > isset() { ... } > unset() { ... } > }
Best regards Christian Stoller -----Original Message----- From: Jazzer Dane [mailto:tbprogrammer@gmail.com] Sent: Tuesday, October 09, 2012 5:08 AM To: Clint Priest Cc: internals@lists.php.net; Aaron Holmes; Benjamin Eberlei Subject: Re: [PHP-DEV] [RFC] Propety Accessors v1.1 While I understand your concern with set being the only keyword using (), and even agree it's a bit problematic, I see a big problem with using $value. Even if $value internally makes sense due to something along the lines of * __setHours($value)* {} being equal to *set {}*, I think using $value without it ever being defined in the developer's code is not at all a good idea. If I see $value in the code, I'll logically look for where it was defined, and when I don't see it anywhere else in the code, things are going to very quickly get confusing. Our best option to combat this confusion is, in my eyes, putting a note in the documentation. That's not enough. A similar alternative to using $value that I'd argue would be much more sensible would be to, as Denis mentioned, use either a magic constant or a superglobal. As I mentioned previously, I would rather go with the set($value) {} syntax. Now, back to the part where I agree with you - the inconsistency wherein set has () that denote it is a method but get, isset, and unset do not. I see this inconsistency as something problematic enough to warrant a solution. We could go with the following: public $Hours { get() { ... } set($value) { ... } isset() { ... } unset() { ... } } Yes, we now have a little bit more meat on the syntax, but in this case, I don't think that it's all that bad. Here's two reasons why: 1) Adding parenthesis denotes that they are all functions - which they are! If anything, adding parenthesis to all of them makes the implementation * more* sensible. 2) It's *only* two more characters per function. On top of that, in my opinion, this syntax is not "ugly". In fact, as I just mentioned - this implementation is arguably *more* consistent with the rest of PHP. On Mon, Oct 8, 2012 at 6:10 PM, Clint Priest <cpriest@zerocue.com> wrote:

Christian Kaps

13 years ago
Hi, typehinting should definitely be available for this feature. But I have another question. Why not go more consistent with the rest of the language? I have mentioned this previously as the first proposal comes up on the list. In my opinion the AS3 getter and setter syntax(http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f30.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7fcb) fits more into the language as the proposed one. Here are my concerns: - I do not like the extra indentation level and it's ugly to document (OK, this is a personal preference) - It isn't possible to declare a private setter and a public getter, as it is possible with methods - If we ever get return type hinting/checks then we needn't consider how the syntax has to look - We must deal with two different syntaxes for the same thing, because both are methods - IDE's, documentation tools, static analyses tools or similar tools have a huge effort to implement this syntax. With the method syntax it's only a small effort - We have to create new rules about how the documentation for this syntax should look like For me the following syntax seems more consistent with the rest of PHP: public get hours() {} public set hours(DateTime $dateTime) {} public isset hours() {} public unset hours() {} Or: public function get hours() {} public function set hours(DateTime $dateTime) {} public function isset hours() {} public function unset hours() {} Or: public function get $hours() {} public function set $hours(DateTime $dateTime) {} public function isset $hours() {} public function unset $hours() {} Cheers, Christian Am 09.10.2012 05:08, schrieb Jazzer Dane:

Sebastian Krebs

13 years ago
2012/10/9 Christian Kaps <christian.kaps@mohiva.com>
> Hi, > > typehinting should definitely be available for this feature. But I have > another question. Why not go more consistent with the rest of the language? > I have mentioned this previously as the first proposal comes up on the > list. In my opinion the AS3 getter and setter syntax( > http://help.adobe.com/**en_US/ActionScript/3.0_**ProgrammingAS3/** > WS5b3ccc516d4fbf351e63e3d118a9**b90204-7f30.html#** > WS5b3ccc516d4fbf351e63e3d118a9**b90204-7fcb<http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f30.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7fcb>) > fits more into the language as the proposed one. > > Here are my concerns: > - I do not like the extra indentation level and it's ugly to document (OK, > this is a personal preference) >
I guess it's more useful to document the property as a whole and not every single accessor on it's own.
> - It isn't possible to declare a private setter and a public getter, as it > is possible with methods >
As far as I understand it it is possible.
> - If we ever get return type hinting/checks then we needn't consider how > the syntax has to look >
- We must deal with two different syntaxes for the same thing, because both
> are methods >
For now it seems the only bigger difference is, that the keyword 'function' is omitted. You can propose to add it :)
> - IDE's, documentation tools, static analyses tools or similar tools have > a huge effort to implement this syntax. With the method syntax it's only a > small effort >
The tools should not decide how the language should look like, just because it makes their life easier ;)
> - We have to create new rules about how the documentation for this syntax > should look like >
Why should the documentation differ from the documentation for regular properties?
> > For me the following syntax seems more consistent with the rest of PHP: > > public get hours() {} > public set hours(DateTime $dateTime) {} > public isset hours() {} > public unset hours() {} > > Or: > > public function get hours() {} > public function set hours(DateTime $dateTime) {} > public function isset hours() {} > public function unset hours() {} > > Or: > > public function get $hours() {} > public function set $hours(DateTime $dateTime) {} > public function isset $hours() {} > public function unset $hours() {} >
I don't like how it separate the single accessors from each other. The benefit from this RFC (imo) -- especially compared to __get()/__set() -- is, that every accessors is directly and obviously bound to the (single) property: You have a single block with all the corresponding accessors in it and not several (on the first glance: different) methods, that could be spread over the whole class. Maybe it's just me, but I really would like to see this feature in 5.5. As far as I remember it was first proposed for 5.3 and now (years after) seeing it delayed, because the syntax "looks ugly (just my opinion)" makes me feel a little bit ... ehm ... sad. I think the syntax is fine and it seems it covers the most important points (even not all). There is a property and "the value" of the property are the accessors. I bet we could find an infinite number of different syntax, that provides exactly the same functionality, but why? Regards, Sebastian
> > Cheers, > Christian > > Am 09.10.2012 05:08, schrieb Jazzer Dane: > > While I understand your concern with set being the only keyword using (), >> and even agree it's a bit problematic, I see a big problem with using >> $value. >> >> Even if $value internally makes sense due to something along the lines of >> * >> __setHours($value)* {} being equal to *set {}*, I think using $value >> without it ever being defined in the developer's code is not at all a good >> idea. >> If I see $value in the code, I'll logically look for where it was defined, >> and when I don't see it anywhere else in the code, things are going to >> very >> quickly get confusing. >> Our best option to combat this confusion is, in my eyes, putting a note in >> the documentation. That's not enough. >> >> A similar alternative to using $value that I'd argue would be much more >> sensible would be to, as Denis mentioned, use either a magic constant or a >> superglobal. >> >> As I mentioned previously, I would rather go with the set($value) {} >> syntax. >> >> Now, back to the part where I agree with you - the inconsistency wherein >> set has () that denote it is a method but get, isset, and unset do not. I >> see this inconsistency as something problematic enough to warrant a >> solution. >> >> We could go with the following: >> public $Hours { >> get() { ... } >> set($value) { ... } >> isset() { ... } >> unset() { ... } >> } >> >> Yes, we now have a little bit more meat on the syntax, but in this case, I >> don't think that it's all that bad. Here's two reasons why: >> 1) Adding parenthesis denotes that they are all functions - which they >> are! >> If anything, adding parenthesis to all of them makes the implementation * >> more* sensible. >> 2) It's *only* two more characters per function. On top of that, in my >> opinion, this syntax is not "ugly". In fact, as I just mentioned - this >> implementation is arguably *more* consistent with the rest of PHP. >> >> On Mon, Oct 8, 2012 at 6:10 PM, Clint Priest <cpriest@zerocue.com> wrote: >> >> Seems a fair amount of people would like it with a definable parameter >>> name, though the original RFC I based mine off of is more than 4 years >>> old >>> (mine is over a year old already). >>> >>> The $value is precisely chosen because it is exactly the way C# operates >>> and the original author thought to keep it the same as another well-known >>> language (why re-invent new syntax for no reason). >>> >>> That being said, javascript does indeed allow it, my concern then would >>> be >>> would we have the parameterized syntax only for set() and not get, isset >>> or >>> unset? >>> >>> If we do have them for all of them, it's a lot of extra characters with >>> no >>> real need. >>> >>> I definitely favor set($value) over a magic $Hours for the $Hours >>> property, but I personally see no problem with the $value, it's not magic >>> it's a locally defined variable. >>> >>> Internally, this: >>> public $Hours { >>> get { ... } >>> set { ... } >>> } >>> >>> Is implemented as standard functions, while they are hidden through >>> reflection, these functions exist (as a result of the above example): >>> >>> public __getHours() { ... } >>> public __setHours($value) { ... } >>> >>> Lastly, with regards to JavaScript style getters/setters, I don't think >>> I've ever cared what the variable name was, I typically just do something >>> like: >>> >>> set blah(x) { ... } <-- x is fairly irrelevant and similarly the use of >>> $value is fairly irrelevant. Thoughts? >>> >>> > -----Original Message----- >>> > From: Jazzer Dane [mailto:tbprogrammer@gmail.com**] >>> > Sent: Monday, October 08, 2012 5:32 PM >>> > To: Benjamin Eberlei >>> > Cc: Aaron Holmes; internals@lists.php.net >>> > Subject: Re: [PHP-DEV] [RFC] Propety Accessors v1.1 >>> > >>> > I agree. >>> > It's more consistent than the $Hours solution and we don't have to add >>> another superglobal or magic constant, which is quite nice. The >>> > typehinting is a big plus as well. >>> > >>> > On Mon, Oct 8, 2012 at 3:26 PM, Benjamin Eberlei <kontakt@beberlei.de >>> >wrote: >>> > >>> > > The set() one is really nice with the typehints. >>> > > >>> > > On Tue, Oct 9, 2012 at 12:19 AM, Aaron Holmes <aaron@aaronholmes.net >>> > >>> > > wrote: >>> > > >>> > > > On 10/8/12 1:07 PM, Denis Portnov wrote: >>> > > > >>> > > >> 08.10.2012 15:52, Clint Priest пишет: >>> > > >> >>> > > >>> public $Hours { >>> > > >>> get { return $this->Seconds / 3600; } >>> > > >>> set { $this->Seconds = $value; } >>> > > >>> isset<http://www.php.net/**isset**<http://www.php.net/isset**>> >>> { return isset< >>> > > >>> http://www.php.net/isset**>($**this->Seconds); } >>> > > >>> unset<http://www.php.net/**unset**<http://www.php.net/unset**>> >>> { unset< >>> > > >>> http://www.php.net/unset**>($**this->Seconds); } >>> > > >>> } >>> > > >>> >>> > > >> >>> > > >> >>> > > >> Hi Clint, >>> > > >> >>> > > >> I've noticed some magic variable '$value' is introduced. And >>> except >>> > > >> for superglobals I guess there is no such thing in PHP, so it >>> looks >>> > > >> bit puzzling to me. I'd suggest on of the following: >>> > > >> >>> > > >> >>> > > >> - setter resambles setter method, wich also allows typehinting >>> > > >> public $Hours { >>> > > >> set ($value) { $this->Seconds = $value * 3600; } >>> > > >> } >>> > > >> >>> > > >> public $Hours { >>> > > >> set (DateTime $dateTime) { $this->Seconds = >>> > > >> $dateTime->getTimestamp(); } >>> > > >> } >>> > > >> >>> > > >> This seems like the cleanest method, in my opinion. Javascript >>> > > >> does >>> > > this >>> > > > for object prototypes: >>> > > > http://ejohn.org/blog/****javascript-getters-and-****setters/<http://ejohn.org/blog/**javascript-getters-and-**setters/> >>> < >>> > > http://ejohn.org/blog/**javascript-getters-and-**setters/<http://ejohn.org/blog/javascript-getters-and-setters/> >>> > >>> > > > >>> > > > >>> > > >> >>> > > >> What do you think? >>> > > >> >>> > > >> Thanks >>> > > >> Denis >>> > > >> >>> > > >> >>> > > > >>> > > > -- >>> > > > PHP Internals - PHP Runtime Development Mailing List To >>> unsubscribe, >>> > > > visit: http://www.php.net/unsub.php >>> > > > >>> > > > >>> > > >>> >>> > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- github.com/KingCrunch

Clint M Priest

13 years ago
Okay, I would like this to be the last time there are revisions to this RFC. To sum up the last few days of conversations, I have these down as points of contention: 1. Accessor functions should not be present on the object and callable directly, for example, $o->__getHours() should not be allowed. 2. Preferred syntax for accessors should be "public set($value) { ... }" with no "magic" $value (with possible type hinting) 3. Automatically implemented get; set; with auto-backing field should be eliminated as this is not necessary for PHP and is confusing most everyone. 4. read-only / write-only keywords, keep them or get rid of them? There is no directly suitable replacement but I believe a private final set() { } will take care of it, even though it much more verbose. 5. Error handling for thrown exceptions should be made more appropriate for accessors 6. The "truth" of reflection. Should it reveal details internal to how PHP works on the inside or should it reflect the way PHP presents it as options? Did I miss anything? I will come up with some way for people to vote on the issues at hand and we can cast our votes and be done with it, then I will finish the project and get it out the door. -Clint

Jazzer Dane

13 years ago
Christian, I think a majority of your concerns are all for none. From top to bottom: - Extra indention: This is definitely a bias and is only moderately relevant while documenting this feature. - You *can* declare a public getter and private setter, or vice versa, or whatever you want. This is in the RFC. See "Asymmetric Accessor Accessibility." - "If we ever get return type hinting/checks then we needn't consider how the syntax has to look" From what I know, this isn't planned for PHP 5.5 and any proposals for it have been largely ignored. Return type hinting won't help when setting either, although it would help with getting. All that being said, type hinting aside, the syntax I proposed is, in my opinion, the most consistent out of any other proposal thus far (arguably aside from yours, I'll go over that momentarily). - "We must deal with two different syntaxes for the same thing" I don't quite understand what you mean here, unless you're talking about braces inside braces. It seems your code examples make your intended point here a bit clearer, though. - "IDE's, documentation tools, static analyses tools or similar tools have a huge effort to implement this syntax. With the method syntax it's only a small effort" While we want to strive to keep a clean and consistent syntax, I don't think molding to whatever is easiest for IDE's is a good idea. - "We have to create new rules about how the documentation for this syntax should look like" From my point of view, this isn't so much a concern as a to-do item later on. On to your proposal: I'm not a fan of this:
> public function get hours() {} >
This is adding another keyword after function, which is arguably more inconsistent than some previous proposals. I'm sort of okay with this:
> public get hours() {} >
Replacing function with get/set/isset/unset... but it still seems a bit too inconsistent and insensible. Replacing the function keyword with the name of a preexisting function? I also do not like how the accessors are separated from one another, which is not the case with the previous proposals. All in all, I still prefer my previous proposal to this. On Tue, Oct 9, 2012 at 12:46 AM, Christian Kaps <christian.kaps@mohiva.com>wrote:

David Muir

13 years ago
On 09/10/12 19:20, Jazzer Dane wrote:
> - "If we ever get return type hinting/checks then we needn't consider how > the syntax has to look" From what I know, this isn't planned for PHP 5.5 > and any proposals for it have been largely ignored. Return type hinting > won't help when setting either, although it would help with getting. All > that being said, type hinting aside, the syntax I proposed is, in my > opinion, the most consistent out of any other proposal thus far (arguably > aside from yours, I'll go over that momentarily).
Excellent point. Return type-hints only affect the getter, and even then it could be a moot point if the property itself could be bound to a particular type. It would actually be a much cleaner solution, and wouldn't require any superfluous parens in the declaration: class MyClass{ public SomeClass $property { get {...} set {...} } } $foo = new MyClass; $bar = new SomeClass; $foo->property = $bar; //ok $foo->property = new StdClass; //type mismatch Heck, half the time setters and getters are implemented specifically for this purpose. If the property itself could be typed, then 90% of the time we wouldn't even need to specify get/set. Just give the property a type and be done with it. That said, it wouldn't help for scalar values, and that leads to a problem with the current syntax (AFAICS). You can't specify the initial value for a property: eg: class MyClass{ public $property = 5 //?can we do this? { set($value){ if(!is_int($value)){ throw new InvalidArgumentException('value of wrong type'); } $this->__property = $value; } } } Or would that have to happen in the constructor? class MyClass{ public $property { set($value){ if(!is_int($value)){ throw new InvalidArgumentException('value of wrong type'); } $this->__property = $value; } } public function __construct(){ $this->property = 5; } } Cheers, David

André Rømcke

13 years ago
On Oct 8, 2012, at 10:07 PM, Denis Portnov <denixport@gmail.com> wrote:
> 08.10.2012 15:52, Clint Priest пишет: >> public $Hours { >> get { return $this->Seconds / 3600; } >> set { $this->Seconds = $value; } >> isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } >> unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } >> } > > > Hi Clint, > > I've noticed some magic variable '$value' is introduced. And except for superglobals I guess there is no such thing in PHP, so it looks bit puzzling to me. I'd suggest on of the following: > > - variable for value has the same name as property > public $Hours { > set { $this->Seconds = $Hours * 3600; } > } > > - magic constant > public $Hours { > set { $this->Seconds = __VALUE__ * 3600; } > } > > - setter resambles setter method, wich also allows typehinting > public $Hours { > set ($value) { $this->Seconds = $value * 3600; } > } > > public $Hours { > set (DateTime $dateTime) { $this->Seconds = $dateTime->getTimestamp(); } > }
If this function inspired syntax is used, then it kind of hints the possibility of future parameter overloading, like: public $Hours { set ( DateTime $dateTime ) { $this->Seconds = $dateTime->getTimestamp(); } set ( int $hours ) { $this->Seconds = $hours*60*60; } } So for me +1 on that syntax, or using/future-proofing for the syntax from C#: public DateTime $Hours { set { $this->Seconds = $value->getTimestamp(); } } However the example doesn't make much sense (hours being datetime).

Nikita Popov

13 years ago
On Mon, Oct 8, 2012 at 1:52 PM, Clint Priest <cpriest@zerocue.com> wrote:
> It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it. > > RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented > > Example Usage: > > class TimePeriod { > private $Seconds = 3600; > > public $Hours { > get { return $this->Seconds / 3600; } > set { $this->Seconds = $value; } > isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } > unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } > } > } > > Changes / Updates > > * isset/unset accessor functions now implemented (object & static context, auto implementations, etc) > > * static accessor now fully functional > > * Reference functionality validated, tests written > > * All operators have been tested, tests written > > * read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section > > * Tested for speed, approaches or meets __get() speed. > > Internally things have changed quite a bit > > * cleaned up and simplified > > * had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY) > > * the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed it. > > The code is available at the url below and is up to date with master, all tests pass. > https://github.com/cpriest/php-src > > I'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated: > > * Check on reflection code written prior to major changes (tests still pass) > > * Add a few more reflection functions that were requested > > In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know.
What concerns me with the current implementation is that it leaks many implementation details, in particular the fact that the accessors are implemented as *real* __getXYZ methods and automatic implementations also use *real* $__XYZ properties. A few examples: ## 1 - __getProperty() method directly callable class Test { public $property { get { return 123; } } } $test = new Test; var_dump($test->property); // int(123) var_dump($test->__getProperty()); // int(123) ## 2 - __getProperty() method exposed via exception class Test { public $throwingProperty { get { throw new Exception; } } } (new Test)->throwingProperty; exception 'Exception' in /home/nikic/dev/php-src/t29.php:9 Stack trace: #0 /home/nikic/dev/php-src/t29.php(31): Test->__getthrowingProperty() #1 {main} ## 3 - Can directly access $__automaticProperty and even unset it (causing notices in the internal code) class Test { public $automaticProperty { get; set; } public function getAutomaticProperty() { return $this->__automaticProperty; } public function unsetAutomaticProperty() { unset($this->__automaticProperty); } } $test->automaticProperty = 'foo'; var_dump($test->getAutomaticProperty()); $test->unsetAutomaticProperty(); var_dump($test->automaticProperty); string(3) "foo" Notice: Undefined property: Test::$__automaticProperty in /home/nikic/dev/php-src/t29.php on line 13 NULL ===== I feel like this approach to the implementation will be a big can of worms. Sure, it works, but it is rather fragile and the enduser ends up dealing with internal stuff which he ought not care about. I think it would be better to cleanly separate out the accessor implementation. It might require more code now, but will be better in the long run. Nikita

Etienne Kneuss

13 years ago
Hi, On Tue, Oct 9, 2012 at 3:01 PM, Nikita Popov <nikita.ppv@gmail.com> wrote:
> On Mon, Oct 8, 2012 at 1:52 PM, Clint Priest <cpriest@zerocue.com> wrote: >> It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it. >> >> RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented >> >> Example Usage: >> >> class TimePeriod { >> private $Seconds = 3600; >> >> public $Hours { >> get { return $this->Seconds / 3600; } >> set { $this->Seconds = $value; } >> isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } >> unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } >> } >> } >> >> Changes / Updates >> >> * isset/unset accessor functions now implemented (object & static context, auto implementations, etc) >> >> * static accessor now fully functional >> >> * Reference functionality validated, tests written >> >> * All operators have been tested, tests written >> >> * read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section >> >> * Tested for speed, approaches or meets __get() speed. >> >> Internally things have changed quite a bit >> >> * cleaned up and simplified >> >> * had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY) >> >> * the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed it. >> >> The code is available at the url below and is up to date with master, all tests pass. >> https://github.com/cpriest/php-src >> >> I'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated: >> >> * Check on reflection code written prior to major changes (tests still pass) >> >> * Add a few more reflection functions that were requested >> >> In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know. > > What concerns me with the current implementation is that it leaks many > implementation details, in particular the fact that the accessors are > implemented as *real* __getXYZ methods and automatic implementations > also use *real* $__XYZ properties. > > A few examples: > > ## 1 - __getProperty() method directly callable > > class Test { > public $property { > get { return 123; } > } > } > > $test = new Test; > var_dump($test->property); // int(123) > var_dump($test->__getProperty()); // int(123) > > ## 2 - __getProperty() method exposed via exception > > class Test { > public $throwingProperty { > get { throw new Exception; } > } > } > > (new Test)->throwingProperty; > > exception 'Exception' in /home/nikic/dev/php-src/t29.php:9 > Stack trace: > #0 /home/nikic/dev/php-src/t29.php(31): Test->__getthrowingProperty() > #1 {main} > > ## 3 - Can directly access $__automaticProperty and even unset it > (causing notices in the internal code) > > class Test { > public $automaticProperty { > get; set; > } > > public function getAutomaticProperty() { > return $this->__automaticProperty; > } > > public function unsetAutomaticProperty() { > unset($this->__automaticProperty); > } > } > > $test->automaticProperty = 'foo'; > var_dump($test->getAutomaticProperty()); > $test->unsetAutomaticProperty(); > var_dump($test->automaticProperty); > > string(3) "foo" > > Notice: Undefined property: Test::$__automaticProperty in > /home/nikic/dev/php-src/t29.php on line 13 > NULL > > ===== > > I feel like this approach to the implementation will be a big can of > worms. Sure, it works, but it is rather fragile and the enduser ends > up dealing with internal stuff which he ought not care about. I think > it would be better to cleanly separate out the accessor > implementation. It might require more code now, but will be better in > the long run. >
I disagree, to me that this feature is all about syntactic sugar, as such it does what it expected: it generates concrete properties and methods that are somewhat hidden to the end-user. I feel that any implementation that do not rely on proper properties/methods would be a big hack. Best,
> Nikita > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php >
-- Etienne Kneuss http://www.colder.ch

Nathan Bruer

13 years ago
Is there a reason why we cannot implement this using PHP's already widely used function syntax: class TimePeriod { private $Seconds = 3600; public $Hours { public function get() { return $this->Seconds / 3600; } private function set($value) { $this->Seconds = $value; } /*public implied */function isset(){ return isset ($this->Seconds); } private function unset() { unset ($this->Seconds); } } } This would be much less confusing as it follows other PHP standards for creating functions and such. I know C# has a similar syntax to what is proposed, but we are not developing for C# we are developing for PHP which has its own syntax rules that differ from C#'s and my vote is to follow PHP's already in existent syntax format. --- -Nathan Bruer -----Original Message----- From: ekneuss@gmail.com [mailto:ekneuss@gmail.com] On Behalf Of Etienne Kneuss Sent: Tuesday, October 09, 2012 8:15 AM To: Nikita Popov Cc: Clint Priest; internals@lists.php.net Subject: Re: [PHP-DEV] [RFC] Propety Accessors v1.1 Hi, On Tue, Oct 9, 2012 at 3:01 PM, Nikita Popov <nikita.ppv@gmail.com> wrote:
> On Mon, Oct 8, 2012 at 1:52 PM, Clint Priest <cpriest@zerocue.com> wrote: >> It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it. >> >> RFC Document: >> https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented >> >> Example Usage: >> >> class TimePeriod { >> private $Seconds = 3600; >> >> public $Hours { >> get { return $this->Seconds / 3600; } >> set { $this->Seconds = $value; } >> isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } >> unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } >> } >> } >> >> Changes / Updates >> >> * isset/unset accessor functions now implemented (object & static context, auto implementations, etc) >> >> * static accessor now fully functional >> >> * Reference functionality validated, tests written >> >> * All operators have been tested, tests written >> >> * read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section >> >> * Tested for speed, approaches or meets __get() speed. >> >> Internally things have changed quite a bit >> >> * cleaned up and simplified >> >> * had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY) >> >> * the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed it. >> >> The code is available at the url below and is up to date with master, all tests pass. >> https://github.com/cpriest/php-src >> >> I'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated: >> >> * Check on reflection code written prior to major changes (tests still pass) >> >> * Add a few more reflection functions that were requested >> >> In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know. > > What concerns me with the current implementation is that it leaks many > implementation details, in particular the fact that the accessors are > implemented as *real* __getXYZ methods and automatic implementations > also use *real* $__XYZ properties. > > A few examples: > > ## 1 - __getProperty() method directly callable > > class Test { > public $property { > get { return 123; } > } > } > > $test = new Test; > var_dump($test->property); // int(123) > var_dump($test->__getProperty()); // int(123) > > ## 2 - __getProperty() method exposed via exception > > class Test { > public $throwingProperty { > get { throw new Exception; } > } > } > > (new Test)->throwingProperty; > > exception 'Exception' in /home/nikic/dev/php-src/t29.php:9 Stack > trace: > #0 /home/nikic/dev/php-src/t29.php(31): Test->__getthrowingProperty() > #1 {main} > > ## 3 - Can directly access $__automaticProperty and even unset it > (causing notices in the internal code) > > class Test { > public $automaticProperty { > get; set; > } > > public function getAutomaticProperty() { > return $this->__automaticProperty; > } > > public function unsetAutomaticProperty() { > unset($this->__automaticProperty); > } > } > > $test->automaticProperty = 'foo'; > var_dump($test->getAutomaticProperty()); > $test->unsetAutomaticProperty(); > var_dump($test->automaticProperty); > > string(3) "foo" > > Notice: Undefined property: Test::$__automaticProperty in > /home/nikic/dev/php-src/t29.php on line 13 NULL > > ===== > > I feel like this approach to the implementation will be a big can of > worms. Sure, it works, but it is rather fragile and the enduser ends > up dealing with internal stuff which he ought not care about. I think > it would be better to cleanly separate out the accessor > implementation. It might require more code now, but will be better in > the long run. >
I disagree, to me that this feature is all about syntactic sugar, as such it does what it expected: it generates concrete properties and methods that are somewhat hidden to the end-user. I feel that any implementation that do not rely on proper properties/methods would be a big hack. Best,
> Nikita > > -- > PHP Internals - PHP Runtime Development Mailing List To unsubscribe, > visit: http://www.php.net/unsub.php >
-- Etienne Kneuss http://www.colder.ch -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php

Leigh

13 years ago
> What concerns me with the current implementation is that it leaks many > implementation details, in particular the fact that the accessors are > implemented as *real* __getXYZ methods and automatic implementations > also use *real* $__XYZ properties.
Further to this, take the following example. public $_state { set { ... } } This automatically generates a "hidden" function __set_state(), which is a magic method already (and should be declared static, and take an array as it's only parameter) Another fine example of how automatically generating *real* implementations can potentially break things.

Leigh

13 years ago
> RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
public $property { set { $this->property = ($this->property*2)+$value } get; } How do I reference the property being set from within the function? The way I have done it in the example will cause recursion? How can I assign to "self"? How do I set the default value of $property when the object is created? Surely I don't have to reverse the set accessors logic and set the inverse in __construct?

Jazzer Dane

13 years ago
I think Leigh brings up some important flaws to in the current RFC. What Leigh is asking for does not appear to be possible, and in my opinion, it should be. I also agree with Rasmus, to a certain extent. By putting only a getter/setter, the developer essentially sets the property as read or write only. At first glance, there is no need for the read-only or write-only keyword. Except... subclassing may or may not be an issue. By setting a property to read-only, it ensures that 'set' can never be set by a subclass. Unless I redefine the entire property... or is that not even possible? *The RFC isn't very clear but it appears that there is no way to redefine the entire property, as if you define it in the subclass with only get, then it will take set, isset, and unset from the parent class, correct? * Though, that can be outright stopped by making the property final. But then there is no point in having read/write-only. From what I can tell, read-only becomes useful if I extend the class and want to partially modify the property. Example:
> class A { > public $seconds = 3600; > > public $hours { > get() { return $this->seconds / 3600 }; > } > } > > class B extends A { > public $hours { // Maintains 'get' from class A > set($value) { $this->seconds = $value; } > } > } >
^There's no way to stop the developer from doing that without read-only. Also, if the property is public, what if an outside class tries to do this:
> class A { > public $seconds = 3600; > > public $hours { > get() { return $this->seconds / 3600 }; > } > } > > $object = new A(); > $object->hours = 100; >
What happens then? And similarly, how do we set a public property as a property accessor, hmm? class A {
> public $hours = 1; > } > > $seconds = 20; > > $object = new A(); > $object->hours = { // Does this work? > get() { return $seconds; } > }; >
There's definitely still some questions to answer before this RFC is ready. On Tue, Oct 9, 2012 at 10:19 AM, Leigh <leight@gmail.com> wrote:

Clint M Priest

13 years ago
Wow, I'm surprised by all the talk about this RFC this time around. I posted this numerous times in the past trying to elicit feedback and got little to none, so I took the time to write it as I thought it should be written. Some of these things will take considerable effort to fix/correct/change. I'll try to address everyone's comments/questions in one post: ________________________________ Nikita:
> What concerns me with the current implementation is that it leaks many implementation details, in particular the fact that the accessors are implemented as *real* __getXYZ methods and automatic implementations also use *real* $__XYZ properties.
>
> A few examples:
>
> ## 1 - __getProperty() method directly callable
>
> class Test {
> public $property {
> get { return 123; }
> }
> }
>
> $test = new Test;
> var_dump($test->property); // int(123)
> var_dump($test->__getProperty()); // int(123)
>
I don't particularly see this as a problem (the ability to use it as a getter or call it as a function), but see my comments at the end. I also don't particularly see the point of auto implemented getters/setters ( public $h { get; set; } )
> ## 2 - __getProperty() method exposed via exception
>
> class Test {
> public $throwingProperty {
> get { throw new Exception; }
> }
> }
>
> (new Test)->throwingProperty;
>
> exception 'Exception' in /home/nikic/dev/php-src/t29.php:9 Stack trace:
> #0 /home/nikic/dev/php-src/t29.php(31): Test->__getthrowingProperty()
> #1 {main}
>
I have gone to great lengths to shield the implementation details from users, this one slipped past me. Nearly all errors that are shown reference a property rather than a function name and this could also be cleaned up.
> ## 3 - Can directly access $__automaticProperty and even unset it (causing notices in the internal code)
>
> class Test {
> public $automaticProperty {
> get; set;
> }
>
> public function getAutomaticProperty() {
> return $this->__automaticProperty;
> }
>
> public function unsetAutomaticProperty() {
> unset($this->__automaticProperty);
> }
> }
>
> $test->automaticProperty = 'foo';
> var_dump($test->getAutomaticProperty());
> $test->unsetAutomaticProperty();
> var_dump($test->automaticProperty);
>
> string(3) "foo"
>
> Notice: Undefined property: Test::$__automaticProperty in /home/nikic/dev/php-src/t29.php on line 13 NULL
I'm not even sure that automatic backing fields are even desired, I never felt the need to have them in C# and the only reason they were included is because they were a part of Dennis's original proposal. Eliminating them would eliminate this as an issue.
>
> =====
>
> I feel like this approach to the implementation will be a big can of worms. Sure, it works, but it is rather fragile and the enduser ends up dealing with internal stuff which he ought not care about. I think it would be better to cleanly separate out the accessor implementation. It might require more code now, but will be better in the long run.
All three of these issues, could be addressed I believe by not having the functions and/or properties a part of the ordinary HashTables, or to have flags set on them. I believe there is already a SHADOW flag type defined which may be able to be used for this type of functionality. ________________________________ Leigh:
> Further to this, take the following example.
>
> public $_state {
> set { ... }
> }
>
> This automatically generates a "hidden" function __set_state(), which is a magic method already (and should be declared static, and take an array as it's only parameter)
>
> Another fine example of how automatically generating *real* implementations can potentially break things.
This would clearly cause problems, again this could possibly be addressed by not having the accessor functions be a part of the standard Functions HashTable, the functions would not even need to have names at that point if they are not intended to be accessible directly.
> public $property {
> set { $this->property = ($this->property*2)+$value } get; }
>
> How do I reference the property being set from within the function? The way I have done it in the example will cause recursion? How can I assign to "self"?
>
> How do I set the default value of $property when the object is created?
> Surely I don't have to reverse the set accessors logic and set the inverse in __construct?
Generally speaking, I don't know why you would use an automatic backing getter with a separate setter, but if you wanted to do that, at present you would access the automatic backing field by $this->__property. The above will not cause recursion, it is protected from recursion by the same mechanism that __get() and __set() are protected. In fact, the above code would set an actual property named $property on the object which would then side-step the accessor entirely (true properties take precedence over accessors, though they may only be "set" from within the setter of the accessor. This may be a bit confusing, but I specifically wrote it this way for the purpose of lazy-loading. For example: public $objList { get { return $this->objList = new myList(); } set { $this->objList = $value; } } The first access of the $objList property getter would create the object and attempt to set its-self which would be passed to the setter, the setter (now guarded) will directly set the property on the object, further calls to $objList would retrieve the already created object (bypassing the accessor). To get out of that situation, you would simply unset($objList) and the accessor would take over again. Please keep in mind that this is only possible from within the setter of the property, nothing outside the setter of the property can create a real property named the same as the accessor, except the setter of the accessor. ________________________________ Nathan:
> This would be much less confusing as it follows other PHP standards for creating functions and such. I know C# has a similar syntax to what is proposed, but we are not developing for C# we are developing for PHP which has its own syntax rules that differ from C#'s and my vote is to follow PHP's already in existent syntax format.
I think having the function keyword there is superfluous, it adds more code to be read, while accessors have the powers of a function, they are quite different from a function. You would probably never have a 35 line getter, for example. ________________________________ Rasmus:
> My only remaining comment is on the read-only and write-only keywords...
> this seems really superfluous and strange to me - the syntax (using a hyphenated keyword) and the feature itself, is way off the grid as compared to other languages.
There is an important distinction with the read-only and write-only keywords and they are analogous to the final keyword. The final keyword prevents any subclasses from over-riding something declared as final. Final is supported for a property as well. The difference here is that a read-only property is semi-final. Subclasses may redefine a get declared as read-only but may not declare a setter. Personally I am not a fan of the concepts of final, read-only or write-only. They were included only because I thought the original RFC for which I was implementing had been ratified and the feature was desired. ________________________________ Johannes:
> About the items in regards to reflection in there:
>
> The RFC states
> ReflectionClass::getMethods() will not return accessor functions
> (hides implementation detail).
> Up until now reflection is leaky and is telling the truth. We should either keep that or completely clean up reflection. (mind also
> get_class_methods() and such)
I have not done anything with get_class_methods() but I certainly could. Probably a better solution would be to have get_class_methods() use Reflection to do its work, so that there is not duplicate code in the core? I see no reason to continue leaking new code, if someone wants to head up an effort to make the rest of Reflection not leak implementation details then great, but to purposely leak implementation details for new features just doesn't make sense to me.
> The RFC also introduces a new class ReflectionPropertyAccessor and has methods returning ReflectionProperty and ReflectionPropertyAccessor.
> there should either be a common base class or interface (see i.e.
> ReflectionFunctionAbstract)
>
I'm not certain there is a point to having a new base class, I would need to look at the code but I would bet that ReflectionPropertyAccessor is a sub-class of ReflectionProperty. In any case, we have the instanceof operator which can be used to distinguish between the results, no?
> What will ReflectionPropertyAccessor->getGetter()->getName() return? I guess reflection will be leaky there? (see first item)
I wrote the reflection additions very early on so I would need to look at the code, but I am pretty certain that the above would return the name of the property, not the function. ________________________________ Jazzer:
> I think Leigh brings up some important flaws to in the current RFC. What Leigh is asking for does not appear to be possible, and in my opinion, it should be.
I'm really not quite clear on what Leigh was going for with the code she indicated, I think she was trying to demonstrate infinite recursion which is not possible due to guards.
> I also agree with Rasmus, to a certain extent.
> By putting only a getter/setter, the developer essentially sets the property as read or write only. At first glance, there is no need for the read-only or write-only keyword. Except... subclassing may or may not be an issue.
>
> By setting a property to read-only, it ensures that 'set' can never be set by a subclass. Unless I redefine the entire property... or is that not even possible? The RFC isn't very clear but it appears that there is no way to redefine the entire property, as if you define it in the subclass with only get, then it will take set, isset, and unset from the parent class, correct? Though, that can be outright stopped by making the property final. But then there is no point in having read/write-only.
>
I tried to be clear about this at the top of the section on read-only and write-only keywords... There are three possible states: /* read-only by virtue of the fact that there is no setter, any sub-class may define a setter and/or redefine the getter */ public $Hours { get { ... } } /* final, no sub-class may redefine any aspect of this accessor */ public final $Hours { get { ... } } /* Explicitly read-only, sub-classes may redefine the getter but may not define a setter */ public read-only $Hours { get { ... } } As I mentioned earlier, I'm not a fan of the concept of final, read-only or write-only, they were only included because the original RFC indicated that was what the community had decided on.
> From what I can tell, read-only becomes useful if I extend the class and want to partially modify the property.
> Example:
> class A {
> public $seconds = 3600;
>
> public $hours {
> get() { return $this->seconds / 3600 };
> }
> }
>
> class B extends A {
> public $hours { // Maintains 'get' from class A
> set($value) { $this->seconds = $value; }
> }
> }
>
> ^There's no way to stop the developer from doing that without read-only.
That is correct
>
> Also, if the property is public, what if an outside class tries to do this:
> class A {
> public $seconds = 3600;
>
> public $hours {
> get() { return $this->seconds / 3600 };
> }
> }
>
> $object = new A();
> $object->hours = 100;
> What happens then?
A fatal error occurs as there is no setter for hours. It is a read only property by virtue of the fact that it has no setter. A sub-class could define a setter for $hours if it so desired (due to the lack of a final or read-only keyword in the declaration).
>
> And similarly, how do we set a public property as a property accessor, hmm?
> class A {
> public $hours = 1;
> }
>
> $seconds = 20;
>
> $object = new A();
> $object->hours = { // Does this work?
> get() { return $seconds; }
> };
I'm not quite sure what you mean here, are you asking if you can dynamically define a property? The answer is no, though I think that would be cool it is likely well beyond my ability to accomplish being that this is my first contribution to the php project. ________________________________ David:
> class MyClass{
>
> public $property = 5 //?can we do this?
> {
> set($value){
> if(!is_int($value)){
> throw new InvalidArgumentException('value of wrong type');
> }
> $this->__property = $value;
> }
> }
> }
>
> Or would that have to happen in the constructor?
>
> class MyClass{
>
> public $property
> {
> set($value){
> if(!is_int($value)){
> throw new InvalidArgumentException('value of wrong type');
> }
> $this->__property = $value;
> }
> }
>
> public function __construct(){
> $this->property = 5;
> }
> }
I think there is some wide-spread confusion over what an accessor is. An accessor does not store its own value, there is no "memory space" allocated for an accessor unless you use the automatic implementation ( public $h { get; set; } ) but if you're going to do that, you may as well just reduce it to ( private $__h ) which is all the previous "accessor" would have done for you. Accessors, in my own use, are nearly always used for convenience aliases with conversions ( $Hours calculated based on a real stored $Seconds value) or as an alias to access a value from another class, they are generally not used to simply store or retrieve values, that's what a property is for, an accessor is for executing code with the syntactic sugar of making it seem like it's a variable. ________________________________ In closing for all of this, I really wish that this type of discourse could have happened long ago when I was requesting it, because many, many hours have been put into the current incarnation. I would like to put together a "vote grid" or something on all that has been discussed here and come to a consensus before I go and re-do any further work. Does the php community have any voting facilities or is it still done via the wiki pages? How should I decide who gets a vote and who doesn't? Should I even be handling the voting myself?

Bernhard Schussek

13 years ago
Hi Clint, In order to achieve read-only and write-only, we could do something similar to this: /* Explicitly read-only, sub-classes may redefine the getter but may not define a setter */ public $Hours { get() { ... } final private set() {} } This would make the additional keyword superfluous. It's very similar to what is currently done in Singleton classes that usually have private constructors. Second, I'd like to throw in the idea of array accessors. I mentioned this before, but did not get any response. public $Addresses { offsetSet($offset, $value) { ... } offsetGet() { ... } offsetUnset($offset) { ... } offsetExists($offset) { ... } } The reasoning for this feature is that in object-oriented design you have to process logic when establishing bidirectional associations between objects. For example, adding an Address to a Contact instance would require Address::$Contact to be set as well if the association is bidirectional. public $Addresses { offsetSet($offset, $address) { $address->Contact = $this; } } $contact->Addresses[] = new Address(); What do you think about supporting this? Cheers, Bernhard

Leigh

13 years ago
On 10 October 2012 08:46, Bernhard Schussek <bschussek@gmail.com> wrote:
> Second, I'd like to throw in the idea of array accessors. I mentioned > this before, but did not get any response. > > public $Addresses { > offsetSet($offset, $value) { ... } > offsetGet() { ... } > offsetUnset($offset) { ... } > offsetExists($offset) { ... } > }
Definitely on the "nice to have" list.

Jazzer Dane

13 years ago
Here's my feedback on some current outstanding issues/suggestions: 1) Default value: I think having functionality for a default value is necessary, but I'm also thinking it may already be implementable within the current syntax.
> class Test { > private $seconds; > public $hours { > get() { > if(!is_null($this->seconds)) { > return $this->seconds; > } else { > return 10; // 10 is default > } > } > } > } >
The above should work fine in many scenarios, right? We could perhaps then claim that the issue may rather be that we need access to the variable *$hours* itself inside of the get/set/etc functions, which I think has been brought up before - though I'm not so sure how sensible that is. Whether we need that or not is up in the air. 2) read-only and write-only is ugly. While this is a bias, I think we can do better. One idea I had is exactly what bernhard suggesting - using *final private set() {}* to achieve read-only functionality. My problem with this implementation is that it's not as logical as I'd prefer. In my eyes, from a logical point of view, just the fact that set is there means that it works. Thus it just so happens that, while setting the variable doesn't error out, it does absolutely nothing. While I don't see any real world scenarios where people would want this behavior, I still am wrestling with this proposed implementation, as I think that, logically speaking, set should work but just not do anything. Whether or not this is illogical is arguably none of our concern - it's the coder's concern. Hmm. I'd definitely like to explore other alternatives to this solution. Already proposed:
> public read-only $property { > get() { ... } > } > public $property { > get() { ... } > final private set() {}; > } >
I'm going to throw out some alternatives just for the sake of it. They may be more illogical than I'd prefer, but I'm just trying to get the juices flowing:
> public $property { > get() { ... } > final private set; > } > public $property { > get() { ... } > final private set(); > } > public $property { > get_only() { ... } // Same as get but implies read only; set not > allowed to be defined. In case of set_only and get_only, top one has > precedence. > } > public $property { > set(read_only); > get() { ... } > } > public $property { > read_only; // or read_only(); > get() { ... } > } >
3) I'll agree with Leigh here:
> I don't like the fact that accessors and their associated properties are > implemented as direct (yet slightly obfuscated/hidden) elements of the > containing class. It's unintuitive (I'd even go as far as saying confusing). >
4) In regards to array accessors, it'd be nice to have but not at all necessary. On Wed, Oct 10, 2012 at 2:29 AM, Leigh <leight@gmail.com> wrote:

David Muir

13 years ago
On 10/10/12 21:41, Jazzer Dane wrote:
> Here's my feedback on some current outstanding issues/suggestions: > > 1) Default value: I think having functionality for a default value is > necessary, but I'm also thinking it may already be implementable within the > current syntax. > >> class Test { >> private $seconds; >> public $hours { >> get() { >> if(!is_null($this->seconds)) { >> return $this->seconds; >> } else { >> return 10; // 10 is default >> } >> } >> } >> } >> > The above should work fine in many scenarios, right? > We could perhaps then claim that the issue may rather be that we need > access to the variable *$hours* itself inside of the get/set/etc functions, > which I think has been brought up before - though I'm not so sure how > sensible that is. Whether we need that or not is up in the air. >
I think the syntax for defaults would be: class UsingInitialValue { private $seconds = 36000; public $hours { get() { return $this->seconds/3600;} } } class UsingLazyLoad { public $seconds { get() { return $this->seconds = 36000;} set($value) {$this->seconds = (int) $value;} } } class UsingConstructor { public $seconds { set($value) {$this->seconds = (int) $value;} } public function __construct(){ $this->seconds = 36000; } } I think I've got those all right... Cheers, David

Clint M Priest

13 years ago
> > Second, I'd like to throw in the idea of array accessors. I mentioned > > this before, but did not get any response. > > > > public $Addresses { > > offsetSet($offset, $value) { ... } > > offsetGet() { ... } > > offsetUnset($offset) { ... } > > offsetExists($offset) { ... } > > } > > Definitely on the "nice to have" list.
While I agree it would be a "nice to have" it would also be un-necessary. There are already ways to do precisely what is desired here by way of ArrayAccess. class Addresses implements ArrayAccess { offsetSet($offset, $value) { ... } offsetGet() { ... } offsetUnset($offset) { ... } offsetExists($offset) { ... } } // In base class where the proposed additional accessor types were suggested public $Addresses = new Addresses(); Or if you felt like using an accessor to control access to the Addresses object, you could implement that as well, but an accessor wouldn't even be necessary. This would provide for cleaner object-oriented principles, the logic for dealing with acceptance of a new address stays within a class designed to hold and control access to the collection of addresses.

Bernhard Schussek

13 years ago
2012/10/10 Clint Priest <cpriest@zerocue.com>:
> While I agree it would be a "nice to have" it would also be un-necessary. There are already ways to do precisely what is desired here by way of ArrayAccess. > > class Addresses implements ArrayAccess { > offsetSet($offset, $value) { ... } > offsetGet() { ... } > offsetUnset($offset) { ... } > offsetExists($offset) { ... } > }
This approach does not work for the use case I presented: class Addresses implements ArrayAccess { public function offsetSet($offset, $address) { $this->_Addresses[] = $address; $address->Contact = $contact; // where do we get the contact from? } } If we pass $contact to the Addresses instance, the class is bound to Contact and cannot be used anymore for different associations that * do not involve Contact * have a different arity (one-to-many: $address->Contact = $contact, many-to-many: $address->Contacts[] = $contact) Consequently, we would need to create a new collection class for each to-many association, which is neither pragmatic nor good OO design.

Leigh

13 years ago
On 10 October 2012 04:51, Clint Priest <cpriest@zerocue.com> wrote:
> Wow, I'm surprised by all the talk about this RFC this time around. I > posted this numerous times in the past trying to elicit feedback and got > little to none, so I took the time to write it as I thought it should be > written. Some of these things will take considerable effort to > fix/correct/change.
Sometimes things get overlooked. Lots of suggestions make their way to the list, usually by people who have no way to implement the feature and they disappear as quickly as they are suggested. Be happy you ARE getting feedback now. The fact people now see this as something that may be implemented as it is (in a way they don't necessarily like) is prompting them to speak up about their concerns.
>> What concerns me with the current implementation is that it leaks many >> implementation details, in particular the fact that the accessors are >> implemented as *real* __getXYZ methods and automatic implementations also >> use *real* $__XYZ properties.
> I don't particularly see this as a problem (the ability to use it as a > getter or call it as a function), but see my comments at the end. I also > don’t particularly see the point of auto implemented getters/setters ( > public $h { get; set; } )
For me the problem is simply that you can call the accessor functions directly. I don't like it. I don't like the fact that accessors and their associated properties are implemented as direct (yet slightly obfuscated/hidden) elements of the containing class. It's unintuitive (I'd even go as far as saying confusing) A property should be a property, not a collection of hidden methods and variables. If it needs to be seen whether a property implements accessors, this should be done through reflection.
>> ## 3 - Can directly access $__automaticProperty and even unset it (causing >> notices in the internal code)
> I'm not even sure that automatic backing fields are even desired
See above I guess, I agree that these "hidden" properties should not exist in the first place.
>> I think it would >> be better to cleanly separate out the accessor implementation. It might >> require more code now, but will be better in the long run.
> All three of these issues, could be addressed I believe by not having the > functions and/or properties a part of the ordinary HashTables, or to have > flags set on them. I believe there is already a SHADOW flag type defined > which may be able to be used for this type of functionality.
Nikita do you have any other proposals for how this should be addressed? I think most of my concerns with this revolve around the implementation not being cleanly separated as you put it. I believe that setters/getters would be a tremendously powerful and useful addition to the language, just not quite like this, so lets here your proposal please :)
>> public $property { >> set { $this->property = ($this->property*2)+$value } get; }
>> How do I reference the property being set from within the function? The >> way I have done it in the example will cause recursion? How can I assign to >> "self"?
> Generally speaking, I don't know why you would use an automatic backing > getter with a separate setter, but if you wanted to do that, at present you > would access the automatic backing field by $this->__property.
Kind of confusing. We have to think of the users who just want to pick this up and run with it. You can't say "I don't know why you would", because people will do strange things, and you have to make it so that the behaviour is predictable, and intuitive.
> The above will not cause recursion, it is protected from recursion by the > same mechanism that __get() and __set() are protected.
Good, thanks. I didn't get to test it since I was on a bus at the time :)
> In fact, the above > code would set an actual property named $property on the object which would > then side-step the accessor entirely (true properties take precedence over > accessors, though they may only be "set" from within the setter of the > accessor. This may be a bit confusing, but I specifically wrote it this way > for the purpose of lazy-loading.
Yep, confusing!
> The first access of the $objList property getter would create the object and > attempt to set its-self which would be passed to the setter, the setter (now > guarded) will directly set the property on the object, further calls to > $objList would retrieve the already created object (bypassing the accessor). > To get out of that situation, you would simply unset($objList) and the > accessor would take over again.
Also confusing. (Think of the users!)
>> I think Leigh brings up some important flaws to in the current RFC. What >> Leigh is asking for does not appear to be possible, and in my opinion, it >> should be.
> I'm really not quite clear on what Leigh was going for with the code she > indicated, I think she was trying to demonstrate infinite recursion which is > not possible due to guards.
She? Only when I go shoe shopping with Nikita at the weekends... Actually the recursion thing was just a query I hadn't tested, and you have answered. The other question I asked was "How do you set the default value of the property with the accessor syntax?"
> I think there is some wide-spread confusion over what an accessor is. An > accessor does not store its own value, there is no “memory space” allocated > for an accessor unless you use the automatic implementation ( public $h { > get; set; } ) but if you’re going to do that, you may as well just reduce it > to ( private $__h ) which is all the previous “accessor” would have done for > you.
> Accessors, in my own use, are nearly always used for convenience aliases > with conversions ( $Hours calculated based on a real stored $Seconds value) > or as an alias to access a value from another class, they are generally not > used to simply store or retrieve values, that’s what a property is for, an > accessor is for executing code with the syntactic sugar of making it seem > like it’s a variable.
In my opinion public $property; // This is a property public $property { // This is still a property set { ... } } I don't see accessors tied to the property as ways of simply setting some other value (in your example, setting $seconds via $hours). I have a feeling a lot of people will not be using these simply for conversions, they will also be using them for validation and sanitising. You already saw Niki throwing an Exception inside an accessor. try { $user->username = $rawData } catch (InvalidUsernameException $e) { ... } They will expect the $username property to contain the data, not $__username, and not having to implement $saneUsername as a second property to store it in.

Clint M Priest

13 years ago
> >> What concerns me with the current implementation is that it leaks > >> many implementation details, in particular the fact that the > >> accessors are implemented as *real* __getXYZ methods and automatic > >> implementations also use *real* $__XYZ properties. > > > I don't particularly see this as a problem (the ability to use it as a > > getter or call it as a function), but see my comments at the end. I > > also don't particularly see the point of auto implemented > > getters/setters ( public $h { get; set; } ) > > For me the problem is simply that you can call the accessor functions directly. I don't like it. > > I don't like the fact that accessors and their associated properties are implemented as direct (yet slightly obfuscated/hidden) > elements of the containing class. It's unintuitive (I'd even go as far as saying > confusing) > > A property should be a property, not a collection of hidden methods and variables. If it needs to be seen whether a property > implements accessors, this should be done through reflection. >
Leigh, a property IS a property, a property accessor IS a series of functions.
> > >> ## 3 - Can directly access $__automaticProperty and even unset it > >> (causing notices in the internal code) > > > I'm not even sure that automatic backing fields are even desired > > See above I guess, I agree that these "hidden" properties should not exist in the first place.
If this automatic implementation { get; set; } is not desired, I'd be happy to rip it out, I never wanted it in the first place. This is the only case in which a "real property" is automatically implemented.
> > >> I think it would > >> be better to cleanly separate out the accessor implementation. It > >> might require more code now, but will be better in the long run. > > > All three of these issues, could be addressed I believe by not having > > the functions and/or properties a part of the ordinary HashTables, or > > to have flags set on them. I believe there is already a SHADOW flag > > type defined which may be able to be used for this type of functionality. > > Nikita do you have any other proposals for how this should be addressed? I think most of my concerns with this revolve around the > implementation not being cleanly separated as you put it. I believe that setters/getters would be a tremendously powerful and useful > addition to the language, just not quite like this, so lets here your proposal please :) > > >> public $property { > >> set { $this->property = ($this->property*2)+$value } get; } > > >> How do I reference the property being set from within the function? > >> The way I have done it in the example will cause recursion? How can I > >> assign to "self"? > > > Generally speaking, I don't know why you would use an automatic > > backing getter with a separate setter, but if you wanted to do that, > > at present you would access the automatic backing field by $this->__property. > > Kind of confusing. We have to think of the users who just want to pick this up and run with it. You can't say "I don't know why you > would", because people will do strange things, and you have to make it so that the behaviour is predictable, and intuitive. >
I agree completely, what are you trying to achieve because the above code makes it look like you are thinking of accessors as properties, which they are not. Accessors do not have their own "memory space."
> > > The above will not cause recursion, it is protected from recursion by > > the same mechanism that __get() and __set() are protected. > > Good, thanks. I didn't get to test it since I was on a bus at the time :) > > > In fact, the above > > code would set an actual property named $property on the object which > > would then side-step the accessor entirely (true properties take > > precedence over accessors, though they may only be "set" from within > > the setter of the accessor. This may be a bit confusing, but I > > specifically wrote it this way for the purpose of lazy-loading. > > Yep, confusing!
Well it's confusing because you wrote it that way... thinking that somehow $property has its own "memory space" which it does not.
> > > The first access of the $objList property getter would create the > > object and attempt to set its-self which would be passed to the > > setter, the setter (now > > guarded) will directly set the property on the object, further calls > > to $objList would retrieve the already created object (bypassing the accessor). > > To get out of that situation, you would simply unset($objList) and the > > accessor would take over again. > > Also confusing. (Think of the users!)
This does not need to be used, it's simply a way it can be used. Again, the confusion I think you are running into is that you think that a property accessor has it's own "memory space," which it does not have.
> > >> I think Leigh brings up some important flaws to in the current RFC. > >> What Leigh is asking for does not appear to be possible, and in my > >> opinion, it should be. > > > I'm really not quite clear on what Leigh was going for with the code > > she indicated, I think she was trying to demonstrate infinite > > recursion which is not possible due to guards. > > She? Only when I go shoe shopping with Nikita at the weekends...
Sorry :)
> > Actually the recursion thing was just a query I hadn't tested, and you have answered. The other question I asked was "How do you set > the default value of the property with the accessor syntax?" > > > I think there is some wide-spread confusion over what an accessor is. > > An accessor does not store its own value, there is no "memory space" > > allocated for an accessor unless you use the automatic implementation > > ( public $h { get; set; } ) but if you're going to do that, you may as > > well just reduce it to ( private $__h ) which is all the previous > > "accessor" would have done for you. > > > Accessors, in my own use, are nearly always used for convenience > > aliases with conversions ( $Hours calculated based on a real stored > > $Seconds value) or as an alias to access a value from another class, > > they are generally not used to simply store or retrieve values, that's > > what a property is for, an accessor is for executing code with the > > syntactic sugar of making it seem like it's a variable. > > In my opinion > > public $property; // This is a property > > public $property { // This is still a property > set { ... } > } >
This would be where you are wrong, the 2nd $property above is not a property at all, it's a property accessor, it has no "memory space" the way a regular property does.
> I don't see accessors tied to the property as ways of simply setting some other value (in your example, setting $seconds via $hours). I > have a feeling a lot of people will not be using these simply for conversions, they will also be using them for validation and sanitising. > You already saw Niki throwing an Exception inside an accessor. > > try { > $user->username = $rawData > } catch (InvalidUsernameException $e) { > ... > } > > They will expect the $username property to contain the data, not $__username, and not having to implement $saneUsername as a > second property to store it in.
$__username only occurs if no body is provided for a getter or setter. It really seems like getting rid of auto-implemented properties is the only way to get through this. I don't want them anyways and it only seems to confuse everyone.

Nikita Popov

13 years ago
On Wed, Oct 10, 2012 at 5:51 AM, Clint Priest <cpriest@zerocue.com> wrote:
> I'm not even sure that automatic backing fields are even desired, I never > felt the need to have them in C# and the only reason they were included is > because they were a part of Dennis's original proposal. Eliminating them > would eliminate this as an issue.
I just did a bit of research regarding this topic and I have found the following reasons why automatic properties exist in C#: 1. Changing a field to a property breaks the binary interface, so all code using the library has to be recompiled. If you are using an automatic property you can safely add additional behavior for it later. 2. Properties support data binding, whereas fields do not. 3. There are attributes that work for properties, but don't work for fields. 4. Reflection for fields and properties works differently, so changing a field to a property is a BC break. I guess that #1 is the most important one (you don't want to break the interface) and it obviously does not apply to PHP. Points #2 and #3 also don't apply as PHP has neither data binding not attributes. #4 also doesn't seem to apply because ReflectionPropertyAccessor defines all the methods that ReflectionProperty defines, so changing field->property should be okay there too. From that I would conclude that the automatic properties are really not needed in PHP. I think they will only cause confusion as to when one should use `public $name;` and when one should use `public $name { get; set; };`. The only thing I'm not sure about is how read-only / write-only properties rely on automatic properties with your current implementation. If the automatic properties aren't needed there either, then I think they can be safely removed. Nikita

Clint M Priest

13 years ago
> On Wed, Oct 10, 2012 at 5:51 AM, Clint Priest <cpriest@zerocue.com> wrote: > > I'm not even sure that automatic backing fields are even desired, I > > never felt the need to have them in C# and the only reason they were > > included is because they were a part of Dennis's original proposal. > > Eliminating them would eliminate this as an issue. > > I just did a bit of research regarding this topic and I have found the following reasons why automatic properties exist in C#: > 1. Changing a field to a property breaks the binary interface, so all code using the library has to be recompiled. If you are using an > automatic property you can safely add additional behavior for it later. > 2. Properties support data binding, whereas fields do not. > 3. There are attributes that work for properties, but don't work for fields. > 4. Reflection for fields and properties works differently, so changing a field to a property is a BC break. > > I guess that #1 is the most important one (you don't want to break the > interface) and it obviously does not apply to PHP. Points #2 and #3 also don't apply as PHP has neither data binding not attributes. #4 > also doesn't seem to apply because ReflectionPropertyAccessor defines all the methods that ReflectionProperty defines, so > changing > field->property should be okay there too. > > From that I would conclude that the automatic properties are really not needed in PHP. I think they will only cause confusion as to > when one should use `public $name;` and when one should use `public $name { get; set; };`. > > The only thing I'm not sure about is how read-only / write-only properties rely on automatic properties with your current > implementation. If the automatic properties aren't needed there either, then I think they can be safely removed.
Nice, I would agree that we should eliminate the automatic get/set since the reason they exist in C# is not an issue in PHP and it has only served to confuse everyone. With the read-only and write-only, they are separate from automatic getters/setters really. If you define a read-only with {get;} it's the same as any other getter.

Johannes Schlueter

13 years ago
On Mon, 2012-10-08 at 11:52 +0000, Clint Priest wrote:
> It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it. > > RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
About the items in regards to reflection in there: The RFC states ReflectionClass::getMethods() will not return accessor functions (hides implementation detail). Up until now reflection is leaky and is telling the truth. We should either keep that or completely clean up reflection. (mind also get_class_methods() and such) The RFC also introduces a new class ReflectionPropertyAccessor and has methods returning ReflectionProperty and ReflectionPropertyAccessor. there should either be a common base class or interface (see i.e. ReflectionFunctionAbstract) What will ReflectionPropertyAccessor->getGetter()->getName() return? I guess reflection will be leaky there? (see first item) johannes

Stas Malyshev

13 years ago
Hi!
> The RFC states > ReflectionClass::getMethods() will not return accessor functions > (hides implementation detail). > Up until now reflection is leaky and is telling the truth. We should > either keep that or completely clean up reflection. (mind also > get_class_methods() and such)
I think the reflection should return all methods that exist. If the accessors are implemented as PHP methods/functions (and I see no reason why not) then reflection should return it. Reflection, as you pointed out, should tell the truth. There's nothing "leaky" about it, IMO - yes, it's an implementation detail, so what? If you don't want to use implementation details, don't - just ignore all __ functions and don't call them. Python, for example, has tons of __ functions, and people live just fine with it.
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227