[PHP-DEV [RFC] Property Accessors v1.2

php.internals

Clint M Priest

13 years ago
Alright, here is the updated RFC as per discussions for the last few days: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented If you could read it over, make sure I have all of your concerns correctly addressed and we can leave this open for the two week waiting period while I make the final code changes. -Clint

Jazzer Dane

13 years ago
I like it. I assume that, in regards to static properties, the static keyword works just as well as self and parent, correct? On Thu, Oct 11, 2012 at 10:23 PM, Clint Priest <cpriest@zerocue.com> wrote:

Clint M Priest

13 years ago
Yeah, if you check out the section in the RFC on static properties I give examples of both of these cases, I have not tested the use of static::$zz though come to think of it, I will put it on my todo list. From: Jazzer Dane [mailto:tbprogrammer@gmail.com] Sent: Friday, October 12, 2012 12:32 AM To: Clint Priest Cc: internals@lists.php.net Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2 I like it. I assume that, in regards to static properties, the static keyword works just as well as self and parent, correct? On Thu, Oct 11, 2012 at 10:23 PM, Clint Priest <cpriest@zerocue.com<mailto:cpriest@zerocue.com>> wrote: Alright, here is the updated RFC as per discussions for the last few days: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented If you could read it over, make sure I have all of your concerns correctly addressed and we can leave this open for the two week waiting period while I make the final code changes. -Clint

Bernhard Schussek

13 years ago
Hi Clint, I would like to urge you again to consider implementing the array access methods. Without that, this very neat feature becomes essentially unusable for modelling one-to-many, many-to-one and many-to-many object relations, as I tried to explain in the previous topic. Objects and relations between them are the most primitive building block of OO design, so I regard this as highly important. Thanks, Bernhard

Clint M Priest

13 years ago
Hey Bernhard, I see a few problems with the proposal: 1) If you're adding all of the ArrayAccess functions, what about all of the Iterator functions? How about other SPL aspects, like Countable, etc. 2) The use case can still be handled with an actual object which implements ArrayAccess and in a better practice scenario, unless I misunderstood what your code sample was implying. (if so, could you provide a complete, concrete example where it would be more advantageous to have these four additional accessor functions?) 3) Zero other languages that I am aware of implement anything more than get/set for accessors, the need for isset/unset is obvious for php because they are so heavily used. In fact, this document referenced in Dennis's original RFC shows 11 other languages with accessors, none of which have more than get/set. http://en.wikipedia.org/wiki/Property_%28programming%29 Lastly, it would probably delay this project another 6 months, perhaps it could be shelved for a version 2 of the accessors aspect? -Clint

Bernhard Schussek

13 years ago
Hi Clint, 1) Point taken. 2) The use case can be solved with an object implementing ArrayAccess, but not pragmatically, because then you need a class for *each* bidirectional association. Let me give a short example: Given the class Article with a bidirectional many-to-one relation to Category and a bidirectional many-to-many relation to Tag, currently you implement the following methods to manage these relations: Article::setCategory($category); Article::addTag($tag); Category::addArticle($article); // calls $article->setCategory($category) Tag::addArticle($article); // calls $article->addTag($tag) (there are different ways to implement this, but usually at least one side of the relation also calls the setter/adder on the other side) You *cannot* implement the $articles collection in Category and Tag using the same class ArticleCollection, because in one case addArticle($article) calls setCategory($category), in the other addTag($tag). In other words, ArticleCollection depends both on the related class (Category, Tag) and the arity (setXxx() vs. addXxx()) of the relation. Consequently, every relation needs a custom collection class and you end up with the classes Article Category Tag ArticleTags CategoryArticles TagArticles just for this simple example if following your approach. With array accessors, this can be solved much more elegantly: class Article { public $category { set(); get(); } public $tags { offsetSet(); offsetGet(); } } class Category { public $articles { offsetSet($offset, $article) { $this->_articles[$offset] = $article; $article->category = $this; } offsetGet(); } } class Tag { public $articles { offsetSet($offset, $article) { $this->_articles[$offset] = $article; $article->tags[] = $this; } offsetGet(); } } $article->category = $category; $article->tags[] = $tag; $category->articles[] = $article; // sets $article->category = $category $tag->articles[] = $article; // adds $article->tags[] = $tag (I know that this example has some flaws, like the automatic offsetGet() or the missing $_articles field, but I guess you get my point) 3) I don't know if many other languages allow overloading of the array accessors at all. Cheers, Bernhard

Clint M Priest

13 years ago
> 1) Point taken. > > 2) The use case can be solved with an object implementing ArrayAccess, but not pragmatically, because then you need a class for > *each* bidirectional association. Let me give a short example: > > Given the class Article with a bidirectional many-to-one relation to Category and a bidirectional many-to-many relation to Tag, > currently you implement the following methods to manage these relations: > > Article::setCategory($category); > Article::addTag($tag); > > Category::addArticle($article); // calls $article->setCategory($category) > > Tag::addArticle($article); // calls $article->addTag($tag) > > (there are different ways to implement this, but usually at least one side of the relation also calls the setter/adder on the other > side) > > You *cannot* implement the $articles collection in Category and Tag using the same class ArticleCollection, because in one case > addArticle($article) calls setCategory($category), in the other addTag($tag). In other words, ArticleCollection depends both on the > related class (Category, Tag) and the arity (setXxx() vs. addXxx()) of the relation. Consequently, every relation needs a custom > collection class and you end up with the classes > > Article > Category > Tag > ArticleTags > CategoryArticles > TagArticles > > just for this simple example if following your approach. > > With array accessors, this can be solved much more elegantly: > > class Article { > public $category { > set(); > get(); > } > > public $tags { > offsetSet(); > offsetGet(); > } > } > > class Category { > public $articles { > offsetSet($offset, $article) { > $this->_articles[$offset] = $article; > $article->category = $this; > } > offsetGet(); > } > } > > class Tag { > public $articles { > offsetSet($offset, $article) { > $this->_articles[$offset] = $article; > $article->tags[] = $this; > } > offsetGet(); > } > } > > $article->category = $category; > $article->tags[] = $tag; > > $category->articles[] = $article; // sets $article->category = $category > > $tag->articles[] = $article; // adds $article->tags[] = $tag > > (I know that this example has some flaws, like the automatic > offsetGet() or the missing $_articles field, but I guess you get my > point) >
I appreciate the desire to have these functions within accessors but I personally feel they would deviate from the purity of accessors moreso than they already are (unset/isset) and also the reasons for point #1. I'm not sure if your use case above is a specific problem you are trying to solve or if it's just an example of what it would allow, but if I may be so bold as to offer an alternative single-class solution to the collection problem you mention above. class SharedPropertyCollection implements ArrayAccess { public __construct($property, $object) { $this->PropertyName = $property; $this->PropertyValue = $object; } public offsetSet($offset, $object) { $this->_store[$offset] = $object; $object->{$this->ProperyName} = $this->PropertyValue } } Caveat: I'm not sure that $object->{$this->PropertyName} is legal, I haven't played with that new feature yet but if not, I'm sure you know the workaround to that issue. The example class above could obviously be extended to share multiple properties. It also has the additional advantage of decoupling. Your example above has a very high class interdependency issue. See if you can drum up any other interest in this feature/aspect...
> 3) I don't know if many other languages allow overloading of the array accessors at all.
Many other languages offer overloading but they refer to it as operator overloading. C++ for example (the first language I learned) has operator overloading for any operator (=, ==, [], etc). I *wish as much as I could* that PHP would allow such flexibility. SPL has really helped a lot in that area.

Lester Caine

13 years ago
Clint Priest wrote:
> Alright, here is the updated RFC as per discussions for the last few days: > > https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented > > If you could read it over, make sure I have all of your concerns correctly addressed and we can leave this open for the two week waiting period while I make the final code changes.
Now I remember why I did not like this wholesale ... There is no easy way to identify that $time->Hours = 12; is doing something other than a bog standard 'store 12 in $Hours' while a simple $time->Hours(12); immediately makes it obvious that there IS some processing involved. And period = $time->Hours(); flags that the result is calculated from some other value in the class. So IS this new layer of magic mirrors really necessary at all? Is it adding to the complexity at the expense of the inherent readability of the code later on? I suppose the argument is that 'people expect it' but that is only because they are not yet used to the better way of working that IS PHP ... just because some people expect cryptic code should that be any reason to weigh PHP down further with it?
-- 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

Pierre Joye

13 years ago
hi Lester, On Fri, Oct 12, 2012 at 10:22 AM, Lester Caine <lester@lsces.co.uk> wrote:
> Clint Priest wrote: >> >> Alright, here is the updated RFC as per discussions for the last few days: >> >> https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented >> >> If you could read it over, make sure I have all of your concerns correctly >> addressed and we can leave this open for the two week waiting period while I >> make the final code changes. > > > Now I remember why I did not like this wholesale ... > > There is no easy way to identify that > $time->Hours = 12; is doing something other than a bog standard 'store 12 in > $Hours' while a simple $time->Hours(12); immediately makes it obvious that > there IS some processing involved. And period = $time->Hours(); flags that > the result is calculated from some other value in the class. > > So IS this new layer of magic mirrors really necessary at all? Is it adding > to the complexity at the expense of the inherent readability of the code > later on? > > I suppose the argument is that 'people expect it' but that is only because > they are not yet used to the better way of working that IS PHP ... just > because some people expect cryptic code should that be any reason to weigh > PHP down further with it? >
Please keep the discussion sane without repeating the same arguments over and over again. This RFC is about adding a new cleaner getter/setter than the __get/__set we have now, which forces us to do some more useless work for basic needs. The complexity you are talking about already exist and you already mentioned this argument already, we got it. Cheers,
-- Pierre @pierrejoye | http://blog.thepimp.net | http://www.libgd.org

Lester Caine

13 years ago
Pierre Joye wrote:
> hi Lester, > > On Fri, Oct 12, 2012 at 10:22 AM, Lester Caine <lester@lsces.co.uk> wrote: >> Clint Priest wrote: >>> >>> Alright, here is the updated RFC as per discussions for the last few days: >>> >>> https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented >>> >>> If you could read it over, make sure I have all of your concerns correctly >>> addressed and we can leave this open for the two week waiting period while I >>> make the final code changes. >> >> >> Now I remember why I did not like this wholesale ... >> >> There is no easy way to identify that >> $time->Hours = 12; is doing something other than a bog standard 'store 12 in >> $Hours' while a simple $time->Hours(12); immediately makes it obvious that >> there IS some processing involved. And period = $time->Hours(); flags that >> the result is calculated from some other value in the class. >> >> So IS this new layer of magic mirrors really necessary at all? Is it adding >> to the complexity at the expense of the inherent readability of the code >> later on? >> >> I suppose the argument is that 'people expect it' but that is only because >> they are not yet used to the better way of working that IS PHP ... just >> because some people expect cryptic code should that be any reason to weigh >> PHP down further with it? >> > > Please keep the discussion sane without repeating the same arguments > over and over again. This RFC is about adding a new cleaner > getter/setter than the __get/__set we have now, which forces us to do > some more useless work for basic needs. The complexity you are talking > about already exist and you already mentioned this argument already, > we got it.
Sorry , but if the complexity was any good, why does it need to be changed? Simply scrap the whole lot and get back to a more sane solution. I'm sorry but this is just the whole problem with PHP 'development' ... people push half developed ideas in, and then it's used as the basis for moving things further forward, when a step back MAY be the best way forward! Has ANY of the new stuff that has been added to PHP recently ACTUALLY been fully finished? In many cases you are now discussion how to make them work better because what exists has problems that will be fixed later :( And we have to live with the consequences in the real world.
-- 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

Pierre Joye

13 years ago
On Fri, Oct 12, 2012 at 10:49 AM, Lester Caine <lester@lsces.co.uk> wrote:
> Sorry , but if the complexity was any good, why does it need to be changed? > Simply scrap the whole lot and get back to a more sane solution. I'm sorry > but this is just the whole problem with PHP 'development' ...
We know that you don't like modern solutions, we really know it by now. Please move on.
-- Pierre @pierrejoye | http://blog.thepimp.net | http://www.libgd.org

Lester Caine

13 years ago
Pierre Joye wrote:
>> Sorry , but if the complexity was any good, why does it need to be changed? >> >Simply scrap the whole lot and get back to a more sane solution. I'm sorry >> >but this is just the whole problem with PHP 'development' ... > We know that you don't like modern solutions, we really know it by > now. Please move on.
getters and setters modern? I was using them in C++ in the 90's and it was NICE to be able to avoid them in PHP when I started moving code over in the early 2000's. But I've just looked back at that code to remind me how it was done there and one forgets how tidy C++ is! The getters and setters were simply implemented in a library function that one of my other core libraries used. They were never actually defined in C++ at all? One never had direct access to the class variables so never missed it. It comes back to what is needed in the core code, and what can simply be selected by those who thing they need it ... I just hope we do not end up on the Python bandwagon of dropping core functions simply because they do not fit with the 'modern way' Python2 is going to exist for a long time simply because people don't have time to rewrite code for P3 ... PHP seems to be going after the same 'modern approach' :( Anyway back to TRYING to get a new server up and running WITH Apache2.4 and PHP5.4 when the distro is still on 2.2 and 5.3 ... It will be some years before any of this can be used generally in ISP hosting ...
-- 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

Nikita Popov

13 years ago
On Fri, Oct 12, 2012 at 7:23 AM, Clint Priest <cpriest@zerocue.com> wrote:
> Alright, here is the updated RFC as per discussions for the last few days: > > https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented > > If you could read it over, make sure I have all of your concerns correctly addressed and we can leave this open for the two week waiting period while I make the final code changes. > > -Clint
I've been thinking a bit about the automatic properties again, and noticed that I forgot to name one use case: Asymmetric accessor visibility. Automatic properties may be useful in that context, so that you can write "public $foo { get; protected set; }" (though they don't necessarily need to be implemented as properties with auto generated code, rather just properties with more detailed visibility handling [a bit related to the stuff Amaury has been saying]). Nikita

Cal

13 years ago
Le 12/10/2012 11:48, Nikita Popov a écrit :
> I've been thinking a bit about the automatic properties again, and > noticed that I forgot to name one use case: Asymmetric accessor > visibility. Automatic properties may be useful in that context, so > that you can write "public $foo { get; protected set; }" (though they > don't necessarily need to be implemented as properties with auto > generated code, rather just properties with more detailed visibility > handling [a bit related to the stuff Amaury has been saying]).
I agree.

