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 like it.
I assume that, in regards to static properties, the static keyword works
just as well as self and parent, correct?
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
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?
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
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
Hey Bernhard,
I see a few problems with the proposal:
-
If you're adding all of the ArrayAccess functions, what about all of the Iterator functions? How about other SPL aspects, like Countable, etc.
-
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?)
-
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
-----Original Message-----
From: Bernhard Schussek [mailto:bschussek@gmail.com]
Sent: Friday, October 12, 2012 3:07 AM
To: Clint Priest
Cc: internals@lists.php.net
Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2Hi 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
Hi Clint,
-
Point taken.
-
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 don't know if many other languages allow overloading of the array
accessors at all.
Cheers,
Bernhard
Point taken.
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 classesArticle
Category
Tag
ArticleTags
CategoryArticles
TagArticlesjust 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...
- 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.
Cheers,
Bernhard
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
hi Lester,
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
Pierre Joye wrote:
hi Lester,
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
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
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
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
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
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
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
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?
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Friday, October 12, 2012 5:48 AM
To: Clint Priest
Cc: internals@lists.php.net
Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2Alright, 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
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
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.
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
I must have missed taking that part out of the RFC, they would not be needed any more with those keywords being removed.
-----Original Message-----
From: Levi Morrison [mailto:morrison.levi@gmail.com]
Sent: Friday, October 12, 2012 9:45 AM
To: Clint Priest
Cc: internals@lists.php.net
Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2Alright, 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
andZEND_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
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
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
> On Sat, Oct 13, 2012 at 10:06 AM, Benjamin Eberlei
> 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 :-)
>
> 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
> 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.
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.commailto: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.
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.
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.netSubject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2****
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.****
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.commailto:amaury.bouchard@gmail.com [mailto:amaury.bouchard@gmail.commailto: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.netmailto:internals@lists.php.net
Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2
2012/10/13 Nikita Popov <nikita.ppv@gmail.commailto: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.
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.
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:
- 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.
- 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.
- 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
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:
- 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.
- 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.
- 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
http://www.colder.ch
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.
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:
- 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.
- 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.
- 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
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.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:
- 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.
- 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.
- 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
http://www.colder.ch
Hi!
https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
My feedback on the RFC:
-
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. -
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.) -
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? Thesort()
case
mentions by-ref return but does not explicitly mention all other cases.
These need to be covered explicitly in the RFC. -
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? -
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).
-
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). -
"Error messaging" section is not clear. Some examples would help -
what is being translated to what? -
"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.
- 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.
-
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. -
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
#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.comwrote:
Hi!
https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
My feedback on the RFC:
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.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.)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? Thesort()
case
mentions by-ref return but does not explicitly mention all other cases.
These need to be covered explicitly in the RFC.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?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).
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)."Error messaging" section is not clear. Some examples would help -
what is being translated to what?"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.
- 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.
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.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
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
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.comwrote:
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
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
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.comwrote:
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
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
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.comwrote:
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
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
Excuse my late-night-influenced terminology, the word "empty" is much more
suitable than "does not have".
And this "solution" really is more of a hack or work-around than a solution.
I do think that there is a better way to go about implementing
read/write-only, but nothing has come to mind as of yet - at least nothing
that doesn't completely change major aspects of this RFC.
On Tue, Oct 16, 2012 at 3:59 AM, Stas Malyshev smalyshev@sugarcrm.comwrote:
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
Jesus, this is such a CF. Can I not write understandable English?
read-only and write-only ENFORCE UPON SUBCLASSES that no setter or getter (respectively) can be defined. That's all they do. There is no currently proposed solution that meets this need.
This "NEED" was pulled from the original RFC that Dennis wrote some 4 years ago. Perhaps we don't even NEED a solution to this problem.
-----Original Message-----
From: Stas Malyshev [mailto:smalyshev@sugarcrm.com]
Sent: Tuesday, October 16, 2012 5:59 AM
To: Jazzer Dane
Cc: Clint Priest; internals@lists.php.net
Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2Hi!
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
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.
-----Original Message-----
From: Stas Malyshev [mailto:smalyshev@sugarcrm.com]
Sent: Tuesday, October 16, 2012 5:48 AM
To: Jazzer Dane
Cc: Clint Priest; internals@lists.php.net
Subject: Re: [PHP-DEV] [PHP-DEV [RFC] Property Accessors v1.2Hi!
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
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
- 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.
- 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.
- 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? Thesort()
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?
- 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.
- 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.
- 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); }
- "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.
- "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.
- 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.
- 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.
- 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.
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
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).
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
- 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.
- 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.
- 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? Thesort()
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?
- 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.
- 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.
- 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); }
- "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 pointSo 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.
- "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.
- 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.
- 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.
- 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.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
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).
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
- 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.
- 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.
- 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? Thesort()
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?
- 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.
- 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.
- 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); }
- "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.
- "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.
- 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.
- 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.
- 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.
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<tel:%28408%29454-6900%20ext.%20227
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.
- 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? - 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.
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).****
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****
- 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.****
- 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.****
- 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? Thesort()
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?****
- 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.****
- 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.*
- 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); }****
- "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 pointSo 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.****
- "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.****
- 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.****
- 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.****
- 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.*
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****
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
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() andI 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 anI'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 ofYes, 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 beI 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.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
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).
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