It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it.
RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
Example Usage:
class TimePeriod {
private $Seconds = 3600;
public $Hours {
get { return $this->Seconds / 3600; }
set { $this->Seconds = $value; }
isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); }
unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); }
}
}
Changes / Updates
-
isset/unset accessor functions now implemented (object & static context, auto implementations, etc)
-
static accessor now fully functional
-
Reference functionality validated, tests written
-
All operators have been tested, tests written
-
read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section
-
Tested for speed, approaches or meets __get() speed.
Internally things have changed quite a bit
-
cleaned up and simplified
-
had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY)
-
the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed it.
The code is available at the url below and is up to date with master, all tests pass.
https://github.com/cpriest/php-src
I'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated:
-
Check on reflection code written prior to major changes (tests still pass)
-
Add a few more reflection functions that were requested
In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know.
-Clint
As an update, just ran some performance testing:
master
Cycles Direct Getter __get
v1.4 @ 10/8/2012 1m .05s .21s .20s
php 5.5.0-dev
Cycles Direct Getter __get
v1.4 @ 10/8/2012 1m .04s n/a .21s
Performance of property accessors was important to me as I'm sure it will be to many, on one million cycles of a simple getter, it's <.01s difference. Depending on the run it is sometimes exactly the same performance.
-----Original Message-----
From: Clint Priest [mailto:cpriest@zerocue.com]
Sent: Monday, October 08, 2012 6:53 AM
To: internals@lists.php.net
Subject: [PHP-DEV] [RFC] Propety Accessors v1.1It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it
out the door for PHP 5.5 release. It's come a long way since the last time I posted about it.RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
Example Usage:
class TimePeriod {
private $Seconds = 3600;public $Hours { get { return $this->Seconds / 3600; } set { $this->Seconds = $value; } isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } }
}
Changes / Updates
isset/unset accessor functions now implemented (object & static context, auto implementations, etc)
static accessor now fully functional
Reference functionality validated, tests written
All operators have been tested, tests written
read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section
Tested for speed, approaches or meets __get() speed.
Internally things have changed quite a bit
cleaned up and simplified
had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY)
the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed
it.
The code is available at the url below and is up to date with master, all tests pass.
https://github.com/cpriest/php-srcI'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated:
Check on reflection code written prior to major changes (tests still pass)
Add a few more reflection functions that were requested
In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know.
-Clint
Good work Clint!
Performance is probably one of the things people will complain about
so it's good that your benchmark is proving that it's merely identical
to the old __get() approach.
What remains on your TODO list for this functionality?
When are you planning to run an RFC vote on this?
I think this would be a valuable addition to PHP 5.5.
Cheers,
Paul.
As an update, just ran some performance testing:
master
Cycles Direct Getter __get
v1.4 @ 10/8/2012 1m .05s .21s .20sphp 5.5.0-dev
Cycles Direct Getter __get
v1.4 @ 10/8/2012 1m .04s n/a .21sPerformance of property accessors was important to me as I'm sure it will be to many, on one million cycles of a simple getter, it's <.01s difference. Depending on the run it is sometimes exactly the same performance.
-----Original Message-----
From: Clint Priest [mailto:cpriest@zerocue.com]
Sent: Monday, October 08, 2012 6:53 AM
To: internals@lists.php.net
Subject: [PHP-DEV] [RFC] Propety Accessors v1.1It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it
out the door for PHP 5.5 release. It's come a long way since the last time I posted about it.RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
Example Usage:
class TimePeriod {
private $Seconds = 3600;public $Hours { get { return $this->Seconds / 3600; } set { $this->Seconds = $value; } isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } }
}
Changes / Updates
isset/unset accessor functions now implemented (object & static context, auto implementations, etc)
static accessor now fully functional
Reference functionality validated, tests written
All operators have been tested, tests written
read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section
Tested for speed, approaches or meets __get() speed.
Internally things have changed quite a bit
cleaned up and simplified
had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY)
the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed
it.
The code is available at the url below and is up to date with master, all tests pass.
https://github.com/cpriest/php-srcI'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated:
Check on reflection code written prior to major changes (tests still pass)
Add a few more reflection functions that were requested
In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know.
-Clint
Hi!
What remains on your TODO list for this functionality?
When are you planning to run an RFC vote on this?I think this would be a valuable addition to PHP 5.5.
I think we shouldn't rush with votes on this until all fine details
aren't hashed out. This is a huge feature - one of the biggest ones
recently, and has a lot of implications for various scenarios. Property
access is what virtually every script in existence does, and doing
changes there have implications that touch every corner of the engine.
I think Clint is doing a great job with this RFC, but we need to
carefully work through all the corners and side cases and relationships
with all other features before we can declare it's ready for the prime
time. Exactly because it is such a big deal it needs to be refined and
polished.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
At this point, the last two weeks of deliberations on this RFC have pushed its release back quite a long ways. I don't know when 5.5 is due out but if it's within the next 6 months (and I'm the only one working on the code) then it will probably not be happening. Due to the dramatic changes that have been talked about, I may just scrap the fork and start over, since I've learned a lot about php-core since starting this a year ago.
-----Original Message-----
From: Stas Malyshev [mailto:smalyshev@sugarcrm.com]
Sent: Tuesday, October 16, 2012 4:27 AM
To: Paul Dragoonis
Cc: Clint Priest; internals@lists.php.net
Subject: Re: [PHP-DEV] [RFC] Propety Accessors v1.1Hi!
What remains on your TODO list for this functionality?
When are you planning to run an RFC vote on this?I think this would be a valuable addition to PHP 5.5.
I think we shouldn't rush with votes on this until all fine details aren't hashed out. This is a huge feature - one of the biggest ones
recently, and has a lot of implications for various scenarios. Property access is what virtually every script in existence does, and
doing changes there have implications that touch every corner of the engine.I think Clint is doing a great job with this RFC, but we need to carefully work through all the corners and side cases and relationships
with all other features before we can declare it's ready for the prime time. Exactly because it is such a big deal it needs to be refined
and polished.Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi!
What remains on your TODO list for this functionality?
When are you planning to run an RFC vote on this?I think this would be a valuable addition to PHP 5.5.
I think we shouldn't rush with votes on this until all fine details
aren't hashed out. This is a huge feature - one of the biggest ones
recently,
Exactly because it is such a big deal it needs to be refined and
polished.
Amen.
Stas Malyshev wrote:
What remains on your TODO list for this functionality?
When are you planning to run an RFC vote on this?I think this would be a valuable addition to PHP 5.5.
I think we shouldn't rush with votes on this until all fine details
aren't hashed out. This is ahuge feature - one of the biggest ones
recently, and has a lot of implications for various scenarios. Property
access is what virtually every script in existence does, and doing
changes there have implications that touch every corner of the engine.I think Clint is doing a great job with this RFC, but we need to
carefully work through all the corners and side cases and relationships
with all other features before we can declare it's ready for the prime
time. Exactly because it is such a big deal it needs to be refined and
polished.
But a vote NOT to include it should still be one of the options!
The more edge cases I see on this discussion, the more I am convinced that this
is simply not right for PHP ... it's not needed and only adds unnecessary bloat.
The more the functionality of PHP gets buried in 'magic code' that can't even be
debugged properly or viewed the less useful PHP becomes.
I'm sorry if people are spending a lot of time trying to make something work,
but that is their choice, and should not be a reason to include something.
I still don't see how this improves anything when all we need to be doing is
managing the existing variables, arrays and objects stored in the object. __get
and __set aren't needed either but that is another matter. Does anybody actually
use them, or are they waiting for the 'better alternative'?
Is there any real reason not simply to be using $obj->var ? It is the fastest
way of doing it anyway ...
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
Hi,
This summer (july 15) I did another proposal, which has some connections
with yours.
For the main usage of getters/setters, my guess is that we need separate
read/write visibilities. Your RFC goes beyond that, but I think both are
complementary.
Most of the time, we write getters/setters to ensure that a private
attribute can be read but not modified (or modified only as we want to).
Your RFC is pretty powerful in some use cases. Like your example, which is
a "virtual" attribute, created from some processing. But create a new
"read-only" keyword is not a good idea for me: introduce a new keyword
should be avoided when it's not absolutely necessary, and it lacks meaning
(exact visibility information).
Correct me if I'm wrong, but your syntax is similar to the one used in C#.
Not a bad idea, but I think we can imagine for PHP somethink a little more
concise for the general usage.
My idea was to write attribute's visibility like
"read_visiblity:write_visibility $attr;"
public:protected $foo; // public reading, protected writing
public:private $bar; // public reading, private writing
protected:private $aaa; // protected reading, private writing
protected:const $bbb; // protected reading, no writing
With your RFC it will be:
public $foo { get; protected set; }
public $bar { get; private set; }
protected $aaa { get; private set; }
protected read-only $bbb;
When I did my proposal on the internals mailing-list, I got some pretty bad
feedbacks, and some very good ones.
So, maybe we can try to merge some ideas?
My patch: http://github.com/Amaury/php-src
Regards,
Amaury
2012/10/8 Clint Priest cpriest@zerocue.com
It's been a while since I posted any updates about this, a few individuals
have been asking about it privately and wanting me to get it out the door
for PHP 5.5 release. It's come a long way since the last time I posted
about it.RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
Example Usage:
class TimePeriod {
private $Seconds = 3600;public $Hours { get { return $this->Seconds / 3600; } set { $this->Seconds = $value; } isset<http://www.php.net/isset> { return isset<
http://www.php.net/isset>($this->Seconds); }
unsethttp://www.php.net/unset { unsethttp://www.php.net/unset($this->Seconds);
}
}
}Changes / Updates
isset/unset accessor functions now implemented (object & static
context, auto implementations, etc)
static accessor now fully functional
Reference functionality validated, tests written
All operators have been tested, tests written
read-only and write-only keywords: Added explanation of reasons
for inclusion at the top of the appropriate RFC section
Tested for speed, approaches or meets __get() speed.
Internally things have changed quite a bit
cleaned up and simplified
had been using 4 to 5 additional fn_flag slots, now down to two
(READ_ONLY and WRITE_ONLY)
the automatic implementations now compiled internal php code,
this greatly simplified that part of the code and future proofed it.
The code is available at the url below and is up to date with master, all
tests pass.
https://github.com/cpriest/php-srcI'd like to get this project wrapped up in time to make it to the 5.5
release, only a few things remain to be completed/updated:
Check on reflection code written prior to major changes (tests
still pass)
Add a few more reflection functions that were requested
In total there are 79 tests for this new functionality, if there are any
others that I have missed, please let me know.-Clint
It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it.
RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
Example Usage:
class TimePeriod {
private $Seconds = 3600;public $Hours { get { return $this->Seconds / 3600; } set { $this->Seconds = $value; } isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } }
}
Changes / Updates
isset/unset accessor functions now implemented (object & static context, auto implementations, etc)
static accessor now fully functional
Reference functionality validated, tests written
All operators have been tested, tests written
read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section
Tested for speed, approaches or meets __get() speed.
Internally things have changed quite a bit
cleaned up and simplified
had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY)
the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed it.
The code is available at the url below and is up to date with master, all tests pass.
https://github.com/cpriest/php-srcI'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated:
Check on reflection code written prior to major changes (tests still pass)
Add a few more reflection functions that were requested
In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know.
What concerns me with the current implementation is that it leaks many
implementation details, in particular the fact that the accessors are
implemented as real __getXYZ methods and automatic implementations
also use real $__XYZ properties.
A few examples:
1 - __getProperty() method directly callable
class Test {
public $property {
get { return 123; }
}
}
$test = new Test;
var_dump($test->property); // int(123)
var_dump($test->__getProperty()); // int(123)
2 - __getProperty() method exposed via exception
class Test {
public $throwingProperty {
get { throw new Exception; }
}
}
(new Test)->throwingProperty;
exception 'Exception' in /home/nikic/dev/php-src/t29.php:9
Stack trace:
#0 /home/nikic/dev/php-src/t29.php(31): Test->__getthrowingProperty()
#1 {main}
3 - Can directly access $__automaticProperty and even unset it
(causing notices in the internal code)
class Test {
public $automaticProperty {
get; set;
}
public function getAutomaticProperty() {
return $this->__automaticProperty;
}
public function unsetAutomaticProperty() {
unset($this->__automaticProperty);
}
}
$test->automaticProperty = 'foo';
var_dump($test->getAutomaticProperty());
$test->unsetAutomaticProperty();
var_dump($test->automaticProperty);
string(3) "foo"
Notice: Undefined property: Test::$__automaticProperty in
/home/nikic/dev/php-src/t29.php on line 13
NULL
=====
I feel like this approach to the implementation will be a big can of
worms. Sure, it works, but it is rather fragile and the enduser ends
up dealing with internal stuff which he ought not care about. I think
it would be better to cleanly separate out the accessor
implementation. It might require more code now, but will be better in
the long run.
Nikita
Hi,
It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it.
RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
Example Usage:
class TimePeriod {
private $Seconds = 3600;public $Hours { get { return $this->Seconds / 3600; } set { $this->Seconds = $value; } isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } }
}
Changes / Updates
isset/unset accessor functions now implemented (object & static context, auto implementations, etc)
static accessor now fully functional
Reference functionality validated, tests written
All operators have been tested, tests written
read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section
Tested for speed, approaches or meets __get() speed.
Internally things have changed quite a bit
cleaned up and simplified
had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY)
the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed it.
The code is available at the url below and is up to date with master, all tests pass.
https://github.com/cpriest/php-srcI'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated:
Check on reflection code written prior to major changes (tests still pass)
Add a few more reflection functions that were requested
In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know.
What concerns me with the current implementation is that it leaks many
implementation details, in particular the fact that the accessors are
implemented as real __getXYZ methods and automatic implementations
also use real $__XYZ properties.A few examples:
1 - __getProperty() method directly callable
class Test {
public $property {
get { return 123; }
}
}$test = new Test;
var_dump($test->property); // int(123)
var_dump($test->__getProperty()); // int(123)2 - __getProperty() method exposed via exception
class Test {
public $throwingProperty {
get { throw new Exception; }
}
}(new Test)->throwingProperty;
exception 'Exception' in /home/nikic/dev/php-src/t29.php:9
Stack trace:
#0 /home/nikic/dev/php-src/t29.php(31): Test->__getthrowingProperty()
#1 {main}3 - Can directly access $__automaticProperty and even unset it
(causing notices in the internal code)
class Test {
public $automaticProperty {
get; set;
}public function getAutomaticProperty() { return $this->__automaticProperty; } public function unsetAutomaticProperty() { unset($this->__automaticProperty); }
}
$test->automaticProperty = 'foo';
var_dump($test->getAutomaticProperty());
$test->unsetAutomaticProperty();
var_dump($test->automaticProperty);string(3) "foo"
Notice: Undefined property: Test::$__automaticProperty in
/home/nikic/dev/php-src/t29.php on line 13
NULL
=====
I feel like this approach to the implementation will be a big can of
worms. Sure, it works, but it is rather fragile and the enduser ends
up dealing with internal stuff which he ought not care about. I think
it would be better to cleanly separate out the accessor
implementation. It might require more code now, but will be better in
the long run.
I disagree, to me that this feature is all about syntactic sugar, as
such it does what it expected: it generates concrete properties and
methods that are somewhat hidden to the end-user.
I feel that any implementation that do not rely on proper
properties/methods would be a big hack.
Best,
Nikita
--
--
Etienne Kneuss
http://www.colder.ch
Is there a reason why we cannot implement this using PHP's already widely used function syntax:
class TimePeriod {
private $Seconds = 3600;
public $Hours {
public function get() { return $this->Seconds / 3600; }
private function set($value) { $this->Seconds = $value; }
/*public implied */function isset(){ return isset ($this->Seconds); }
private function unset() { unset ($this->Seconds); }
}
}
This would be much less confusing as it follows other PHP standards for creating functions and such. I know C# has a similar syntax to what is proposed, but we are not developing for C# we are developing for PHP which has its own syntax rules that differ from C#'s and my vote is to follow PHP's already in existent syntax format.
-Nathan Bruer
-----Original Message-----
From: ekneuss@gmail.com [mailto:ekneuss@gmail.com] On Behalf Of Etienne Kneuss
Sent: Tuesday, October 09, 2012 8:15 AM
To: Nikita Popov
Cc: Clint Priest; internals@lists.php.net
Subject: Re: [PHP-DEV] [RFC] Propety Accessors v1.1
Hi,
It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it.
RFC Document:
https://wiki.php.net/rfc/propertygetsetsyntax-as-implementedExample Usage:
class TimePeriod {
private $Seconds = 3600;public $Hours { get { return $this->Seconds / 3600; } set { $this->Seconds = $value; } isset<http://www.php.net/isset> { return isset<http://www.php.net/isset>($this->Seconds); } unset<http://www.php.net/unset> { unset<http://www.php.net/unset>($this->Seconds); } }
}
Changes / Updates
isset/unset accessor functions now implemented (object & static context, auto implementations, etc)
static accessor now fully functional
Reference functionality validated, tests written
All operators have been tested, tests written
read-only and write-only keywords: Added explanation of reasons for inclusion at the top of the appropriate RFC section
Tested for speed, approaches or meets __get() speed.
Internally things have changed quite a bit
cleaned up and simplified
had been using 4 to 5 additional fn_flag slots, now down to two (READ_ONLY and WRITE_ONLY)
the automatic implementations now compiled internal php code, this greatly simplified that part of the code and future proofed it.
The code is available at the url below and is up to date with master, all tests pass.
https://github.com/cpriest/php-srcI'd like to get this project wrapped up in time to make it to the 5.5 release, only a few things remain to be completed/updated:
Check on reflection code written prior to major changes (tests still pass)
Add a few more reflection functions that were requested
In total there are 79 tests for this new functionality, if there are any others that I have missed, please let me know.
What concerns me with the current implementation is that it leaks many
implementation details, in particular the fact that the accessors are
implemented as real __getXYZ methods and automatic implementations
also use real $__XYZ properties.A few examples:
1 - __getProperty() method directly callable
class Test {
public $property {
get { return 123; }
}
}$test = new Test;
var_dump($test->property); // int(123)
var_dump($test->__getProperty()); // int(123)2 - __getProperty() method exposed via exception
class Test {
public $throwingProperty {
get { throw new Exception; }
}
}(new Test)->throwingProperty;
exception 'Exception' in /home/nikic/dev/php-src/t29.php:9 Stack
trace:
#0 /home/nikic/dev/php-src/t29.php(31): Test->__getthrowingProperty()
#1 {main}3 - Can directly access $__automaticProperty and even unset it
(causing notices in the internal code)
class Test {
public $automaticProperty {
get; set;
}public function getAutomaticProperty() { return $this->__automaticProperty; } public function unsetAutomaticProperty() { unset($this->__automaticProperty); }
}
$test->automaticProperty = 'foo';
var_dump($test->getAutomaticProperty());
$test->unsetAutomaticProperty();
var_dump($test->automaticProperty);string(3) "foo"
Notice: Undefined property: Test::$__automaticProperty in
/home/nikic/dev/php-src/t29.php on line 13NULL
=====
I feel like this approach to the implementation will be a big can of
worms. Sure, it works, but it is rather fragile and the enduser ends
up dealing with internal stuff which he ought not care about. I think
it would be better to cleanly separate out the accessor
implementation. It might require more code now, but will be better in
the long run.
I disagree, to me that this feature is all about syntactic sugar, as such it does what it expected: it generates concrete properties and methods that are somewhat hidden to the end-user.
I feel that any implementation that do not rely on proper properties/methods would be a big hack.
Best,
Nikita
--
To unsubscribe,
visit: http://www.php.net/unsub.php
--
Etienne Kneuss
http://www.colder.ch
What concerns me with the current implementation is that it leaks many
implementation details, in particular the fact that the accessors are
implemented as real __getXYZ methods and automatic implementations
also use real $__XYZ properties.
Further to this, take the following example.
public $_state {
set { ... }
}
This automatically generates a "hidden" function __set_state(), which
is a magic method already (and should be declared static, and take an
array as it's only parameter)
Another fine example of how automatically generating real
implementations can potentially break things.
RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
public $property {
set { $this->property = ($this->property*2)+$value }
get;
}
How do I reference the property being set from within the function? The way
I have done it in the example will cause recursion? How can I assign to
"self"?
How do I set the default value of $property when the object is created?
Surely I don't have to reverse the set accessors logic and set the inverse
in __construct?
I think Leigh brings up some important flaws to in the current RFC. What
Leigh is asking for does not appear to be possible, and in my opinion, it
should be.
I also agree with Rasmus, to a certain extent.
By putting only a getter/setter, the developer essentially sets the
property as read or write only. At first glance, there is no need for the
read-only or write-only keyword. Except... subclassing may or may not be an
issue.
By setting a property to read-only, it ensures that 'set' can never be set
by a subclass. Unless I redefine the entire property... or is that not even
possible? *The RFC isn't very clear but it appears that there is no way to
redefine the entire property, as if you define it in the subclass with only
get, then it will take set, isset, and unset from the parent class, correct?
- Though, that can be outright stopped by making the property final. But
then there is no point in having read/write-only.
From what I can tell, read-only becomes useful if I extend the class and
want to partially modify the property.
Example:
class A {
public $seconds = 3600;public $hours {
get() { return $this->seconds / 3600 };
}
}class B extends A {
public $hours { // Maintains 'get' from class A
set($value) { $this->seconds = $value; }
}
}
^There's no way to stop the developer from doing that without read-only.
Also, if the property is public, what if an outside class tries to do this:
class A {
public $seconds = 3600;public $hours {
get() { return $this->seconds / 3600 };
}
}$object = new A();
$object->hours = 100;
What happens then?
And similarly, how do we set a public property as a property accessor, hmm?
class A {
public $hours = 1;
}$seconds = 20;
$object = new A();
$object->hours = { // Does this work?
get() { return $seconds; }
};
There's definitely still some questions to answer before this RFC is ready.
RFC Document:
https://wiki.php.net/rfc/propertygetsetsyntax-as-implementedpublic $property {
set { $this->property = ($this->property*2)+$value }
get;
}How do I reference the property being set from within the function? The way
I have done it in the example will cause recursion? How can I assign to
"self"?How do I set the default value of $property when the object is created?
Surely I don't have to reverse the set accessors logic and set the inverse
in __construct?
Wow, I'm surprised by all the talk about this RFC this time around. I posted this numerous times in the past trying to elicit feedback and got little to none, so I took the time to write it as I thought it should be written. Some of these things will take considerable effort to fix/correct/change.
I'll try to address everyone's comments/questions in one post:
Nikita:
What concerns me with the current implementation is that it leaks many implementation details, in particular the fact that the accessors are implemented as real __getXYZ methods and automatic implementations also use real $__XYZ properties.
A few examples:
1 - __getProperty() method directly callable
class Test {
public $property {
get { return 123; }
}
}
$test = new Test;
var_dump($test->property); // int(123)
var_dump($test->__getProperty()); // int(123)
I don't particularly see this as a problem (the ability to use it as a getter or call it as a function), but see my comments at the end. I also don't particularly see the point of auto implemented getters/setters ( public $h { get; set; } )
2 - __getProperty() method exposed via exception
class Test {
public $throwingProperty {
get { throw new Exception; }
}
}
(new Test)->throwingProperty;
exception 'Exception' in /home/nikic/dev/php-src/t29.php:9 Stack trace:
#0 /home/nikic/dev/php-src/t29.php(31): Test->__getthrowingProperty()
#1 {main}
I have gone to great lengths to shield the implementation details from users, this one slipped past me. Nearly all errors that are shown reference a property rather than a function name and this could also be cleaned up.
3 - Can directly access $__automaticProperty and even unset it (causing notices in the internal code)
class Test {
public $automaticProperty {
get; set;
}
public function getAutomaticProperty() {
return $this->__automaticProperty;
}
public function unsetAutomaticProperty() {
unset($this->__automaticProperty);
}
}
$test->automaticProperty = 'foo';
var_dump($test->getAutomaticProperty());
$test->unsetAutomaticProperty();
var_dump($test->automaticProperty);
string(3) "foo"
Notice: Undefined property: Test::$__automaticProperty in /home/nikic/dev/php-src/t29.php on line 13
NULL
I'm not even sure that automatic backing fields are even desired, I never felt the need to have them in C# and the only reason they were included is because they were a part of Dennis's original proposal. Eliminating them would eliminate this as an issue.
=====
I feel like this approach to the implementation will be a big can of worms. Sure, it works, but it is rather fragile and the enduser ends up dealing with internal stuff which he ought not care about. I think it would be better to cleanly separate out the accessor implementation. It might require more code now, but will be better in the long run.
All three of these issues, could be addressed I believe by not having the functions and/or properties a part of the ordinary HashTables, or to have flags set on them. I believe there is already a SHADOW flag type defined which may be able to be used for this type of functionality.
Leigh:
Further to this, take the following example.
public $_state {
set { ... }
}
This automatically generates a "hidden" function __set_state(), which is a magic method already (and should be declared static, and take an array as it's only parameter)
Another fine example of how automatically generating real implementations can potentially break things.
This would clearly cause problems, again this could possibly be addressed by not having the accessor functions be a part of the standard Functions HashTable, the functions would not even need to have names at that point if they are not intended to be accessible directly.
public $property {
set { $this->property = ($this->property*2)+$value } get; }
How do I reference the property being set from within the function? The way I have done it in the example will cause recursion? How can I assign to "self"?
How do I set the default value of $property when the object is created?
Surely I don't have to reverse the set accessors logic and set the inverse in __construct?
Generally speaking, I don't know why you would use an automatic backing getter with a separate setter, but if you wanted to do that, at present you would access the automatic backing field by $this->__property.
The above will not cause recursion, it is protected from recursion by the same mechanism that __get() and __set() are protected. In fact, the above code would set an actual property named $property on the object which would then side-step the accessor entirely (true properties take precedence over accessors, though they may only be "set" from within the setter of the accessor. This may be a bit confusing, but I specifically wrote it this way for the purpose of lazy-loading.
For example:
public $objList {
get { return $this->objList = new myList(); }
set { $this->objList = $value; }
}
The first access of the $objList property getter would create the object and attempt to set its-self which would be passed to the setter, the setter (now guarded) will directly set the property on the object, further calls to $objList would retrieve the already created object (bypassing the accessor). To get out of that situation, you would simply unset($objList) and the accessor would take over again.
Please keep in mind that this is only possible from within the setter of the property, nothing outside the setter of the property can create a real property named the same as the accessor, except the setter of the accessor.
Nathan:
This would be much less confusing as it follows other PHP standards for creating functions and such. I know C# has a similar syntax to what is proposed, but we are not developing for C# we are developing for PHP which has its own syntax rules that differ from C#'s and my vote is to follow PHP's already in existent syntax format.
I think having the function keyword there is superfluous, it adds more code to be read, while accessors have the powers of a function, they are quite different from a function. You would probably never have a 35 line getter, for example.
Rasmus:
My only remaining comment is on the read-only and write-only keywords...
this seems really superfluous and strange to me - the syntax (using a hyphenated keyword) and the feature itself, is way off the grid as compared to other languages.
There is an important distinction with the read-only and write-only keywords and they are analogous to the final keyword. The final keyword prevents any subclasses from over-riding something declared as final. Final is supported for a property as well. The difference here is that a read-only property is semi-final. Subclasses may redefine a get declared as read-only but may not declare a setter. Personally I am not a fan of the concepts of final, read-only or write-only. They were included only because I thought the original RFC for which I was implementing had been ratified and the feature was desired.
Johannes:
About the items in regards to reflection in there:
The RFC states
ReflectionClass::getMethods() will not return accessor functions
(hides implementation detail).
Up until now reflection is leaky and is telling the truth. We should either keep that or completely clean up reflection. (mind also
get_class_methods()
and such)
I have not done anything with get_class_methods()
but I certainly could. Probably a better solution would be to have get_class_methods()
use Reflection to do its work, so that there is not duplicate code in the core?
I see no reason to continue leaking new code, if someone wants to head up an effort to make the rest of Reflection not leak implementation details then great, but to purposely leak implementation details for new features just doesn't make sense to me.
The RFC also introduces a new class ReflectionPropertyAccessor and has methods returning ReflectionProperty and ReflectionPropertyAccessor.
there should either be a common base class or interface (see i.e.
ReflectionFunctionAbstract)
I'm not certain there is a point to having a new base class, I would need to look at the code but I would bet that ReflectionPropertyAccessor is a sub-class of ReflectionProperty. In any case, we have the instanceof operator which can be used to distinguish between the results, no?
What will ReflectionPropertyAccessor->getGetter()->getName() return? I guess reflection will be leaky there? (see first item)
I wrote the reflection additions very early on so I would need to look at the code, but I am pretty certain that the above would return the name of the property, not the function.
Jazzer:
I think Leigh brings up some important flaws to in the current RFC. What Leigh is asking for does not appear to be possible, and in my opinion, it should be.
I'm really not quite clear on what Leigh was going for with the code she indicated, I think she was trying to demonstrate infinite recursion which is not possible due to guards.
I also agree with Rasmus, to a certain extent.
By putting only a getter/setter, the developer essentially sets the property as read or write only. At first glance, there is no need for the read-only or write-only keyword. Except... subclassing may or may not be an issue.
By setting a property to read-only, it ensures that 'set' can never be set by a subclass. Unless I redefine the entire property... or is that not even possible? The RFC isn't very clear but it appears that there is no way to redefine the entire property, as if you define it in the subclass with only get, then it will take set, isset, and unset from the parent class, correct? Though, that can be outright stopped by making the property final. But then there is no point in having read/write-only.
I tried to be clear about this at the top of the section on read-only and write-only keywords... There are three possible states:
/* read-only by virtue of the fact that there is no setter, any sub-class may define a setter and/or redefine the getter */
public $Hours {
get { ... }
}
/* final, no sub-class may redefine any aspect of this accessor */
public final $Hours {
get { ... }
}
/* Explicitly read-only, sub-classes may redefine the getter but may not define a setter */
public read-only $Hours {
get { ... }
}
As I mentioned earlier, I'm not a fan of the concept of final, read-only or write-only, they were only included because the original RFC indicated that was what the community had decided on.
From what I can tell, read-only becomes useful if I extend the class and want to partially modify the property.
Example:
class A {
public $seconds = 3600;
public $hours {
get() { return $this->seconds / 3600 };
}
}
class B extends A {
public $hours { // Maintains 'get' from class A
set($value) { $this->seconds = $value; }
}
}
^There's no way to stop the developer from doing that without read-only.
That is correct
Also, if the property is public, what if an outside class tries to do this:
class A {
public $seconds = 3600;
public $hours {
get() { return $this->seconds / 3600 };
}
}
$object = new A();
$object->hours = 100;
What happens then?
A fatal error occurs as there is no setter for hours. It is a read only property by virtue of the fact that it has no setter. A sub-class could define a setter for $hours if it so desired (due to the lack of a final or read-only keyword in the declaration).
And similarly, how do we set a public property as a property accessor, hmm?
class A {
public $hours = 1;
}
$seconds = 20;
$object = new A();
$object->hours = { // Does this work?
get() { return $seconds; }
};
I'm not quite sure what you mean here, are you asking if you can dynamically define a property? The answer is no, though I think that would be cool it is likely well beyond my ability to accomplish being that this is my first contribution to the php project.
David:
class MyClass{
public $property = 5 //?can we do this?
{
set($value){
if(!is_int($value)){
throw new InvalidArgumentException('value of wrong type');
}
$this->__property = $value;
}
}
}
Or would that have to happen in the constructor?
class MyClass{
public $property
{
set($value){
if(!is_int($value)){
throw new InvalidArgumentException('value of wrong type');
}
$this->__property = $value;
}
}
public function __construct(){
$this->property = 5;
}
}
I think there is some wide-spread confusion over what an accessor is. An accessor does not store its own value, there is no "memory space" allocated for an accessor unless you use the automatic implementation ( public $h { get; set; } ) but if you're going to do that, you may as well just reduce it to ( private $__h ) which is all the previous "accessor" would have done for you.
Accessors, in my own use, are nearly always used for convenience aliases with conversions ( $Hours calculated based on a real stored $Seconds value) or as an alias to access a value from another class, they are generally not used to simply store or retrieve values, that's what a property is for, an accessor is for executing code with the syntactic sugar of making it seem like it's a variable.
In closing for all of this, I really wish that this type of discourse could have happened long ago when I was requesting it, because many, many hours have been put into the current incarnation. I would like to put together a "vote grid" or something on all that has been discussed here and come to a consensus before I go and re-do any further work. Does the php community have any voting facilities or is it still done via the wiki pages? How should I decide who gets a vote and who doesn't? Should I even be handling the voting myself?
Hi Clint,
In order to achieve read-only and write-only, we could do something
similar to this:
/* Explicitly read-only, sub-classes may redefine the getter but may
not define a setter */
public $Hours {
get() { ... }
final private set() {}
}
This would make the additional keyword superfluous. It's very similar
to what is currently done in Singleton classes that usually have
private constructors.
Second, I'd like to throw in the idea of array accessors. I mentioned
this before, but did not get any response.
public $Addresses {
offsetSet($offset, $value) { ... }
offsetGet() { ... }
offsetUnset($offset) { ... }
offsetExists($offset) { ... }
}
The reasoning for this feature is that in object-oriented design you
have to process logic when establishing bidirectional associations
between objects. For example, adding an Address to a Contact instance
would require Address::$Contact to be set as well if the association
is bidirectional.
public $Addresses {
offsetSet($offset, $address) {
$address->Contact = $this;
}
}
$contact->Addresses[] = new Address();
What do you think about supporting this?
Cheers,
Bernhard
Second, I'd like to throw in the idea of array accessors. I mentioned
this before, but did not get any response.public $Addresses {
offsetSet($offset, $value) { ... }
offsetGet() { ... }
offsetUnset($offset) { ... }
offsetExists($offset) { ... }
}
Definitely on the "nice to have" list.
Here's my feedback on some current outstanding issues/suggestions:
- Default value: I think having functionality for a default value is
necessary, but I'm also thinking it may already be implementable within the
current syntax.
class Test {
private $seconds;
public $hours {
get() {
if(!is_null($this->seconds)) {
return $this->seconds;
} else {
return 10; // 10 is default
}
}
}
}
The above should work fine in many scenarios, right?
We could perhaps then claim that the issue may rather be that we need
access to the variable $hours itself inside of the get/set/etc functions,
which I think has been brought up before - though I'm not so sure how
sensible that is. Whether we need that or not is up in the air.
- read-only and write-only is ugly. While this is a bias, I think we can
do better.
One idea I had is exactly what bernhard suggesting - using final private
set() {} to achieve read-only functionality.
My problem with this implementation is that it's not as logical as I'd
prefer. In my eyes, from a logical point of view, just the fact that set is
there means that it works. Thus it just so happens that, while setting the
variable doesn't error out, it does absolutely nothing.
While I don't see any real world scenarios where people would want this
behavior, I still am wrestling with this proposed implementation, as I
think that, logically speaking, set should work but just not do anything.
Whether or not this is illogical is arguably none of our concern - it's the
coder's concern. Hmm.
I'd definitely like to explore other alternatives to this solution.
Already proposed:
public read-only $property {
get() { ... }
}
public $property {
get() { ... }
final private set() {};
}
I'm going to throw out some alternatives just for the sake of it. They may
be more illogical than I'd prefer, but I'm just trying to get the juices
flowing:
public $property {
get() { ... }
final private set;
}
public $property {
get() { ... }
final private set();
}
public $property {
get_only() { ... } // Same as get but implies read only; set not
allowed to be defined. In case of set_only and get_only, top one has
precedence.
}
public $property {
set(read_only);
get() { ... }
}
public $property {
read_only; // or read_only();
get() { ... }
}
- I'll agree with Leigh here:
I don't like the fact that accessors and their associated properties are
implemented as direct (yet slightly obfuscated/hidden) elements of the
containing class. It's unintuitive (I'd even go as far as saying confusing).
- In regards to array accessors, it'd be nice to have but not at all
necessary.
Second, I'd like to throw in the idea of array accessors. I mentioned
this before, but did not get any response.public $Addresses {
offsetSet($offset, $value) { ... }
offsetGet() { ... }
offsetUnset($offset) { ... }
offsetExists($offset) { ... }
}Definitely on the "nice to have" list.
Here's my feedback on some current outstanding issues/suggestions:
- Default value: I think having functionality for a default value is
necessary, but I'm also thinking it may already be implementable within the
current syntax.class Test {
private $seconds;
public $hours {
get() {
if(!is_null($this->seconds)) {
return $this->seconds;
} else {
return 10; // 10 is default
}
}
}
}The above should work fine in many scenarios, right?
We could perhaps then claim that the issue may rather be that we need
access to the variable $hours itself inside of the get/set/etc functions,
which I think has been brought up before - though I'm not so sure how
sensible that is. Whether we need that or not is up in the air.
I think the syntax for defaults would be:
class UsingInitialValue {
private $seconds = 36000;
public $hours {
get() { return $this->seconds/3600;}
}
}
class UsingLazyLoad {
public $seconds {
get() { return $this->seconds = 36000;}
set($value) {$this->seconds = (int) $value;}
}
}
class UsingConstructor {
public $seconds {
set($value) {$this->seconds = (int) $value;}
}
public function __construct(){
$this->seconds = 36000;
}
}
I think I've got those all right...
Cheers,
David
Second, I'd like to throw in the idea of array accessors. I mentioned
this before, but did not get any response.public $Addresses {
offsetSet($offset, $value) { ... }
offsetGet() { ... }
offsetUnset($offset) { ... }
offsetExists($offset) { ... }
}Definitely on the "nice to have" list.
While I agree it would be a "nice to have" it would also be un-necessary. There are already ways to do precisely what is desired here by way of ArrayAccess.
class Addresses implements ArrayAccess {
offsetSet($offset, $value) { ... }
offsetGet() { ... }
offsetUnset($offset) { ... }
offsetExists($offset) { ... }
}
// In base class where the proposed additional accessor types were suggested
public $Addresses = new Addresses();
Or if you felt like using an accessor to control access to the Addresses object, you could implement that as well, but an accessor wouldn't even be necessary.
This would provide for cleaner object-oriented principles, the logic for dealing with acceptance of a new address stays within a class designed to hold and control access to the collection of addresses.
2012/10/10 Clint Priest cpriest@zerocue.com:
While I agree it would be a "nice to have" it would also be un-necessary. There are already ways to do precisely what is desired here by way of ArrayAccess.
class Addresses implements ArrayAccess {
offsetSet($offset, $value) { ... }
offsetGet() { ... }
offsetUnset($offset) { ... }
offsetExists($offset) { ... }
}
This approach does not work for the use case I presented:
class Addresses implements ArrayAccess {
public function offsetSet($offset, $address) {
$this->_Addresses[] = $address;
$address->Contact = $contact; // where do we get the contact from?
}
}
If we pass $contact to the Addresses instance, the class is bound to
Contact and cannot be used anymore for different associations that
- do not involve Contact
- have a different arity (one-to-many: $address->Contact = $contact,
many-to-many: $address->Contacts[] = $contact)
Consequently, we would need to create a new collection class for each
to-many association, which is neither pragmatic nor good OO design.
Wow, I'm surprised by all the talk about this RFC this time around. I
posted this numerous times in the past trying to elicit feedback and got
little to none, so I took the time to write it as I thought it should be
written. Some of these things will take considerable effort to
fix/correct/change.
Sometimes things get overlooked. Lots of suggestions make their way to
the list, usually by people who have no way to implement the feature
and they disappear as quickly as they are suggested. Be happy you ARE
getting feedback now. The fact people now see this as something that
may be implemented as it is (in a way they don't necessarily like) is
prompting them to speak up about their concerns.
What concerns me with the current implementation is that it leaks many
implementation details, in particular the fact that the accessors are
implemented as real __getXYZ methods and automatic implementations also
use real $__XYZ properties.
I don't particularly see this as a problem (the ability to use it as a
getter or call it as a function), but see my comments at the end. I also
don’t particularly see the point of auto implemented getters/setters (
public $h { get; set; } )
For me the problem is simply that you can call the accessor functions
directly. I don't like it.
I don't like the fact that accessors and their associated properties
are implemented as direct (yet slightly obfuscated/hidden) elements of
the containing class. It's unintuitive (I'd even go as far as saying
confusing)
A property should be a property, not a collection of hidden methods
and variables. If it needs to be seen whether a property implements
accessors, this should be done through reflection.
3 - Can directly access $__automaticProperty and even unset it (causing
notices in the internal code)
I'm not even sure that automatic backing fields are even desired
See above I guess, I agree that these "hidden" properties should not
exist in the first place.
I think it would
be better to cleanly separate out the accessor implementation. It might
require more code now, but will be better in the long run.
All three of these issues, could be addressed I believe by not having the
functions and/or properties a part of the ordinary HashTables, or to have
flags set on them. I believe there is already a SHADOW flag type defined
which may be able to be used for this type of functionality.
Nikita do you have any other proposals for how this should be
addressed? I think most of my concerns with this revolve around the
implementation not being cleanly separated as you put it. I believe
that setters/getters would be a tremendously powerful and useful
addition to the language, just not quite like this, so lets here your
proposal please :)
public $property {
set { $this->property = ($this->property*2)+$value } get; }
How do I reference the property being set from within the function? The
way I have done it in the example will cause recursion? How can I assign to
"self"?
Generally speaking, I don't know why you would use an automatic backing
getter with a separate setter, but if you wanted to do that, at present you
would access the automatic backing field by $this->__property.
Kind of confusing. We have to think of the users who just want to pick
this up and run with it. You can't say "I don't know why you would",
because people will do strange things, and you have to make it so that
the behaviour is predictable, and intuitive.
The above will not cause recursion, it is protected from recursion by the
same mechanism that __get() and __set() are protected.
Good, thanks. I didn't get to test it since I was on a bus at the time :)
In fact, the above
code would set an actual property named $property on the object which would
then side-step the accessor entirely (true properties take precedence over
accessors, though they may only be "set" from within the setter of the
accessor. This may be a bit confusing, but I specifically wrote it this way
for the purpose of lazy-loading.
Yep, confusing!
The first access of the $objList property getter would create the object and
attempt to set its-self which would be passed to the setter, the setter (now
guarded) will directly set the property on the object, further calls to
$objList would retrieve the already created object (bypassing the accessor).
To get out of that situation, you would simply unset($objList) and the
accessor would take over again.
Also confusing. (Think of the users!)
I think Leigh brings up some important flaws to in the current RFC. What
Leigh is asking for does not appear to be possible, and in my opinion, it
should be.
I'm really not quite clear on what Leigh was going for with the code she
indicated, I think she was trying to demonstrate infinite recursion which is
not possible due to guards.
She? Only when I go shoe shopping with Nikita at the weekends...
Actually the recursion thing was just a query I hadn't tested, and you
have answered. The other question I asked was "How do you set the
default value of the property with the accessor syntax?"
I think there is some wide-spread confusion over what an accessor is. An
accessor does not store its own value, there is no “memory space” allocated
for an accessor unless you use the automatic implementation ( public $h {
get; set; } ) but if you’re going to do that, you may as well just reduce it
to ( private $__h ) which is all the previous “accessor” would have done for
you.
Accessors, in my own use, are nearly always used for convenience aliases
with conversions ( $Hours calculated based on a real stored $Seconds value)
or as an alias to access a value from another class, they are generally not
used to simply store or retrieve values, that’s what a property is for, an
accessor is for executing code with the syntactic sugar of making it seem
like it’s a variable.
In my opinion
public $property; // This is a property
public $property { // This is still a property
set { ... }
}
I don't see accessors tied to the property as ways of simply setting
some other value (in your example, setting $seconds via $hours). I
have a feeling a lot of people will not be using these simply for
conversions, they will also be using them for validation and
sanitising. You already saw Niki throwing an Exception inside an
accessor.
try {
$user->username = $rawData
} catch (InvalidUsernameException $e) {
...
}
They will expect the $username property to contain the data, not
$__username, and not having to implement $saneUsername as a second
property to store it in.
What concerns me with the current implementation is that it leaks
many implementation details, in particular the fact that the
accessors are implemented as real __getXYZ methods and automatic
implementations also use real $__XYZ properties.I don't particularly see this as a problem (the ability to use it as a
getter or call it as a function), but see my comments at the end. I
also don't particularly see the point of auto implemented
getters/setters ( public $h { get; set; } )For me the problem is simply that you can call the accessor functions directly. I don't like it.
I don't like the fact that accessors and their associated properties are implemented as direct (yet slightly obfuscated/hidden)
elements of the containing class. It's unintuitive (I'd even go as far as saying
confusing)A property should be a property, not a collection of hidden methods and variables. If it needs to be seen whether a property
implements accessors, this should be done through reflection.
Leigh, a property IS a property, a property accessor IS a series of functions.
3 - Can directly access $__automaticProperty and even unset it
(causing notices in the internal code)
I'm not even sure that automatic backing fields are even desired
See above I guess, I agree that these "hidden" properties should not exist in the first place.
If this automatic implementation { get; set; } is not desired, I'd be happy to rip it out, I never wanted it in the first place. This is the only case in which a "real property" is automatically implemented.
I think it would
be better to cleanly separate out the accessor implementation. It
might require more code now, but will be better in the long run.All three of these issues, could be addressed I believe by not having
the functions and/or properties a part of the ordinary HashTables, or
to have flags set on them. I believe there is already a SHADOW flag
type defined which may be able to be used for this type of functionality.Nikita do you have any other proposals for how this should be addressed? I think most of my concerns with this revolve around the
implementation not being cleanly separated as you put it. I believe that setters/getters would be a tremendously powerful and useful
addition to the language, just not quite like this, so lets here your proposal please :)public $property {
set { $this->property = ($this->property*2)+$value } get; }How do I reference the property being set from within the function?
The way I have done it in the example will cause recursion? How can I
assign to "self"?Generally speaking, I don't know why you would use an automatic
backing getter with a separate setter, but if you wanted to do that,
at present you would access the automatic backing field by $this->__property.Kind of confusing. We have to think of the users who just want to pick this up and run with it. You can't say "I don't know why you
would", because people will do strange things, and you have to make it so that the behaviour is predictable, and intuitive.
I agree completely, what are you trying to achieve because the above code makes it look like you are thinking of accessors as properties, which they are not. Accessors do not have their own "memory space."
The above will not cause recursion, it is protected from recursion by
the same mechanism that __get() and __set() are protected.Good, thanks. I didn't get to test it since I was on a bus at the time :)
In fact, the above
code would set an actual property named $property on the object which
would then side-step the accessor entirely (true properties take
precedence over accessors, though they may only be "set" from within
the setter of the accessor. This may be a bit confusing, but I
specifically wrote it this way for the purpose of lazy-loading.Yep, confusing!
Well it's confusing because you wrote it that way... thinking that somehow $property has its own "memory space" which it does not.
The first access of the $objList property getter would create the
object and attempt to set its-self which would be passed to the
setter, the setter (now
guarded) will directly set the property on the object, further calls
to $objList would retrieve the already created object (bypassing the accessor).
To get out of that situation, you would simply unset($objList) and the
accessor would take over again.Also confusing. (Think of the users!)
This does not need to be used, it's simply a way it can be used. Again, the confusion I think you are running into is that you think that a property accessor has it's own "memory space," which it does not have.
I think Leigh brings up some important flaws to in the current RFC.
What Leigh is asking for does not appear to be possible, and in my
opinion, it should be.I'm really not quite clear on what Leigh was going for with the code
she indicated, I think she was trying to demonstrate infinite
recursion which is not possible due to guards.She? Only when I go shoe shopping with Nikita at the weekends...
Sorry :)
Actually the recursion thing was just a query I hadn't tested, and you have answered. The other question I asked was "How do you set
the default value of the property with the accessor syntax?"I think there is some wide-spread confusion over what an accessor is.
An accessor does not store its own value, there is no "memory space"
allocated for an accessor unless you use the automatic implementation
( public $h { get; set; } ) but if you're going to do that, you may as
well just reduce it to ( private $__h ) which is all the previous
"accessor" would have done for you.Accessors, in my own use, are nearly always used for convenience
aliases with conversions ( $Hours calculated based on a real stored
$Seconds value) or as an alias to access a value from another class,
they are generally not used to simply store or retrieve values, that's
what a property is for, an accessor is for executing code with the
syntactic sugar of making it seem like it's a variable.In my opinion
public $property; // This is a property
public $property { // This is still a property
set { ... }
}
This would be where you are wrong, the 2nd $property above is not a property at all, it's a property accessor, it has no "memory space" the way a regular property does.
I don't see accessors tied to the property as ways of simply setting some other value (in your example, setting $seconds via $hours). I
have a feeling a lot of people will not be using these simply for conversions, they will also be using them for validation and sanitising.
You already saw Niki throwing an Exception inside an accessor.try {
$user->username = $rawData
} catch (InvalidUsernameException $e) {
...
}They will expect the $username property to contain the data, not $__username, and not having to implement $saneUsername as a
second property to store it in.
$__username only occurs if no body is provided for a getter or setter.
It really seems like getting rid of auto-implemented properties is the only way to get through this. I don't want them anyways and it only seems to confuse everyone.
I'm not even sure that automatic backing fields are even desired, I never
felt the need to have them in C# and the only reason they were included is
because they were a part of Dennis's original proposal. Eliminating them
would eliminate this as an issue.
I just did a bit of research regarding this topic and I have found the
following reasons why automatic properties exist in C#:
- Changing a field to a property breaks the binary interface, so all
code using the library has to be recompiled. If you are using an
automatic property you can safely add additional behavior for it
later. - Properties support data binding, whereas fields do not.
- There are attributes that work for properties, but don't work for fields.
- Reflection for fields and properties works differently, so changing
a field to a property is a BC break.
I guess that #1 is the most important one (you don't want to break the
interface) and it obviously does not apply to PHP. Points #2 and #3
also don't apply as PHP has neither data binding not attributes. #4
also doesn't seem to apply because ReflectionPropertyAccessor defines
all the methods that ReflectionProperty defines, so changing
field->property should be okay there too.
From that I would conclude that the automatic properties are really
not needed in PHP. I think they will only cause confusion as to when
one should use public $name;
and when one should use public $name { get; set; };
.
The only thing I'm not sure about is how read-only / write-only
properties rely on automatic properties with your current
implementation. If the automatic properties aren't needed there
either, then I think they can be safely removed.
Nikita
I'm not even sure that automatic backing fields are even desired, I
never felt the need to have them in C# and the only reason they were
included is because they were a part of Dennis's original proposal.
Eliminating them would eliminate this as an issue.I just did a bit of research regarding this topic and I have found the following reasons why automatic properties exist in C#:
- Changing a field to a property breaks the binary interface, so all code using the library has to be recompiled. If you are using an
automatic property you can safely add additional behavior for it later.- Properties support data binding, whereas fields do not.
- There are attributes that work for properties, but don't work for fields.
- Reflection for fields and properties works differently, so changing a field to a property is a BC break.
I guess that #1 is the most important one (you don't want to break the
interface) and it obviously does not apply to PHP. Points #2 and #3 also don't apply as PHP has neither data binding not attributes. #4
also doesn't seem to apply because ReflectionPropertyAccessor defines all the methods that ReflectionProperty defines, so
changing
field->property should be okay there too.From that I would conclude that the automatic properties are really not needed in PHP. I think they will only cause confusion as to
when one should usepublic $name;
and when one should usepublic $name { get; set; };
.The only thing I'm not sure about is how read-only / write-only properties rely on automatic properties with your current
implementation. If the automatic properties aren't needed there either, then I think they can be safely removed.
Nice, I would agree that we should eliminate the automatic get/set since the reason they exist in C# is not an issue in PHP and it has only served to confuse everyone.
With the read-only and write-only, they are separate from automatic getters/setters really. If you define a read-only with {get;} it's the same as any other getter.
It's been a while since I posted any updates about this, a few individuals have been asking about it privately and wanting me to get it out the door for PHP 5.5 release. It's come a long way since the last time I posted about it.
RFC Document: https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented
About the items in regards to reflection in there:
The RFC states
ReflectionClass::getMethods() will not return accessor functions
(hides implementation detail).
Up until now reflection is leaky and is telling the truth. We should
either keep that or completely clean up reflection. (mind also
get_class_methods()
and such)
The RFC also introduces a new class ReflectionPropertyAccessor and has
methods returning ReflectionProperty and ReflectionPropertyAccessor.
there should either be a common base class or interface (see i.e.
ReflectionFunctionAbstract)
What will ReflectionPropertyAccessor->getGetter()->getName() return? I
guess reflection will be leaky there? (see first item)
johannes
Hi!
The RFC states
ReflectionClass::getMethods() will not return accessor functions
(hides implementation detail).
Up until now reflection is leaky and is telling the truth. We should
either keep that or completely clean up reflection. (mind also
get_class_methods()
and such)
I think the reflection should return all methods that exist. If the
accessors are implemented as PHP methods/functions (and I see no reason
why not) then reflection should return it. Reflection, as you pointed
out, should tell the truth. There's nothing "leaky" about it, IMO - yes,
it's an implementation detail, so what? If you don't want to use
implementation details, don't - just ignore all __ functions and don't
call them. Python, for example, has tons of __ functions, and people
live just fine with it.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227