Kingsquare.nl - Robin Speekenbrink

13 years ago
paf! On vrijdag 12 oktober 2012 13:50:34, Cal wrote:

Kingsquare.nl - Robin Speekenbrink

13 years ago
Please ignore me... i'm a n00b and my mail programme F*cked me... my bad Regards, Robin Speekenbrink Kingsquare BV On vrijdag 12 oktober 2012 13:50:34, Cal wrote:

Christian Stoller

13 years ago
Good hint, Nikita. I like the feature of automatic properies. For the problems with unauthorized access: What about signing automated generated "variables", so that they are only accessable through the property? @Lester Caine: This properties do not break backward compatibility. If you do not want to use it, you do not need. I like them because they offer an easy, short and well readable way to protected object or class variables. Best regards Christian Stoller -----Original Message----- From: Nikita Popov [mailto:nikita.ppv@gmail.com] Sent: Friday, October 12, 2012 12:48 PM To: Clint Priest Cc: internals@lists.php.net Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2 On Fri, Oct 12, 2012 at 7:23 AM, Clint Priest <cpriest@zerocue.com> wrote:
> Alright, here is the updated RFC as per discussions for the last few days: > > https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented > > If you could read it over, make sure I have all of your concerns correctly addressed and we can leave this open for the two week waiting period while I make the final code changes. > > -Clint
I've been thinking a bit about the automatic properties again, and noticed that I forgot to name one use case: Asymmetric accessor visibility. Automatic properties may be useful in that context, so that you can write "public $foo { get; protected set; }" (though they don't necessarily need to be implemented as properties with auto generated code, rather just properties with more detailed visibility handling [a bit related to the stuff Amaury has been saying]). Nikita
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php

Lester Caine

13 years ago
Christian Stoller wrote:
> @Lester Caine: This properties do not break backward compatibility. If you do not want to use it, you do not need. > I like them because they offer an easy, short and well readable way to protected object or class variables.
The problem is not my own use Christian ... like many of the facilities that have been added in PHP4.3 and 5.4, they get taken up by frameworks and third party libraries, so either one has to stay with libraries that you can understand and are compatible or learn all the foibles of these new features simply to track NEW bugs created in perfectly good code that was working until someone upgraded a library. My comment about 'finding a good distro' is very pertinent here, since the one I am currently playing with Fedora 17 provides many of the pear and other libraries in the distro, but the pear ones have to be avoided since they are not strict complaint, and the other graphics libraries need to be reworked for PHP5.4. While 'it does not break backward compatibility', getting legacy websites to run on a more modern setup is not a quick job, and simply ignoring or switching off all the warnings does not help when there IS something which needs fixing! I would bet that 80% of code in use today needs reworking for PHP5.3 even let alone 5.4. I wish now I had simply forked PHP5.2 and was maintaining security fixes on that, rather than NOW having to patch customers legacy sites because the version of PHP5.2 their ISP is running has newly identified holes! We simply don't have a LTS nail to work from and for which we can maintain security, and none of these developments are helping to solve the problem. ANOTHER ISP has just rolled back to 5.2 because of the problems with legacy sites on 5.3 and this is a bigger problem for PHP than fixing something that was broken when it was first implemented at all ...
-- 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

Clint M Priest

13 years ago
The problem with that Nikita is that it would need a variable storage location, which would mean a hidden, auto-implemented property. You were dead-set against that from the get go. Keep in mind that property accessors are not properties, real properties will take over the accessor, this could only be done from within the setter of an accessor, but there isn't any way to create an accessor named $Hours which accesses a variable named $Hours. In any case, your example below could be implemented without automatic creation simply by doing the backing yourself, such as: private $myFoo; public $foo { get() { return $this->myFoo; } protected set($x) { $this->myFoo = $x; } } Again, without another "variable store" automatic properties goes out the door, can't really have one without the other. Shall I hold off on this discussion point before proceeding?

Nikita Popov

13 years ago
On Sat, Oct 13, 2012 at 6:47 AM, Clint Priest <cpriest@zerocue.com> wrote:
> The problem with that Nikita is that it would need a variable storage location, which would mean a hidden, auto-implemented property. You were dead-set against that from the get go.
What I was mainly against was the particular way how automatic properties were implemented (with a real __property as a protected backing field, which was directly exposed to the user). But automatic properties don't really need that, or at least I don't see why. Just "public $foo { get; set; }" doesn't really need anything, it can be equivalent to "public $foo" (not that it makes much sense having it in that case ^^). "public $foo { get; protected set; }" can also be implemented without automagically generated accessor methods. This is what Amaury's patch does, just with a different syntax (it provides more detailed visibility handling, but does not need accessors). Another reason why it might be better to not use generated accessors for this, but rather just more detailed visibility flags for properties, is performance. Right now "public $foo" would be a "fast" property, but "public $foo { get; protected set; }" would turn it into a "slow" method based property. Not sure how important that is though :)
> Keep in mind that property accessors are not properties, real properties will take over the accessor, this could only be done from within the setter of an accessor, but there isn't any way to create an accessor named $Hours which accesses a variable named $Hours.
"Isn't a way" as in "not possible in the current implementation" or as in "not possible due to technical limitations in the engine"? I was going to suggest to allow accessing the $foo property from within the $foo accessor as to avoid having to create a separate protected backing property. But if that's not possible for technical reasons then that obviously won't work out :/
> In any case, your example below could be implemented without automatic creation simply by doing the backing yourself, such as: > > private $myFoo; > > public $foo { > get() { return $this->myFoo; } > protected set($x) { $this->myFoo = $x; } > }
Yes, sure you can implement it manually. Automatic properties are only there to make it simpler. I can't exactly judge just how important asymmetric visibility will be, so I don't know just how important it is to make it short and easy. I can tell that Symfony has 3x more getters than setters and ZF has 1.6x more getters. But I'm not sure whether this means "many properties with asymmetric visibility" or "many properties with just a getter".
> Shall I hold off on this discussion point before proceeding?
Yes, please. As there isn't really a consensus on this it would be better to wait a bit more. Nikita

Clint M Priest

13 years ago
> > On Sat, Oct 13, 2012 at 6:47 AM, Clint Priest <cpriest@zerocue.com> wrote: > > The problem with that Nikita is that it would need a variable storage location, which would mean a hidden, auto-implemented property. You were dead-set against that from the get go. > What I was mainly against was the particular way how automatic properties were implemented (with a real __property as a protected > backing field, which was directly exposed to the user). But automatic properties don't really need that, or at least I don't see why. Just > "public $foo { get; set; }" doesn't really need anything, it can be equivalent to "public $foo" (not that it makes much sense having it in > that case ^^). "public $foo { get; protected set; }" can also be implemented without automagically generated accessor methods. This is > what Amaury's patch does, just with a different syntax (it provides more detailed visibility handling, but does not need accessors). > Another reason why it might be better to not use generated accessors for this, but rather just more detailed visibility flags for > properties, is performance. Right now "public $foo" would be a "fast" > property, but "public $foo { get; protected set; }" would turn it into a "slow" method based property. Not sure how important that is > though > :) > > > Keep in mind that property accessors are not properties, real properties will take over the accessor, this could only be done from within the setter of an accessor, but there isn't any way to create an accessor named $Hours which accesses a variable named $Hours. > "Isn't a way" as in "not possible in the current implementation" or as in "not possible due to technical limitations in the engine"? I was > going to suggest to allow accessing the $foo property from within the $foo accessor as to avoid having to create a separate protected > backing property. But if that's not possible for technical reasons then that obviously won't work out :/ >
As it is presently written, if there is a real $foo property and a real $foo accessor, the $foo accessor is never called. This works exactly like it would if it were __get(), in the code where __get() would be called, a check is made for an getter with the name $foo and if it is exists, that is called in lieu of __get. This aspect is also why lazy loading can work. If you have an accessor $foo with a getter that "lazy loads" something expensive and then sets $foo (via its own setter), the next time $foo is accessed it is a regular, standard public property and the "lazy load" never gets called again (unless the real property is unset).
> > In any case, your example below could be implemented without automatic creation simply by doing the backing yourself, such as: > > > > private $myFoo; > > > > public $foo { > > get() { return $this->myFoo; } > > protected set($x) { $this->myFoo = $x; } } > Yes, sure you can implement it manually. Automatic properties are only there to make it simpler. I can't exactly judge just how > important asymmetric visibility will be, so I don't know just how important it is to make it short and easy. I can tell that Symfony has 3x > more getters than setters and ZF has 1.6x more getters. But I'm not sure whether this means "many properties with asymmetric > visibility" or "many properties with just a getter". > > > Shall I hold off on this discussion point before proceeding? > Yes, please. As there isn't really a consensus on this it would be better to wait a bit more. > > Nikita
So it sounds to me like you aren't looking for "auto implementation" but rather asymmetric visibility for properties... (aka Amaury's feature). One of my design goals was to *not* create an additional "memory store" for each accessor as this is not what other languages do, this is also why the auto implementation created a separate memory store, so that every other accessor that didn't need to store data of its own would be free from the memory overhead of having an unused memory spot. public $foo { get; protected set; } wouldn't be a problem to be translated to mean "I don't really want an accessor, I want to define asymmetric visibility on a property," the difficulty is what to do when something like this is defined: public $foo { get; protected set($x) { ... } } Now we have a difficult situation. There is a real property of $foo so no getter is called, it is referenced directly and in the case of setting it, clearly the desired functionality given the definition is that the setter would be called if $foo is attempted to be set within the right visibility scope. The difficulty is that to make the above work, accessors and properties must merge, a getter and setter check must be made on any property access which will slow things down. This is why, right now, properties and accessors are separated concepts, so that there is no additional over-head for real properties.

Levi Morrison

13 years ago
On Thu, Oct 11, 2012 at 11:23 PM, Clint Priest <cpriest@zerocue.com> wrote:
> Alright, here is the updated RFC as per discussions for the last few days: > > https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented > > If you could read it over, make sure I have all of your concerns correctly addressed and we can leave this open for the two week waiting period while I make the final code changes. > > -Clint >
I'm not sure if that RFC is up to date; are `ZEND_ACC_READONLY` and `ZEND_ACC_WRITEONLY` still there? Are there still 79 test cases? I'd like to see wider coverage if you can think of more things to tests. Ideally every conditional statement has a test. I used to be very against this RFC but I admit the details have improved a lot with the ability to type hint and the removal of read and write only keywords. Levi Morrison

Clint M Priest

13 years ago
I must have missed taking that part out of the RFC, they would not be needed any more with those keywords being removed.

Benjamin Eberlei

13 years ago
On Fri, Oct 12, 2012 at 7:23 AM, Clint Priest <cpriest@zerocue.com> wrote:
> Alright, here is the updated RFC as per discussions for the last few days: > > https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented > > If you could read it over, make sure I have all of your concerns correctly > addressed and we can leave this open for the two week waiting period while > I make the final code changes. > > -Clint > >
Can we discuss the removal of automatic get; set; again? For me that was the best feature of the whole RFC, essentially allowing to define a property with getter/setter in 1 LOC. Allowing future overriding of that getter/Setter if the application required it. This is absolutely essential for database backed recods which will have tons of those property accessors. Now if i apply PSR coding styles to accessors, then i end up with 11 LOC for accessors defining a property, a property accessor and get/set functions, compared to 10 LOC with usual getter/setters. As i understand it was removed because of the unclear behavior when we have the following code: public $Foo { get(); set($value) { $this->??? = $value; }} I propose to reintroduce this shortcut, if you define both getter/setter as automatic: public $Foo { get(); set(); } The syntax is up for grabs, but it would be very sad if this be removed. greetings, Benjamin

Nikita Popov

13 years ago
On Sat, Oct 13, 2012 at 10:06 AM, Benjamin Eberlei <kontakt@beberlei.de> wrote:
> Can we discuss the removal of automatic get; set; again? For me that was > the best feature of the whole RFC, essentially allowing to define a > property with getter/setter in 1 LOC. Allowing future overriding of that > getter/Setter if the application required it. > > This is absolutely essential for database backed recods which will have > tons of those property accessors. Now if i apply PSR coding styles to > accessors, then i end up with 11 LOC for accessors defining a property, a > property accessor and get/set functions, compared to 10 LOC with usual > getter/setters.
Not sure I quite understand you. What prevents you from just using a normal public property? public $property; Accessors give you the ability to add alternative behavior for the property lateron (this is actually the main use I personally see in this RFC: Not actually using it, but having the possibility of using it). The only place (which I see off the top of my head) where property / automatic property makes a difference is interfaces. In the current implementation interfaces can only define properties with accessors. So: interface Foo { // this is okay public $abc { get; set; } // this is invalid public $abc; } And similarly you can't implement the public $abc { get; set; } definition from the interface using a plain property. I think that properties should be definable in interfaces, or at a normal property "public $abc;" should satisfy the interface "public $abc { get; set; }" (I mean, it kinda does from a functional point of view, right?) Nikita

Benjamin Eberlei

13 years ago
On Sat, Oct 13, 2012 at 11:29 AM, Nikita Popov <nikita.ppv@gmail.com> wrote:
> On Sat, Oct 13, 2012 at 10:06 AM, Benjamin Eberlei <kontakt@beberlei.de> > wrote: > > Can we discuss the removal of automatic get; set; again? For me that was > > the best feature of the whole RFC, essentially allowing to define a > > property with getter/setter in 1 LOC. Allowing future overriding of that > > getter/Setter if the application required it. > > > > This is absolutely essential for database backed recods which will have > > tons of those property accessors. Now if i apply PSR coding styles to > > accessors, then i end up with 11 LOC for accessors defining a property, a > > property accessor and get/set functions, compared to 10 LOC with usual > > getter/setters. > > Not sure I quite understand you. What prevents you from just using a > normal public property? > > public $property; >
Ah yes, of course. Sorry, stupid me :-)

Amaury Bouchard

13 years ago
2012/10/13 Nikita Popov <nikita.ppv@gmail.com>
> interface Foo { > // this is okay > public $abc { get; set; } > > // this is invalid > public $abc; > } >
Sorry, I missed something. Why the first should be correct but not the second one? For me it's exactly the same thing.

Clint M Priest

13 years ago
Interfaces are used to define what methods must be present, properties are not allowed. From: amaury.bouchard@gmail.com [mailto:amaury.bouchard@gmail.com] On Behalf Of Amaury Bouchard Sent: Saturday, October 13, 2012 5:06 AM To: Nikita Popov Cc: Benjamin Eberlei; Clint Priest; internals@lists.php.net Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2 2012/10/13 Nikita Popov <nikita.ppv@gmail.com<mailto:nikita.ppv@gmail.com>> interface Foo { // this is okay public $abc { get; set; } // this is invalid public $abc; } Sorry, I missed something. Why the first should be correct but not the second one? For me it's exactly the same thing.

Amaury Bouchard

13 years ago
2012/10/13 Clint Priest <cpriest@zerocue.com>
> Interfaces are used to define what methods must be present, properties > are not allowed. >
Yes, so no one should be correct, right? I mean, yes the first declaration implies some code; but for the interface, it's still a property definition.

Clint M Priest

13 years ago
Interfaces are used to define what methods must be present, properties are not allowed. Yes, so no one should be correct, right? I mean, yes the first declaration implies some code; but for the interface, it's still a property definition. You're mixing concepts here, it's an accessor definition, not a property definition. property != accessor, an accessor just happens to look and act like a property (which is the point of accessors). From: amaury.bouchard@gmail.com<mailto:amaury.bouchard@gmail.com> [mailto:amaury.bouchard@gmail.com<mailto:amaury.bouchard@gmail.com>] On Behalf Of Amaury Bouchard Sent: Saturday, October 13, 2012 5:06 AM To: Nikita Popov Cc: Benjamin Eberlei; Clint Priest; internals@lists.php.net<mailto:internals@lists.php.net> Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2 2012/10/13 Nikita Popov <nikita.ppv@gmail.com<mailto:nikita.ppv@gmail.com>> interface Foo { // this is okay public $abc { get; set; } // this is invalid public $abc; } Sorry, I missed something. Why the first should be correct but not the second one? For me it's exactly the same thing.

Amaury Bouchard

13 years ago
2012/10/13 Clint Priest <cpriest@zerocue.com>
> Interfaces are used to define what methods must be present, properties > are not allowed.**** > > ** ** > > Yes, so no one should be correct, right?**** > > I mean, yes the first declaration implies some code; but for the > interface, it's still a property definition.**** > > ** ** > > You’re mixing concepts here, it’s an accessor definition, not a property > definition. property != accessor, an accessor just happens to look and act > like a property (which is the point of accessors). >
Interfaces define methods, not properties. Fine. But is an accessor (as defined in the RFC) a method? Or should we consider that an accessor definition is valid inside an interface? I would say no, because it will be used as a property: outside of the object that implements the accessor, nobody know if it's an attribute or an accessor function. It's the whole point of the RFC (apart from the asymetric visibility, but you know my point of view). So, for me, this code should be incorrect: interface Fooable { public $abc { get; set; } } Because if an object implements the interface: class Fooer implements Fooable { public $abc { get { /* what you want */ } set { /* what you want too */ } } } How this code will be used? Like that: $foo = new Fooer(); $foo->abc = 3; print($foo->abc); Everybody will agree with me that $abc is used like a property, not as a method. So the language should enforce that. There is a real issue here; this is not a fad from me.

Clint M Priest

13 years ago
-Clint On Oct 13, 2012, at 4:21 PM, "Amaury Bouchard" <amaury@amaury.net<mailto:amaury@amaury.net>> wrote: 2012/10/13 Clint Priest <cpriest@zerocue.com<mailto:cpriest@zerocue.com>> Interfaces are used to define what methods must be present, properties are not allowed. Yes, so no one should be correct, right? I mean, yes the first declaration implies some code; but for the interface, it's still a property definition. You’re mixing concepts here, it’s an accessor definition, not a property definition. property != accessor, an accessor just happens to look and act like a property (which is the point of accessors). Interfaces define methods, not properties. Fine. But is an accessor (as defined in the RFC) a method? Or should we consider that an accessor definition is valid inside an interface? I would say no, because it will be used as a property: outside of the object that implements the accessor, nobody know if it's an attribute or an accessor function. It's the whole point of the RFC (apart from the asymetric visibility, but you know my point of view). So, for me, this code should be incorrect: interface Fooable { public $abc { get; set; } } Because if an object implements the interface: class Fooer implements Fooable { public $abc { get { /* what you want */ } set { /* what you want too */ } } } How this code will be used? Like that: $foo = new Fooer(); $foo->abc = 3; print($foo->abc); Everybody will agree with me that $abc is used like a property, not as a method. So the language should enforce that. There is a real issue here; this is not a fad from me. An accessor is a method and an interface which defines an accessor is indicating that the accessor must be implemented by any using the interface. See: http://msdn.microsoft.com/en-US/library/64syzecx(v=vs.80).aspx Also, "used like a property" does not mean it is a property, it is a method call with the syntax of accessing a property, it is still a method call. I believe __get() may be declared in an interface and if so, then accessors should be as well.

Amaury Bouchard

13 years ago
You are right, we can define __get() in an interface. So, in the end I will shut my mouth. But still, it's not logical. An interface is a contract which defines the capacities of an object. If an interface defines the entry point swim(), we know that an object which implements this interface is able to swim. The goal is to define what is callable in an implementation (the basics of duck typing: if it can swim() and fly(), it's a flying fish). In PHP, it was decided that interfaces may not include member variables. I guess it's a philosophical choice; you give orders to an object, knowing what it can do (thanks to the interface). And orders are given by calling methods, not by setting properties. As I understand your explanation, you think it's normal to be able to define accessors in an interface, because it means there is some code behind. But from my point of view, the implementation (in the PHP interpreter) is not relevant; it's a design choice first. Take a look at this code: interface Fooable { public $aaa { get; set; } } class Fooer implements Fooable { public $aaa { get { /* wathever ... */ } set { /* ... you want */ } } } class Bar { public function do() { $foo = new Fooer(); $foo->aaa = 3; } } The Bar object doesn't care if $aaa is an attribute or an accessor. It is used the same way. So, if we agree that this code is correct, I understand that something like "$foo->aaa" is a valid entry point to communicate with an object through an interface. TIt means that an API is not only defined by methods. You can argue that an accessor is different from a property, but again, only Fooer knows that, Bar shouldn't have to deal with that. Then, for me there is two logical choices: - Forbid accessors in interfaces, as properties are already forbidden. - Allow accessors in interfaces => allow properties too. But it is a complete redefinition of interfaces in PHP. I saw the link you gave to me. Well, as I said before, there is a lot of good ideas in C#, but I think PHP is an opinionated language and we can do our own choices (see how namespaces work in a very different way from C++ namespaces, for example). As you point out, __get() implies the choice has already be done. So I shut up now :-) 2012/10/14 Clint Priest <cpriest@zerocue.com>

Nikita Popov

13 years ago
On Fri, Oct 12, 2012 at 7:23 AM, Clint Priest <cpriest@zerocue.com> wrote:
> Alright, here is the updated RFC as per discussions for the last few days: > > https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented > > If you could read it over, make sure I have all of your concerns correctly addressed and we can leave this open for the two week waiting period while I make the final code changes. > > -Clint
So here comes my round of feedback on the current proposal. But before getting to that: I have collected a bit of data how getter and setter are currently used in Symfony and ZF: https://gist.github.com/3884203 Mainly so I get a better overview of the situation, but might be of interest for other people involved in this discussion too. So, my points on the proposal, in no particular order: 1. Typehints ----------------- The last revision of the proposal added the ability to typehint the set() accessor, so that's a good start. Still I would like to go a bit further on this point. My data says that about 20-30% of all setters are typehinted and it is highly probable that if we had scalar typehints (as I'm sure we will have in the future) that number would be even larger. In particular I would like to have an easy way to typehint if I *don't* want to override any additional behavior. Currently that would require me to write something along the lines of this: protected $_date; public $date { get() { return $this->_date; } set(DateTime $date) { $this->_date = $date; } } This is a rather lot of boilerplate code just to add a typehint. With automatic properties this might be reduced to this: public $date { get(); set(DateTime $date); } This is already a lot nicer, because I don't have to implement the same getting and setting pattern again and again, but I still find that it doesn't do a particularly good job at conveying its intention. Rather, what that code *really* wants to say is just this: public DateTime $date; I think that it would be very beneficial to support this syntax, because I think that a large part of the use of the accessor proposal will just be type checking. But if we indeed want to syntax we should reconsider the get {} set { $value; } syntax again, because as I understand it the new get() {} set($value) {} syntax was mainly introduced to deal with setter typehints. If we chose to allow typehints before the property name, then that wouldn't be necessary anymore. 2. Interfaces ------------------ I already mentioned this before, but I'll reiterate it here again: Currently properties and properties with accessors are handled different with regards to interfaces. In particular, if you have an interface such as follows: interface Extension { public $name { get; } // more stuff } then the following is not a valid implementation of the interface: class FooExtension implements Extension { public $name = 'foo'; // more stuff } This does not make sense to me: $foo->name is a gettable property, as such it satisfies the interface. I don't see a reason why we would need a hard distinction between properties and properties with accessors here. The converse is also true: An interface currently can not define a "plain" property: interface Foo { public $foo; } I would expect this interface definition to be the same as "public $foo { get; set; }". But I'm not sure on this point, as one could argue that it's just a redundant way to say the same thing. 3. Protected backing property ------------------------------------------ The RFC currently contains just a single code example, namely setting a $Seconds property through an $Hours property, which is an example where you want to override both accessors and there is a backing property that already exists "naturally". If you on the other hand you consider cases where you only want to override either the set or the get behavior and there doesn't already exist a backing property, you will end up with a code like this: protected $_isEnabled; public $isEnabled { get() { return $this->_isEnabled; } set($isEnabled) { $this->_isEnabled = (bool) $isEnabled; } } The above code really only adds a bool cast, but still it has to do two things: Add a protected backing property and define a getter which basically matches the default behavior. With automatic properties as they are currently implemented you could write: public $isEnabled { get(); set($isEnabled) { $this->__isEnabled = (bool) $isEnabled; } } This is closer to what I actually want to do, but it relies on using $this->__isEnabled, which is really just an implementation detail. Instead, I would rather want to have access to the "underlying" property: public $isEnabled { get(); set($isEnabled) { $this->isEnabled = (bool) $isEnabled; } } I know that the wording is a bit vague; another way to put it, is that the above inverts the current shadowing behavior: In the current implementation, if there exists a "plain" property and an "accessor" property with the same name, then the plain property shadows the accessor property. As you say, this is to help lazy-loading. I don't really buy the argument about lazy-loading, because I don't think that the small perf difference between going trough an accessor and accessing a plain property is really relevant in that context (especially as lazy-loading would have required a method otherwise). What I'd rather see is the reverse behavior: An accessor property shadows a plain property, so that the plain property is only accessible from within the accessor methods. This would allow you to write code like in the last example above and I think it would also make automatic properties more meaningful (you could store the state in a property with the same name; also nicely integrating with serialization and debugging). I know that you (Clint) want to create a hard distinction between plain properties and accessor properties, but I think that making it more smooth juts integrated better with PHP. I already noticed several people who, after reading the RFC, tried to write code where they access the property from within the accessor. It seems to me that people think it should work. The current behavior where you can shadow the accessor property by assigning a property of the same name seems rather obscure to me. This is the feedback I have for now. Looking forward to your thoughts on the manner. Nikita

Etienne Kneuss

13 years ago
On Sun, Oct 14, 2012 at 2:50 PM, Nikita Popov <nikita.ppv@gmail.com> wrote:
> On Fri, Oct 12, 2012 at 7:23 AM, Clint Priest <cpriest@zerocue.com> wrote: >> Alright, here is the updated RFC as per discussions for the last few days: >> >> https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented >> >> If you could read it over, make sure I have all of your concerns correctly addressed and we can leave this open for the two week waiting period while I make the final code changes. >> >> -Clint > > So here comes my round of feedback on the current proposal. > > But before getting to that: I have collected a bit of data how getter > and setter are currently used in Symfony and ZF: > https://gist.github.com/3884203 Mainly so I get a better overview of > the situation, but might be of interest for other people involved in > this discussion too. > > So, my points on the proposal, in no particular order: > > 1. Typehints > ----------------- > > The last revision of the proposal added the ability to typehint the > set() accessor, so that's a good start. Still I would like to go a bit > further on this point. My data says that about 20-30% of all setters > are typehinted and it is highly probable that if we had scalar > typehints (as I'm sure we will have in the future) that number would > be even larger. In particular I would like to have an easy way to > typehint if I *don't* want to override any additional behavior. > Currently that would require me to write something along the lines of > this: > > protected $_date; > public $date { > get() { return $this->_date; } > set(DateTime $date) { $this->_date = $date; } > } > > This is a rather lot of boilerplate code just to add a typehint. With > automatic properties this might be reduced to this: > > public $date { > get(); > set(DateTime $date); > } > > This is already a lot nicer, because I don't have to implement the > same getting and setting pattern again and again, but I still find > that it doesn't do a particularly good job at conveying its intention. > Rather, what that code *really* wants to say is just this: > > public DateTime $date; > > I think that it would be very beneficial to support this syntax, > because I think that a large part of the use of the accessor proposal > will just be type checking. But if we indeed want to syntax we should > reconsider the get {} set { $value; } syntax again, because as I > understand it the new get() {} set($value) {} syntax was mainly > introduced to deal with setter typehints. If we chose to allow > typehints before the property name, then that wouldn't be necessary > anymore. > > 2. Interfaces > ------------------ > > I already mentioned this before, but I'll reiterate it here again: > Currently properties and properties with accessors are handled > different with regards to interfaces. In particular, if you have an > interface such as follows: > > interface Extension { > public $name { get; } > // more stuff > } > > then the following is not a valid implementation of the interface: > > class FooExtension implements Extension { > public $name = 'foo'; > // more stuff > } > > This does not make sense to me: $foo->name is a gettable property, as > such it satisfies the interface. I don't see a reason why we would > need a hard distinction between properties and properties with > accessors here.
I have to agree here. Interfaces are means to describe the capabilities of an object from the outside. And from the outside, properties defined through accessors are used like properties, not methods. It would IMHO be better not to support accessors in interfaces until properties are also (if ever) supported. Best,
> > The converse is also true: An interface currently can not define a > "plain" property: > > interface Foo { > public $foo; > } > > I would expect this interface definition to be the same as "public > $foo { get; set; }". But I'm not sure on this point, as one could > argue that it's just a redundant way to say the same thing. > > 3. Protected backing property > ------------------------------------------ > > The RFC currently contains just a single code example, namely setting > a $Seconds property through an $Hours property, which is an example > where you want to override both accessors and there is a backing > property that already exists "naturally". > > If you on the other hand you consider cases where you only want to > override either the set or the get behavior and there doesn't already > exist a backing property, you will end up with a code like this: > > protected $_isEnabled; > public $isEnabled { > get() { return $this->_isEnabled; } > set($isEnabled) { $this->_isEnabled = (bool) $isEnabled; } > } > > The above code really only adds a bool cast, but still it has to do > two things: Add a protected backing property and define a getter which > basically matches the default behavior. With automatic properties as > they are currently implemented you could write: > > public $isEnabled { > get(); > set($isEnabled) { $this->__isEnabled = (bool) $isEnabled; } > } > > This is closer to what I actually want to do, but it relies on using > $this->__isEnabled, which is really just an implementation detail. > > Instead, I would rather want to have access to the "underlying" property: > > public $isEnabled { > get(); > set($isEnabled) { $this->isEnabled = (bool) $isEnabled; } > } > > I know that the wording is a bit vague; another way to put it, is that > the above inverts the current shadowing behavior: > > In the current implementation, if there exists a "plain" property and > an "accessor" property with the same name, then the plain property > shadows the accessor property. As you say, this is to help > lazy-loading. I don't really buy the argument about lazy-loading, > because I don't think that the small perf difference between going > trough an accessor and accessing a plain property is really relevant > in that context (especially as lazy-loading would have required a > method otherwise). > > What I'd rather see is the reverse behavior: An accessor property > shadows a plain property, so that the plain property is only > accessible from within the accessor methods. This would allow you to > write code like in the last example above and I think it would also > make automatic properties more meaningful (you could store the state > in a property with the same name; also nicely integrating with > serialization and debugging). > > I know that you (Clint) want to create a hard distinction between > plain properties and accessor properties, but I think that making it > more smooth juts integrated better with PHP. I already noticed several > people who, after reading the RFC, tried to write code where they > access the property from within the accessor. It seems to me that > people think it should work. The current behavior where you can shadow > the accessor property by assigning a property of the same name seems > rather obscure to me. > > This is the feedback I have for now. Looking forward to your thoughts > on the manner. > Nikita > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php >
-- Etienne Kneuss http://www.colder.ch

Jazzer Dane

13 years ago
One of the reasons the current syntax, as seen below, was chosen, is for typehints - yes.
> public $property { > get() { ... } > set{TypeHint $value) { ... } > } >
The other reason it was chosen was to specifically get rid of the magic $value that appeared out of thin air. If typehints become possible without having to specify it in the set accessor, I'd still argue that the syntax needs to stay as, in my opinion, it is the most cohesive and sensible syntax that was proposed - even compared to:
> public $property { > get { ... } > set { ... } > } >
In terms of interfaces, Clint's reason for allowing accessors in interfaces is moderately sensible: to remain cohesive with the already allowed __get and __set magic methods that are usable in interfaces.* That's the underlying issue.* If we want to require an object to be able to be converted to a string, we can sensibly put __toString in the interface. But what does forcefully implementing __get or __set do for the object? What does it tell us about how we're able to use it? It relates to properties in arguably such a manner that we(i.e. accessing the object's API) nor the interface should care about. But that's an argument for another day. So what do we do right now, then? If we allow it in interfaces, my main worry is that people will put the accessor in an interface just because they can't put a property, and they'd use the accessor as a normal, basic property. And finally, in terms of protected backing properties: It seems nice, but, and I'm sure Clint will clarify on this, I'm not so sure that's really what accessors are supposed to be used for. If people want a typehinted property, then we should allow them to create a typehinted property rather than forcing them to use accessors. On Sun, Oct 14, 2012 at 5:50 AM, Nikita Popov <nikita.ppv@gmail.com> wrote:

Etienne Kneuss

13 years ago
On Sun, Oct 14, 2012 at 10:55 PM, Jazzer Dane <tbprogrammer@gmail.com> wrote:
> One of the reasons the current syntax, as seen below, was chosen, is for > typehints - yes. > >> public $property { >> get() { ... } >> set{TypeHint $value) { ... } >> } >> > The other reason it was chosen was to specifically get rid of the magic > $value that appeared out of thin air. If typehints become possible without > having to specify it in the set accessor, I'd still argue that the syntax > needs to stay as, in my opinion, it is the most cohesive and sensible > syntax that was proposed - even compared to: > >> public $property { >> get { ... } >> set { ... } >> } >> > > > In terms of interfaces, Clint's reason for allowing accessors in interfaces > is moderately sensible: to remain cohesive with the already allowed __get > and __set magic methods that are usable in interfaces.* That's the > underlying issue.* If we want to require an object to be able to be > converted to a string, we can sensibly put __toString in the interface. But > what does forcefully implementing __get or __set do for the object? What > does it tell us about how we're able to use it? It relates to properties in > arguably such a manner that we(i.e. accessing the object's API) nor the > interface should care about. But that's an argument for another day.
I don't get this argument, __get and __set are allowed in interfaces simply because they are simple methods. And preventing them from being defined in interfaces would be an extra step that brings nothing. In fact nothing prevents you from using them as methods. Here we are talking about defining a specific property in terms of their underlying accessors, which are linked to methods simply by implementation detail. In terms of interface, it defines a specific property, nothing more, nothing less. I see no reason to allow that and disallow specifying "normal"properties.
> > So what do we do right now, then? If we allow it in interfaces, my main > worry is that people will put the accessor in an interface just because > they can't put a property, and they'd use the accessor as a normal, basic > property. > > > And finally, in terms of protected backing properties: It seems nice, but, > and I'm sure Clint will clarify on this, I'm not so sure that's really what > accessors are supposed to be used for. If people want a typehinted > property, then we should allow them to create a typehinted property rather > than forcing them to use accessors. > > On Sun, Oct 14, 2012 at 5:50 AM, Nikita Popov <nikita.ppv@gmail.com> wrote: > >> On Fri, Oct 12, 2012 at 7:23 AM, Clint Priest <cpriest@zerocue.com> wrote: >> > Alright, here is the updated RFC as per discussions for the last few >> days: >> > >> > https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented >> > >> > If you could read it over, make sure I have all of your concerns >> correctly addressed and we can leave this open for the two week waiting >> period while I make the final code changes. >> > >> > -Clint >> >> So here comes my round of feedback on the current proposal. >> >> But before getting to that: I have collected a bit of data how getter >> and setter are currently used in Symfony and ZF: >> https://gist.github.com/3884203 Mainly so I get a better overview of >> the situation, but might be of interest for other people involved in >> this discussion too. >> >> So, my points on the proposal, in no particular order: >> >> 1. Typehints >> ----------------- >> >> The last revision of the proposal added the ability to typehint the >> set() accessor, so that's a good start. Still I would like to go a bit >> further on this point. My data says that about 20-30% of all setters >> are typehinted and it is highly probable that if we had scalar >> typehints (as I'm sure we will have in the future) that number would >> be even larger. In particular I would like to have an easy way to >> typehint if I *don't* want to override any additional behavior. >> Currently that would require me to write something along the lines of >> this: >> >> protected $_date; >> public $date { >> get() { return $this->_date; } >> set(DateTime $date) { $this->_date = $date; } >> } >> >> This is a rather lot of boilerplate code just to add a typehint. With >> automatic properties this might be reduced to this: >> >> public $date { >> get(); >> set(DateTime $date); >> } >> >> This is already a lot nicer, because I don't have to implement the >> same getting and setting pattern again and again, but I still find >> that it doesn't do a particularly good job at conveying its intention. >> Rather, what that code *really* wants to say is just this: >> >> public DateTime $date; >> >> I think that it would be very beneficial to support this syntax, >> because I think that a large part of the use of the accessor proposal >> will just be type checking. But if we indeed want to syntax we should >> reconsider the get {} set { $value; } syntax again, because as I >> understand it the new get() {} set($value) {} syntax was mainly >> introduced to deal with setter typehints. If we chose to allow >> typehints before the property name, then that wouldn't be necessary >> anymore. >> >> 2. Interfaces >> ------------------ >> >> I already mentioned this before, but I'll reiterate it here again: >> Currently properties and properties with accessors are handled >> different with regards to interfaces. In particular, if you have an >> interface such as follows: >> >> interface Extension { >> public $name { get; } >> // more stuff >> } >> >> then the following is not a valid implementation of the interface: >> >> class FooExtension implements Extension { >> public $name = 'foo'; >> // more stuff >> } >> >> This does not make sense to me: $foo->name is a gettable property, as >> such it satisfies the interface. I don't see a reason why we would >> need a hard distinction between properties and properties with >> accessors here. >> >> The converse is also true: An interface currently can not define a >> "plain" property: >> >> interface Foo { >> public $foo; >> } >> >> I would expect this interface definition to be the same as "public >> $foo { get; set; }". But I'm not sure on this point, as one could >> argue that it's just a redundant way to say the same thing. >> >> 3. Protected backing property >> ------------------------------------------ >> >> The RFC currently contains just a single code example, namely setting >> a $Seconds property through an $Hours property, which is an example >> where you want to override both accessors and there is a backing >> property that already exists "naturally". >> >> If you on the other hand you consider cases where you only want to >> override either the set or the get behavior and there doesn't already >> exist a backing property, you will end up with a code like this: >> >> protected $_isEnabled; >> public $isEnabled { >> get() { return $this->_isEnabled; } >> set($isEnabled) { $this->_isEnabled = (bool) $isEnabled; } >> } >> >> The above code really only adds a bool cast, but still it has to do >> two things: Add a protected backing property and define a getter which >> basically matches the default behavior. With automatic properties as >> they are currently implemented you could write: >> >> public $isEnabled { >> get(); >> set($isEnabled) { $this->__isEnabled = (bool) $isEnabled; } >> } >> >> This is closer to what I actually want to do, but it relies on using >> $this->__isEnabled, which is really just an implementation detail. >> >> Instead, I would rather want to have access to the "underlying" property: >> >> public $isEnabled { >> get(); >> set($isEnabled) { $this->isEnabled = (bool) $isEnabled; } >> } >> >> I know that the wording is a bit vague; another way to put it, is that >> the above inverts the current shadowing behavior: >> >> In the current implementation, if there exists a "plain" property and >> an "accessor" property with the same name, then the plain property >> shadows the accessor property. As you say, this is to help >> lazy-loading. I don't really buy the argument about lazy-loading, >> because I don't think that the small perf difference between going >> trough an accessor and accessing a plain property is really relevant >> in that context (especially as lazy-loading would have required a >> method otherwise). >> >> What I'd rather see is the reverse behavior: An accessor property >> shadows a plain property, so that the plain property is only >> accessible from within the accessor methods. This would allow you to >> write code like in the last example above and I think it would also >> make automatic properties more meaningful (you could store the state >> in a property with the same name; also nicely integrating with >> serialization and debugging). >> >> I know that you (Clint) want to create a hard distinction between >> plain properties and accessor properties, but I think that making it >> more smooth juts integrated better with PHP. I already noticed several >> people who, after reading the RFC, tried to write code where they >> access the property from within the accessor. It seems to me that >> people think it should work. The current behavior where you can shadow >> the accessor property by assigning a property of the same name seems >> rather obscure to me. >> >> This is the feedback I have for now. Looking forward to your thoughts >> on the manner. >> Nikita >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: http://www.php.net/unsub.php >> >>
-- Etienne Kneuss http://www.colder.ch

Stas Malyshev

13 years ago
Hi!
> https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
My feedback on the RFC: 1. Accessors IMO should be regular PHP methods that PHP generates with two additional things: a. Their name is generated by PHP b. Their argument set is defined by the accessor pattern (i.e. same thing as __get/__set). We should keep the amount of magic and special cases to the minimum, the engine is complex enough as it is. This of course includes the full range of options available for the methods - final, reflection, call scenarios, etc. 2. isset is defined as: isset() { return $this->Hours != NULL; } This does not seem to be correct - != NULL does not work like isset now. Try this: class A { public $x = 0; } $a = new A; var_dump(isset($a->x)); var_dump($a->x != NULL); This needs to be fixed - generated isset() should be defined to work exactly like regular isset and actually use the same code path. Argument to isset() call can be retrieved using accessor but it should not differ from isset() result in any possible way or situation (including error situations - e.g. notices, etc.) 3. How references and complex cases are handled? Didn't find anything about it, e.g. how it handles $foo->bar++, $foo->bar[] = 1, $foo->bar[123] = 4, etc. ($foo->bar being property with get/set defined of course)? How $foobar =& $foo->bar is handled? The sort() case mentions by-ref return but does not explicitly mention all other cases. These need to be covered explicitly in the RFC. 4. We have some LSP controls now in place to ensure non-LSP overrides generate E_STRICT. Will this be the case for properties too? Meaning, what happens if you add overriding protected getter to a property that was previously fully public - thus violating the LSP? 5. What happens if you override accessor property with plain old variable property? I.e.: class A { protected $secret { get() { return "secret"; } } } class B extends A { public $secret = "not a secret anymore"; } Also, what happens here if A extends B in the same scenario (this refers to the #4 too). 6. Thinking more about isset/unset - why not make isset/unset always be the default unless overridden? I can't really see the case where you want echo $foo->bar to work but if(isset($foo->bar)) echo $foo->bar to not work. I think isset() and unset() should always use automatic implementations by default (fixed in accord with #2 of course). 7. "Error messaging" section is not clear. Some examples would help - what is being translated to what? 8. "Static accessors" section is not clear, namely this one: This yielded the possibility that a getter call was being made while it should not be allowed (if there was no getter defined) and so pass_two() was changed to look for these non-backpatched illegal static getter calls and a compile time error is produced. When the code is compiled, the class definition (and, in fact, the name of the class we're talking about) may not be available, so how can you known if certain property of this class has getters defined? Also, I'm not sure what is the thing about backpatching and converting to function calls - I think it should work via engine handlers just as the rest of the things work in the engine, is it not the case? If not, it should be made the case. 9. This: Eliminate the ability for an accessor to be called via $o→__getHours(), the accessor functions will be completely unavailable for use except as property references ($o→Hours) I think it a mistake. More magic and complication in the engine is not a good thing and would require tons of special case checks in all places where we deal with functions. I think it is wrong - if we create a callable entity, it should be callable. __ in the name is the indication enough that you're dealing with special method and you should tread carefully, we should not go out of our way to mess up the engine to prevent it. 10. I'm not sure what the point about debug_backtrace() is but again, we should not create more complicated magic there, it just should show what really is happening. 11. About read-only/write-only thing - I like not having the keywords, but it is still not clear how final produces read-only property - do I say something like "final set();" or how can I say "there's no set and never will be"? I'm sorry if some points were already discussed - I scanned through the multiple threads but may very well missed something in 100+ messages that there are, in any case I feel most of the questions above must be reflected in RFC explicitly. Thanks,
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227

Jazzer Dane

13 years ago
#2: I agree with you here - this is problematic. isset/unset accessors that invoke the isset/unset function should actually invoke the function rather than being compared to null, as that isn't the same as isset. #5: From what I understand, an extending class can not override an accessor with a non-accessor. #11: If you set an accessor's get or set to *final private*, you are not able to extend and it are only able to invoke it from the current class. If you don't invoke it, then it is virtually read or write only. On Tue, Oct 16, 2012 at 2:12 AM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:

Stas Malyshev

13 years ago
Hi!
> #5: From what I understand, an extending class can not override an > accessor with a non-accessor.
This should be in the RFC then - along with what exactly happens. Note that this will represent a sort of BC break in terms that you could have two properties $a before, but if you change implementation of $a in base class from plain old property to accessor property, the child class would break. Which is not good, since compatible changes in parent class should not break child classes - and which will also impede adoption of this feature, since you can not guarantee no child class does it.
> #11: If you set an accessor's get or set to /final private/, you are not > able to extend and it are only able to invoke it from the current class. > If you don't invoke it, then it is virtually read or write only.
I get this, but what do you write as a method body if you want to just disallow it? Do you write just {}? Then it's not good for get() since get() is supposed to return a value, and also not good for set() since base class still can call private methods, and we want set() to be not available for everybody.
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227

Jazzer Dane

13 years ago
In regards to #11, yes, you'd just write {}. I imagine you could also just throw an exception. Maybe an exception could be thrown automatically if there is no code between the braces. Though this doesn't really directly relate to providing the ability to disable get/set in an accessor. The only class that would ever be able to logically attempt to use the empty getter/setter would be the very one that you define the accessor in. We went through multiple alternative options to read/write-only, and the implementation you see in the 1.2 RFC is the most widely agreed upon proposal. I don't doubt that there is room for improvement in this area, but we haven't had any further proposals as of yet. On Tue, Oct 16, 2012 at 2:43 AM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:

Stas Malyshev

13 years ago
Hi!
> In regards to #11, yes, you'd just write {}. I imagine you could also
This doesn't work for the same class (and for traits which put things in the context of the same class) - it would not behave as "no setter", it would behave as "there's a setter doing nothing". Is this the proposed solution? Exception is a possibility but then everybody would do it differently which reduces the value of standardizing it (the whole point of having accessors since otherwise we could just do __get and throw exceptions).
> We went through multiple alternative options to read/write-only, and the > implementation you see in the 1.2 RFC is the most widely agreed upon > proposal. I don't doubt that there is room for improvement in this area, > but we haven't had any further proposals as of yet.
Actually, I do not see anything explicitly said in the proposal that works for the cases outlined above. I wanted to just make sure if that means "no solution currently" (then should be on TODO list) or "we have a solution but it's not outlined in the RFC" (should be added then).
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227

Jazzer Dane

13 years ago
Stas, the proposed "solution" thus far is to make the getter or setter final and private and not have a body. This would naturally throw an exception if it was accessed from anywhere but the class it was defined. The class it was defined in has to remember that it is virtually a read/write only accessor. Although it has not been proposed yet, perhaps the above can be further enforced by automatically throwing an exception whenever a getter/setter exists but does not have a body. Nevertheless, the "solution" is, as you can see, pretty loose right now, and I definitely wouldn't object to hearing and discussing alternative proposals. On Tue, Oct 16, 2012 at 3:18 AM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:

Stas Malyshev

13 years ago
Hi!
> Stas, the proposed "solution" thus far is to make the getter or setter > final and private and not have a body. This would naturally throw an > exception if it was accessed from anywhere but the class it was defined. > The class it was defined in has to remember that it is virtually a > read/write only accessor.
What you mean by "not have a body" - is there special syntax for body-less methods introduced? Then it should be in the RFC. How it is implemented - what exactly is stored in the function table then?
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227

Jazzer Dane

13 years ago
I apologize for my confusing terminology - let me elaborate. There are no special syntaxes. The below code should help clear things up a bit: class MyClass {
> private $otherProperty; > public $property { > get() {}; // Does not "have a body", as there is no code between the > curly braces. > set($value) { $this->otherProperty = $value; } // Does "have a body", > as there IS code between the curly braces. > } > } >
On Tue, Oct 16, 2012 at 3:48 AM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:

Stas Malyshev

13 years ago
Hi!
> I apologize for my confusing terminology - let me elaborate. There are > no special syntaxes. The below code should help clear things up a bit: > > class MyClass { > private $otherProperty; > public $property { > get() {}; // Does not "have a body", as there is no code between > the curly braces.
It does have a body. This body is just default empty method body returning null - which does not throw any exceptions and is completely indistinguishable from the outside from property being equal to null. I'm not sure it's what the intent of *-only variable is, though I guess it is a way to hack around it. I wonder however if it can be done better.
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227

Clint M Priest

13 years ago
Stas, go back a few "revisions" of the RFC and you'll see there used to be automatically implemented accessors which were voted out. The current RFC does not reflect the current "fork" of the code, it did before I modified it with the consensus from around 10/12 before Nikita brought up some more thoughts on the subject. I believe the general idea of the changes is documented at the bottom of the document in the change list, but you're free to go back to previous versions of the RFC.

Clint M Priest

13 years ago
Hey Stas, a bunch of this has already been covered but I will attempt to answer each of these as is my current understanding of "the hives" decision... :P
> 1. Accessors IMO should be regular PHP methods that PHP generates with two additional things: > a. Their name is generated by PHP > b. Their argument set is defined by the accessor pattern (i.e. same thing as __get/__set). > We should keep the amount of magic and special cases to the minimum, the engine is complex enough as it is. > This of course includes the full range of options available for the methods - final, reflection, call scenarios, etc.
This is the way it is, though Nikita strongly disagrees that they should be "callable, visible" methods on the object and I agree with Nikita on this issue, I never did like the idea that __getHours() and __setHours() were methods of a class just because of an accessor definition, which is why I worked to have Reflection hide that fact. If I were to go about this again, they probably will not be methods of the class. Internally there is little requiring an op_array to be attached to a class in order to be executed.
> 2. isset is defined as: > isset() { return $this->Hours != NULL; } This does not seem to be correct - != NULL does not work like isset now. > Try this: > class A { public $x = 0; } > $a = new A; > var_dump(isset($a->x)); > var_dump($a->x != NULL); > This needs to be fixed - generated isset() should be defined to work exactly like regular isset and actually use the same code path. > Argument to isset() call can be retrieved using accessor but it should not differ from isset() result in any possible way or situation > (including error situations - e.g. notices, etc.)
The reason that = NULL and != NULL was chosen is because isset() and unset() are special states that are available only to a variable or property, since a get/set do not return a real property they cannot be used with isset/unset. Now that there has been discussion of an accessor being a real property shadowed by get/set, standard isset/unset could potentially be used against the underlying property.
> 3. How references and complex cases are handled? Didn't find anything about it, e.g. how it handles $foo->bar++, $foo->bar[] = 1, > $foo->bar[123] = 4, etc. ($foo->bar being property with get/set defined of course)? How $foobar =& $foo->bar is handled? The sort() > case mentions by-ref return but does not explicitly mention all other cases. > These need to be covered explicitly in the RFC.
This is covered in the RFC, perhaps not clearly enough (let me know how I could expand on it further for clarity). To answer each of your questions, you can think of what would happen as if it were a function call. For example, if you did $foo->bar()[123] = 4 you would be modifying a return-by-val. If bar were instead defined as returning a reference, then it would indeed work as you expect above. $foobar = &$foo->bar would work as expected as long as the getter is returning a reference as well. I, perhaps mistakenly, assumed that if return-by-ref and sort() worked properly, then all other "usages of references" should equally work the same, is that not right? If it's not right, why not?
> 4. We have some LSP controls now in place to ensure non-LSP overrides generate E_STRICT. Will this be the case for properties too? > Meaning, what happens if you add overriding protected getter to a property that was previously fully public - thus violating the LSP?
Certainly, this basic object oriented functionality, of course it is upheld. This is one of the reasons I leveraged standard functions in the first place, because these issues were automatically handled by the existing core.
> > 5. What happens if you override accessor property with plain old variable property? I.e.: > > class A { > protected $secret { > get() { return "secret"; } > } > } > > class B extends A { > public $secret = "not a secret anymore"; } > > Also, what happens here if A extends B in the same scenario (this refers to the #4 too).
This is currently up in the air on the RFC side and is being referred to as which "shadows" which. The code currently has it that properties shadow accessors, Nikita suggested that should be inverted and I agree, so unless someone else thinks otherwise, accessors will probably shadow properties.
> > 6. Thinking more about isset/unset - why not make isset/unset always be the default unless overridden? I can't really see the case > where you want echo $foo->bar to work but if(isset($foo->bar)) echo $foo->bar to not work. I think isset() and unset() should always > use automatic implementations by default (fixed in accord with #2 of course).
See response to #2 above, without a getter/setter returning a real property, isset/unset accessors were necessary because isset()/unset() cannot perform on a dynamic value. Consider this: unset($foo->bar()) or isset($foo->bar()). Since we are moving in the direction of accessors shadowing properties, I could see the default implementation of isset/unset actually be more like unset() { return unset($this->bar); } and isset() { return isset($this->bar); }
> 7. "Error messaging" section is not clear. Some examples would help - what is being translated to what?
I will expand on this section in the RFC, but this section is referring to the fact that if bar were an accessor without a set() and this were attempted: $foo->bar = 4; It would have errored with: Fatal error: no function __setbar() exists on object $foo <-- That is not exactly the message returned by core, but you get the point So the section on error messaging is referring to the fact that all errors involving accessors have been changed to make more sense, ie: Fatal error: cannot set $foo->bar, no setter is defined.
> 8. "Static accessors" section is not clear, namely this one: > This yielded the possibility that a getter call was being made while it should not be allowed (if there was no getter defined) and so > pass_two() was changed to look for these non-backpatched illegal static getter calls and a compile time error is produced. > > When the code is compiled, the class definition (and, in fact, the name of the class we're talking about) may not be available, so how > can you known if certain property of this class has getters defined?
Oh crap, you're referring to the idea that during compilation a class definition may exist below the point of "current compilation," right? I had not considered this case and you're right, this would break badly.
> Also, I'm not sure what is the thing about backpatching and converting to function calls - I think it should work via engine handlers > just as the rest of the things work in the engine, is it not the case? If not, it should be made the case.
The static accessors was above my pay grade when I first tackled them, in comparison the object get/set was a piece of cake because it was already being handled by __get() and __set() but static properties are handled in an entirely different way. Because of the nearly completely undocumented code, I could not make heads nor tails of how it worked. I still do not know if I could make this work at the vm/opcode level during runtime but I'd be much more willing to give it a go now. Most of my deep questions over the last year have gone unanswered and so I was left to my own devices to find a way to make it work.
> 9. This: > Eliminate the ability for an accessor to be called via $o→__getHours(), the accessor functions will be completely unavailable for use > except as property references ($o→Hours) > > I think it a mistake. More magic and complication in the engine is not a good thing and would require tons of special case checks in > all places where we deal with functions. I think it is wrong - if we create a callable entity, it should be callable. __ in the name is the > indication enough that you're dealing with special method and you should tread carefully, we should not go out of our way to mess > up the engine to prevent it.
You and Nikita get your gloves on, makes it easier on me to go your direction but Nikita did not like this at all.
> 10. I'm not sure what the point about debug_backtrace() is but again, we should not create more complicated magic there, it just > should show what really is happening.
This would refer to the fact that a debug backtrace would show that it's in a function called __getHours() (for example) which would only serve to confuse a php developer who doesn't know that a getter for $Hours is really a function named __getHours(). Therefore I will be patching the error so that it indicates something the php developer will understand, namely that the line is referring to a getter.
> 11. About read-only/write-only thing - I like not having the keywords, but it is still not clear how final produces read-only property - > do I say something like "final set();" or how can I say "there's no set and never will be"?
I could not get this point across to anyone but gave up, private final set(); != read-only. With a non "read-only" keyword scenario, a "set" call would still be allowed and would show up as an access violation to outside observers and would be allowed from within the class. This is not what I wanted but I had enough other points of contention and I yielded on this.

Jazzer Dane

13 years ago
private final get/set() {} is indeed not a read/write only functionality. I really would like to keep new keywords out of whatever syntax is implemented, but I think the latest proposal can and should be improved upon. I've been thinking about this quite a bit. To reiterate the initial problem, although we can disable get/set by not including it in the accessor, the reason we need read/write only is, without it, there is no way to not allow subclasses to add set/get to their parent's accessor. Going with the idea that not including get/set disables them, what about this syntax:
> class MyObject > { > public $property { > get() { ... } > final set; // Defining an accessor function without body is virtually > the same as not defining the accessor function at all, i.e. this property > accessor can not be set. > } > } > > $object = new MyObject(); > $propertyValue = $object->property; // Works > $object->property = 10; // Does not work, set not allowed >
By "disabling" accessor functions that don't provide a body, we can easily imitate read/write arguably better than what the current RFC is capable of, and use exceptions that already exist (same as the exception thrown if the accessor function is never defined in the property accessor). On Tue, Oct 16, 2012 at 4:19 AM, Clint Priest <cpriest@zerocue.com> wrote:

Clint M Priest

13 years ago
This would conflict with the concept of auto-implementation, which if we went with Nikita’s suggestion, would make an undefined body setter actually set the property it shadows. For example class a { public $prop { get() { … } final set($x); } } Would have final set() translate to final set($x) { $this->prop = $x; } We *could* have it be that final set; or final set(); <-- Without the () or without the ($x) specifically mean that it is read-only but the clarity goes WAY down. It is not at all intuitive that any of those things means ‘read-only’ Why is everyone so against the two new keywords? I’m told there has been talk long ago about having them as keywords anyway. Is it the dash? Is this more palatable? public readonly $prop { get() { } } Or public read_only $prop { … } Specifically, read-only actually has the advantage that one of the few ways that it would break BC is if someone had defined a variable named $read and attempted to subtract a constant or string of ‘only’ from it. $read = 5; echo $read-only; // Yields 5 with define(‘only’, 2); echo $read-only; // Becomes 3 I find it highly unlikely that someone would have been using $read-only or $write-only since variables cannot have dashes in them, he interpreter sees that as a subtraction and as shown above it would be nonsensical to have written PHP like that. From: Jazzer Dane [mailto:tbprogrammer@gmail.com] Sent: Tuesday, October 16, 2012 6:45 AM To: Clint Priest Cc: Stas Malyshev; internals@lists.php.net Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2 private final get/set() {} is indeed not a read/write only functionality. I really would like to keep new keywords out of whatever syntax is implemented, but I think the latest proposal can and should be improved upon. I've been thinking about this quite a bit. To reiterate the initial problem, although we can disable get/set by not including it in the accessor, the reason we need read/write only is, without it, there is no way to not allow subclasses to add set/get to their parent's accessor. Going with the idea that not including get/set disables them, what about this syntax: class MyObject { public $property { get() { ... } final set; // Defining an accessor function without body is virtually the same as not defining the accessor function at all, i.e. this property accessor can not be set. } } $object = new MyObject(); $propertyValue = $object->property; // Works $object->property = 10; // Does not work, set not allowed By "disabling" accessor functions that don't provide a body, we can easily imitate read/write arguably better than what the current RFC is capable of, and use exceptions that already exist (same as the exception thrown if the accessor function is never defined in the property accessor). On Tue, Oct 16, 2012 at 4:19 AM, Clint Priest <cpriest@zerocue.com<mailto:cpriest@zerocue.com>> wrote: Hey Stas, a bunch of this has already been covered but I will attempt to answer each of these as is my current understanding of "the hives" decision... :P
> 1. Accessors IMO should be regular PHP methods that PHP generates with two additional things: > a. Their name is generated by PHP > b. Their argument set is defined by the accessor pattern (i.e. same thing as __get/__set). > We should keep the amount of magic and special cases to the minimum, the engine is complex enough as it is. > This of course includes the full range of options available for the methods - final, reflection, call scenarios, etc.
This is the way it is, though Nikita strongly disagrees that they should be "callable, visible" methods on the object and I agree with Nikita on this issue, I never did like the idea that __getHours() and __setHours() were methods of a class just because of an accessor definition, which is why I worked to have Reflection hide that fact. If I were to go about this again, they probably will not be methods of the class. Internally there is little requiring an op_array to be attached to a class in order to be executed.
> 2. isset is defined as: > isset() { return $this->Hours != NULL; } This does not seem to be correct - != NULL does not work like isset now. > Try this: > class A { public $x = 0; } > $a = new A; > var_dump(isset($a->x)); > var_dump($a->x != NULL); > This needs to be fixed - generated isset() should be defined to work exactly like regular isset and actually use the same code path. > Argument to isset() call can be retrieved using accessor but it should not differ from isset() result in any possible way or situation > (including error situations - e.g. notices, etc.)
The reason that = NULL and != NULL was chosen is because isset() and unset() are special states that are available only to a variable or property, since a get/set do not return a real property they cannot be used with isset/unset. Now that there has been discussion of an accessor being a real property shadowed by get/set, standard isset/unset could potentially be used against the underlying property.
> 3. How references and complex cases are handled? Didn't find anything about it, e.g. how it handles $foo->bar++, $foo->bar[] = 1, > $foo->bar[123] = 4, etc. ($foo->bar being property with get/set defined of course)? How $foobar =& $foo->bar is handled? The sort() > case mentions by-ref return but does not explicitly mention all other cases. > These need to be covered explicitly in the RFC.
This is covered in the RFC, perhaps not clearly enough (let me know how I could expand on it further for clarity). To answer each of your questions, you can think of what would happen as if it were a function call. For example, if you did $foo->bar()[123] = 4 you would be modifying a return-by-val. If bar were instead defined as returning a reference, then it would indeed work as you expect above. $foobar = &$foo->bar would work as expected as long as the getter is returning a reference as well. I, perhaps mistakenly, assumed that if return-by-ref and sort() worked properly, then all other "usages of references" should equally work the same, is that not right? If it's not right, why not?
> 4. We have some LSP controls now in place to ensure non-LSP overrides generate E_STRICT. Will this be the case for properties too? > Meaning, what happens if you add overriding protected getter to a property that was previously fully public - thus violating the LSP?
Certainly, this basic object oriented functionality, of course it is upheld. This is one of the reasons I leveraged standard functions in the first place, because these issues were automatically handled by the existing core.
> > 5. What happens if you override accessor property with plain old variable property? I.e.: > > class A { > protected $secret { > get() { return "secret"; } > } > } > > class B extends A { > public $secret = "not a secret anymore"; } > > Also, what happens here if A extends B in the same scenario (this refers to the #4 too).
This is currently up in the air on the RFC side and is being referred to as which "shadows" which. The code currently has it that properties shadow accessors, Nikita suggested that should be inverted and I agree, so unless someone else thinks otherwise, accessors will probably shadow properties.
> > 6. Thinking more about isset/unset - why not make isset/unset always be the default unless overridden? I can't really see the case > where you want echo $foo->bar to work but if(isset($foo->bar)) echo $foo->bar to not work. I think isset() and unset() should always > use automatic implementations by default (fixed in accord with #2 of course).
See response to #2 above, without a getter/setter returning a real property, isset/unset accessors were necessary because isset()/unset() cannot perform on a dynamic value. Consider this: unset($foo->bar()) or isset($foo->bar()). Since we are moving in the direction of accessors shadowing properties, I could see the default implementation of isset/unset actually be more like unset() { return unset($this->bar); } and isset() { return isset($this->bar); }
> 7. "Error messaging" section is not clear. Some examples would help - what is being translated to what?
I will expand on this section in the RFC, but this section is referring to the fact that if bar were an accessor without a set() and this were attempted: $foo->bar = 4; It would have errored with: Fatal error: no function __setbar() exists on object $foo <-- That is not exactly the message returned by core, but you get the point So the section on error messaging is referring to the fact that all errors involving accessors have been changed to make more sense, ie: Fatal error: cannot set $foo->bar, no setter is defined.
> 8. "Static accessors" section is not clear, namely this one: > This yielded the possibility that a getter call was being made while it should not be allowed (if there was no getter defined) and so > pass_two() was changed to look for these non-backpatched illegal static getter calls and a compile time error is produced. > > When the code is compiled, the class definition (and, in fact, the name of the class we're talking about) may not be available, so how > can you known if certain property of this class has getters defined?
Oh crap, you're referring to the idea that during compilation a class definition may exist below the point of "current compilation," right? I had not considered this case and you're right, this would break badly.
> Also, I'm not sure what is the thing about backpatching and converting to function calls - I think it should work via engine handlers > just as the rest of the things work in the engine, is it not the case? If not, it should be made the case.
The static accessors was above my pay grade when I first tackled them, in comparison the object get/set was a piece of cake because it was already being handled by __get() and __set() but static properties are handled in an entirely different way. Because of the nearly completely undocumented code, I could not make heads nor tails of how it worked. I still do not know if I could make this work at the vm/opcode level during runtime but I'd be much more willing to give it a go now. Most of my deep questions over the last year have gone unanswered and so I was left to my own devices to find a way to make it work.
> 9. This: > Eliminate the ability for an accessor to be called via $o→__getHours(), the accessor functions will be completely unavailable for use > except as property references ($o→Hours) > > I think it a mistake. More magic and complication in the engine is not a good thing and would require tons of special case checks in > all places where we deal with functions. I think it is wrong - if we create a callable entity, it should be callable. __ in the name is the > indication enough that you're dealing with special method and you should tread carefully, we should not go out of our way to mess > up the engine to prevent it.
You and Nikita get your gloves on, makes it easier on me to go your direction but Nikita did not like this at all.
> 10. I'm not sure what the point about debug_backtrace() is but again, we should not create more complicated magic there, it just > should show what really is happening.
This would refer to the fact that a debug backtrace would show that it's in a function called __getHours() (for example) which would only serve to confuse a php developer who doesn't know that a getter for $Hours is really a function named __getHours(). Therefore I will be patching the error so that it indicates something the php developer will understand, namely that the line is referring to a getter.
> 11. About read-only/write-only thing - I like not having the keywords, but it is still not clear how final produces read-only property - > do I say something like "final set();" or how can I say "there's no set and never will be"?
I could not get this point across to anyone but gave up, private final set(); != read-only. With a non "read-only" keyword scenario, a "set" call would still be allowed and would show up as an access violation to outside observers and would be allowed from within the class. This is not what I wanted but I had enough other points of contention and I yielded on this.

Jazzer Dane

13 years ago
Yes, I'm aware - I thought of the clash with Nikita's proposal while writing mine, but decided not to bring it up. If we were to go with my proposed syntax, then one of two things would have to happen. 1) Don't auto implement get/set. *On a similar note, what's the difference between using a property accessor that auto implements get/set* and a normal property? 2) As you said, differentiate between get; and get();. The issue this creates is obvious, and in my opinion this isn't a viable solution. For the record, *read_only* and even moreso *readonly* are, in my opinion, nicer than *read-only*. On Tue, Oct 16, 2012 at 5:10 AM, Clint Priest <cpriest@zerocue.com> wrote:

Stas Malyshev

13 years ago
Hi!
> This is the way it is, though Nikita strongly disagrees that they > should be "callable, visible" methods on the object and I agree with > Nikita on this issue, I never did like the idea that __getHours() and
I think PHP engine has enough complexity and we do not need to add more unwarranted one. These are methods, they exist - so they should exist everywhere methods exist. Doing otherwise will result in a tons of inconsistencies and weird occurrences. If you are in __getHours method but the engine says __getHours does not exist and can not be called, it is bad magic. There's no use case for this bad magic and I see no reason for it except for us trying to restrict users for purist reasons. I do not think the engine needs complex bad magic when simple solution of making regular methods would work just as well in all cases where methods work.
> If I were to go about this again, they probably will not be methods > of the class. Internally there is little requiring an op_array to be > attached to a class in order to be executed.
So methods of what would they be? What would be their scope? What would $this mean? What would method name and backtrace report? How debugging/profiling tools would work with them? I see no need to reinvent complex APIs that would require dozens of changes in all tools dealing with PHP engine when simple methods approach would work as well. If they won't be regular methods I think it would be too much trouble to have yet another entity which is "like method but not really a method" in the engine.
> The reason that = NULL and != NULL was chosen is because isset() and > unset() are special states that are available only to a variable or > property, since a get/set do not return a real property they cannot > be used with isset/unset. Now that there has been discussion of an
I'm not sure I understand here. Property can be either set (exists and not storing null) or not, there's nothing special about it - all variables and properties work this way. Automatic isset should work *exactly* like plain PHP's isset(), and unset should make variable into the state where isset returns false (and resources are freed). The code in the RFC for isset() is not working like PHP's isset. That should be fixed.
> This is covered in the RFC, perhaps not clearly enough (let me know > how I could expand on it further for clarity). To answer each of
Yes, it needs to be expanded, since no examples right now show how it is supposed to be working.
> I, perhaps mistakenly, assumed that if return-by-ref and sort() > worked properly, then all other "usages of references" should equally > work the same, is that not right? If it's not right, why not?
I'm not sure, I didn't check all of them yet. All these need to be tested.
> Certainly, this basic object oriented functionality, of course it is > upheld. This is one of the reasons I leveraged standard functions in > the first place, because these issues were automatically handled by > the existing core.
I'm not sure overriding "public $foo" with "public $foo { protected get() {} }" is covered by existing code. Existing code has no way to cover such things.
> This is currently up in the air on the RFC side and is being referred > to as which "shadows" which. The code currently has it that > properties shadow accessors, Nikita suggested that should be inverted > and I agree, so unless someone else thinks otherwise, accessors will > probably shadow properties.
Which raises the question above again - how LSP is preserved? What would happen in such case?
> See response to #2 above, without a getter/setter returning a real > property, isset/unset accessors were necessary because > isset()/unset() cannot perform on a dynamic value. Consider this:
You seem to misunderstand what I wrote there. I am proposing that isset() would a) always have default implementation if get() is defined and b) work exactly like PHP isset() in that the way that if get() returns null (or does not exist if write-only properties are introduced) then it returns false, otherwise it returns true. The fact that current isset() operator does not work on T variables is irrelevant, since I'm talking about level below that - how that operator is implemented. The implementation should be augmented so that for properties with accessors it would work exactly the same as for properties without ones.
> direction of accessors shadowing properties, I could see the default > implementation of isset/unset actually be more like unset() { return > unset($this->bar); } and isset() { return isset($this->bar); }
If this is pseudocode, meaning "call get accessor if exists and return true if return value is not null, otherwise return false" then it's fine for isset. For unset, you'd still want to use set accessor or fail if set accessor is not defined.
> Oh crap, you're referring to the idea that during compilation a class > definition may exist below the point of "current compilation," right? > I had not considered this case and you're right, this would break > badly.
Yes, class definition is runtime, when you compiling code you have no idea not only what this class is, but thanks to $foo::bar also what this class' name is (which is no different since if you had the name you still had no idea what class is under this name).
> of how it worked. I still do not know if I could make this work at > the vm/opcode level during runtime but I'd be much more willing to > give it a go now.
I think you'd need some additional hooks in the class structure for this and some additional methods in zend_object_handlers.c. If you need more on this please send me a message, I'll try to help you (though next 2 weeks I'll have very sketchy internet access as I'm on vacation/traveling, so please be patient :)
> You and Nikita get your gloves on, makes it easier on me to go your > direction but Nikita did not like this at all.
Well, I think I've explained my approach above, if Nikita has something to add he's most welcome.
> This would refer to the fact that a debug backtrace would show that > it's in a function called __getHours() (for example) which would only > serve to confuse a php developer who doesn't know that a getter for > $Hours is really a function named __getHours(). Therefore I will be
I think it's a wrong approach to lie to the developer in order to make it "easier". If the engine has function __getHours, it should say so. The developer smart enough to use properties is smart enough to understand very simple concept of auto-generated function name (given that __get exists for years in the engine).
> I could not get this point across to anyone but gave up, private > final set(); != read-only. With a non "read-only" keyword scenario, > a "set" call would still be allowed and would show up as an access > violation to outside observers and would be allowed from within the > class. This is not what I wanted but I had enough other points of > contention and I yielded on this.
I see. I guess this should be TODO then, maybe somebody will have better ideas. I really don't like special case keywords, so it'd be great if we could figure it out using generic approach or maybe some light magic like autogenerated method throwing predefined exception.
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227

Clint M Priest

13 years ago
Gabeezus, I have to get to work Stas! ;)
> > This is the way it is, though Nikita strongly disagrees that they > > should be "callable, visible" methods on the object and I agree with > > Nikita on this issue, I never did like the idea that __getHours() and > > I think PHP engine has enough complexity and we do not need to add more unwarranted one. These are methods, they exist - so > they should exist everywhere methods exist. Doing otherwise will result in a tons of inconsistencies and weird occurrences. If you > are in __getHours method but the engine says __getHours does not exist and can not be called, it is bad magic. There's no use case > for this bad magic and I see no reason for it except for us trying to restrict users for purist reasons. I do not think the engine needs > complex bad magic when simple solution of making regular methods would work just as well in all cases where methods work.
I very much disagree, engine details should not be visible to users. It is irrelevant to them and only serves to confuse.
> > > If I were to go about this again, they probably will not be methods of > > the class. Internally there is little requiring an op_array to be > > attached to a class in order to be executed. > > So methods of what would they be? What would be their scope? What would $this mean? What would method name and backtrace > report? How debugging/profiling tools would work with them? I see no need to reinvent complex APIs that would require dozens of > changes in all tools dealing with PHP engine when simple methods approach would work as well. > If they won't be regular methods I think it would be too much trouble to have yet another entity which is "like method but not really > a method" > in the engine.
To be clear, they would be regular methods, they would just not exist in the HashTable *functions of the class. In every other way they are methods of the class.
> > The reason that = NULL and != NULL was chosen is because isset() and > > unset() are special states that are available only to a variable or > > property, since a get/set do not return a real property they cannot be > > used with isset/unset. Now that there has been discussion of an > > I'm not sure I understand here. Property can be either set (exists and not storing null) or not, there's nothing special about it - all > variables and properties work this way. Automatic isset should work > *exactly* like plain PHP's isset(), and unset should make variable into the state where isset returns false (and resources are freed). > The code in the RFC for isset() is not working like PHP's isset. That should be fixed. >
PHP 5.4.7 (cli) (built: Sep 13 2012 09:32:31) Copyright (c) 1997-2012 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies with DBG v4.6.1, (C) 2000,2012, by Dmitri Dmitrienko [root@deploy /opt/esp/release]# php -ddisplay_errors=1 -ddisplay_startup_errors=1 -a Interactive shell php > function a() { return 1; } php > unset(a()); Fatal error: Can't use function return value in write context in php shell code on line 1 php > isset(a()); Fatal error: Can't use function return value in write context in php shell code on line 1 See the problem?
> > This is covered in the RFC, perhaps not clearly enough (let me know > > how I could expand on it further for clarity). To answer each of > > Yes, it needs to be expanded, since no examples right now show how it is supposed to be working.
How would you like me to expand on it?
> > > I, perhaps mistakenly, assumed that if return-by-ref and sort() worked > > properly, then all other "usages of references" should equally work > > the same, is that not right? If it's not right, why not? > > I'm not sure, I didn't check all of them yet. All these need to be tested.
I don't know the other "contexts" that need to be tested. There are already over 80 tests written for this (.phpt) that all pass, please let me know what other tests need to be written and I'd be happy to write them.
> > Certainly, this basic object oriented functionality, of course it is > > upheld. This is one of the reasons I leveraged standard functions in > > the first place, because these issues were automatically handled by > > the existing core. > > I'm not sure overriding "public $foo" with "public $foo { protected > get() {} }" is covered by existing code. Existing code has no way to cover such things.
This isn't over-riding, this is simultaneous existence and at present, properties shadow accessors, though Nikita wants to invert that.
> > This is currently up in the air on the RFC side and is being referred > > to as which "shadows" which. The code currently has it that > > properties shadow accessors, Nikita suggested that should be inverted > > and I agree, so unless someone else thinks otherwise, accessors will > > probably shadow properties. > > Which raises the question above again - how LSP is preserved? What would happen in such case?
It's preserved in the same exact way (and by the same exact code) that any other function over-rides are handled. The accessor syntax, LITERALLY, translates into functions and thus go through the ordinary LSP preservation lines of code. It's the reason I chose to go that route in the first place (to leverage the existing LSP checks)
> > > See response to #2 above, without a getter/setter returning a real > > property, isset/unset accessors were necessary because > > isset()/unset() cannot perform on a dynamic value. Consider this: > > You seem to misunderstand what I wrote there. I am proposing that > isset() would a) always have default implementation if get() is defined and b) work exactly like PHP isset() in that the way that if > get() returns null (or does not exist if write-only properties are introduced) then it returns false, otherwise it returns true. > The fact that current isset() operator does not work on T variables is irrelevant, since I'm talking about level below that - how that > operator is implemented. The implementation should be augmented so that for properties with accessors it would work exactly the > same as for properties without ones.
With isset/unset accessors, they *can be* implemented appropriately, with automatic isset/unset implementations, they cannot, for reasons stated above: php > isset(a()); Fatal error: Can't use function return value in write context in php shell code on line 1
> > direction of accessors shadowing properties, I could see the default > > implementation of isset/unset actually be more like unset() { return > > unset($this->bar); } and isset() { return isset($this->bar); } > > If this is pseudocode, meaning "call get accessor if exists and return true if return value is not null, otherwise return false" then it's > fine for isset. For unset, you'd still want to use set accessor or fail if set accessor is not defined.
You've got that right... isset() cannot exist (and is not allowed) if a getter is not defined, the inverse is true of unset.
> > Oh crap, you're referring to the idea that during compilation a class > > definition may exist below the point of "current compilation," right? > > I had not considered this case and you're right, this would break > > badly. > > Yes, class definition is runtime, when you compiling code you have no idea not only what this class is, but thanks to $foo::bar also > what this class' name is (which is no different since if you had the name you still had no idea what class is under this name).
Yes, these cases were not considered. The static accessors portion of this project took up an inordinate amount of time as compared to the rest.
> > > of how it worked. I still do not know if I could make this work at > > the vm/opcode level during runtime but I'd be much more willing to > > give it a go now. > > I think you'd need some additional hooks in the class structure for this and some additional methods in zend_object_handlers.c. If > you need more on this please send me a message, I'll try to help you (though next 2 weeks I'll have very sketchy internet access as > I'm on vacation/traveling, so please be patient :) >
Yes! I could really use some help in this area. I will PM you because I never knew which way to go or which way would be acceptable (adding new opcodes, messing with existing opcodes, etc). I would like to get static accessors calling into zend_object_handlers.c the way objects are but it was beyond me how to do this when I was struggling to find a way. I was actually quite excited when I found code in the core which "back patches" the op array to accomplish some things, because it gave me "the way" to make this work. Now that you've brought these valid points up, my implementation will not work.
> > You and Nikita get your gloves on, makes it easier on me to go your > > direction but Nikita did not like this at all. > > Well, I think I've explained my approach above, if Nikita has something to add he's most welcome. > > > This would refer to the fact that a debug backtrace would show that > > it's in a function called __getHours() (for example) which would only > > serve to confuse a php developer who doesn't know that a getter for > > $Hours is really a function named __getHours(). Therefore I will be > > I think it's a wrong approach to lie to the developer in order to make it "easier". If the engine has function __getHours, it should say > so. > The developer smart enough to use properties is smart enough to understand very simple concept of auto-generated function name > (given that __get exists for years in the engine).
But what is the value of it? The developer is working within the context of an accessor, not an implementation detail which doesn't mean anything to them?
> > I could not get this point across to anyone but gave up, private final > > set(); != read-only. With a non "read-only" keyword scenario, a "set" > > call would still be allowed and would show up as an access violation > > to outside observers and would be allowed from within the class. This > > is not what I wanted but I had enough other points of contention and I > > yielded on this. > > I see. I guess this should be TODO then, maybe somebody will have better ideas. I really don't like special case keywords, so it'd be > great if we could figure it out using generic approach or maybe some light magic like autogenerated method throwing predefined > exception.
In this regard, I have yet to see any proposal that is as clear or concise as public read-only $abc. What is the big problem with adding read-only and write-only keywords? Once they are in the language they could be expanded to plain properties.

Amaury Bouchard

13 years ago
2012/10/16 Clint Priest <cpriest@zerocue.com>
> In this regard, I have yet to see any proposal that is as clear or concise > as public read-only $abc. What is the big problem with adding read-only > and write-only keywords? Once they are in the language they could be > expanded to plain properties.
public:const $abc; (in cyberspace, no one can hear me scream) No need for another keyword when there is one doing the job. IMHO, write-only doesn't make any sense. If you define something "like an attribute" to be only writeable, in fact you are defining a method. But you use it like that: $obj->attr = 3; instead of using it like that: $obj->meth(3); What's the point? More, read-only and write-only are very poor-meaning keywords. We need full PPP visibility (and yes, I know the RFC allows asymetric visibility, but it implies to create accessors and therefore some code).

Stas Malyshev

13 years ago
Hi!
> I very much disagree, engine details should not be visible to users. > It is irrelevant to them and only serves to confuse.
It's not engine detail. We're not talking about exposing C pointers or zend_op values. We're talking about implementing methods that have special meaning. They are still methods and it only makes sense that they behave like other methods do. Not doing that - as you are perfectly aware - adds a lot of complications and makes the engine inconsistent - now you have methods that engine treats one way and methods that the engine teats in completely different way, and in every place you deal with methods you need to have special cases.
> To be clear, they would be regular methods, they would just not exist > in the HashTable *functions of the class. In every other way they > are methods of the class.
Don't sound like a good idea. Imagine a debugger/profiler that works with PHP. Now instead of having one hashtable to deal with it has two since current function may or may not be in different hashtable. Imagine extension or other tool or just engine part that deals with functions - now everywhere you have to make provisions for the fact that functions now live in two places instead of one. I don't think design-wise it is a good idea.
> See the problem?
I am *NOT* saiyng you should apply isset operator as it to it. I am saying that code that is implementing the default isset should work *exactly* as the isset operator would work on regular property. Of course it can not be the same operator - it should be changed/extended - but it should keep working the same way. E.g. not return "not set" when property is set to 0.
> I don't know the other "contexts" that need to be tested. There are > already over 80 tests written for this (.phpt) that all pass, please > let me know what other tests need to be written and I'd be happy to > write them.
I think the following cases should be covered: - $foo->bar++/-- (postfix and prefix) - with actual value being number, string, empty, not existing. - $foo->bar[$index] = $x (with value being array, object with ArrayAccess, string, integer - the last should produce proper error and no leaks) - $foo->bar[] = $x with the same as above - $foo->bar->baz = $x with $bar being object or empty or non-object value (proper error and no leaks) - $foo->bar{$x} = "foo" (string offsets), also checks that non-string vars work fine - $foo->bar =& $bar->baz, etc. with modifying both sides and checking everything works (including modifying via accessors) - property loop detection needs to be tested (i.e. $this->foo in getter for $foo or $this->bar in $foo and $this->foo in $bar). Maybe more if I'd think of anything. I realize some of these may feel obvious but we're modifying very core part - it's better to be safe than sorry.
> It's preserved in the same exact way (and by the same exact code) > that any other function over-rides are handled. The accessor syntax, > LITERALLY, translates into functions and thus go through the ordinary > LSP preservation lines of code. It's the reason I chose to go that > route in the first place (to leverage the existing LSP checks)
The accessor syntax translates to functions but public $foo does not. That's the issue. There's no existing check for overriding public $foo with private accessor. There are checks that would work between two accessors - but not between accessor and plain property.
> With isset/unset accessors, they *can be* implemented appropriately, > with automatic isset/unset implementations, they cannot, for reasons > stated above: > > php > isset(a()); > > Fatal error: Can't use function return value in write context in php > shell code on line 1
You do not need to apply isset() as is to function call. You can have isset method that is smart enough to do the right thing. You're writing the engine patch, not the PHP code transformation tool, so you can do more things here. If you need specific, I can look into the code, probably later as I'll have to move on pretty soon :)
> You've got that right... isset() cannot exist (and is not allowed) if
isset() should always be allowed - it just should return false if something goes wrong. This is how it is used now - as safe operator which will always work and return false if something is wrong. Making isset() "not allowed" means that you need another operator saying "am I allowed to call isset() here or will my code fail?". No need for that as isset() is exactly such kind of operator for read access. No such thing for write access btw which is quite bad since nothing now guarantees simple $foo->bar = 1; wouldn't blow up and there's no way to check for that. Which makes impossible to write robust code. This alone makes me very uneasy about the whole read-only idea.
> Yes, these cases were not considered. The static accessors portion > of this project took up an inordinate amount of time as compared to > the rest.
Maybe we should limit accessors to dynamic ones for now or split it out to additional spec/1.1 feature?
> But what is the value of it? The developer is working within the > context of an accessor, not an implementation detail which doesn't > mean anything to them?
If it doesn't mean anything, he can ignore it. However, if it *does* mean something to him (e.g. he wants to profile/test/debug his code, or he wants to do some other thing where current function is important) it should be consistent. I'm very much against introducing inconsistencies in the engine when it is not warranted by very good reasons.
> In this regard, I have yet to see any proposal that is as clear or > concise as public read-only $abc. What is the big problem with > adding read-only and write-only keywords? Once they are in the > language they could be expanded to plain properties.
I think special-case keywords are usually not a good idea unless they cover a very frequent use case and save a lot of boilerplate code - why a very rare case of write-only variable gets a keyword and my favorite case doesn't? In any case, for the reasons described above I think read-only is not a very good idea as such and we should not encourage people to do it with special keyword unless we have a way to handle it.
-- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227