First of all, I think the functionality provided by Clint and Nikita's RFC [1] is in
demand and would be an asset to PHP, but I also think it can repackaged more simply.
People are comparing the RFC to C# [2], but while they look similar, C# has a simpler
implementation we should learn from.
- C# draws a firm line between property and field, keeping both concepts simple.
A "field" is like a PHP property. There's no more to understand.
A "property" is simply a set of functions emulating a field, and $value is available in
the setter body. There's no more to understand.
Importantly, I think, the property takes the place of a field; there's never a "shadow"
field of the same name to think about. The obvious downside is you must use a
distinctly-named field for storage, but the upside is conceptual and behavioral
simplicity. There's no need for "guarding" mechanisms and rules about where "$this->prop"
will mean field access or trigger getter/setter; we know there is no field "prop", period.
As Sharif Ramadan pointed out, it also makes state snapshots clearer as there is no value
stored under "prop" to potentially confuse. Under the RFC, every property would show up as
a field even if the accessors didn't use that field.
- C# has no issetter/unsetter.
IMO customizing these functions is completely unneeded for the vast majority of use cases
and could be replaced by simpler logic: isset($this->prop) calls the getter and compares
to NULL; unset($this->prop) passes NULL
to the setter.
- C# has no auto-implementations.
IMO auto implementations have some value removing boilerplate, but we should not make them
violate #1. We could have the property syntax specify what field to use for underlying
storage, or simply conclude that the boilerplate is worth the clarity.
I think the path forward is to determine how we can serve the same goals as this RFC, but
retain the conceptual simplicity of the C# implementation and maybe syntax that strays
less from current PHP.
I have some ideas that I could start forging into an RFC. Consider this:
class Foo {
private $_bar;
public function get bar { return $this->_bar; }
public function set bar { $this->_bar = $value; }
}
Advantages I see over the RFC:
- It's clearer that the accessors map to regular methods, and you know how to control
visibility of methods and how they're inherited. - It's clearer $bar doesn't exist as a property, and it will never show up in state
inspection. - You know $_bar is a plain PHP property, and you can initialize it directly in the
constructor; we don't need an "initter". - There are no guards/proxies/shadow property to think about
As for type-hinting, I think that could be achieved by a separate, simpler mechanism:
type-hinting the properties.
class Foo {
private Bar? $_bar;
}
$_bar is a regular property, but which can only be set to a Bar or to null ("?" implies
nullable). That gives you simple typed properties (useful and simpler to understand
without accessors), but also suggests how to combine these to get "typed accessors":
class Foo {
// settable anywhere
public Bar $bar;
// "read only"
protected Baz? $_baz;
public function get baz { return $this->_baz; }
}
Down the road we could further address how to shorten this syntax but while keeping it
clear that accessors are just functions and properties are just value stores.
[1] https://wiki.php.net/rfc/propertygetsetsyntax-v1.2
[2] http://msdn.microsoft.com/en-us/library/x9fsa0sw(v=vs.80).aspx
Steve Clay
Hi Steve,
- C# has no issetter/unsetter.
IMO customizing these functions is completely unneeded for the vast
majority of use cases and could be replaced by simpler logic:
isset($this->prop) calls the getter and compares to NULL;
unset($this->prop) passesNULL
to the setter.
I tend to agree here- I don't see a ton of value in isset/unset accessor
functions.
- C# has no auto-implementations.
IMO auto implementations have some value removing boilerplate, but we
should not make them violate #1. We could have the property syntax
specify what field to use for underlying storage, or simply conclude
that the boilerplate is worth the clarity.
Not completely true:
http://msdn.microsoft.com/en-us/library/bb384054.aspx
I think the path forward is to determine how we can serve the same goals
as this RFC, but retain the conceptual simplicity of the C#
implementation and maybe syntax that strays less from current PHP.I have some ideas that I could start forging into an RFC. Consider this:
class Foo {
private $_bar;
public function get bar { return $this->_bar; }
public function set bar { $this->_bar = $value; }
}
This is too similar to what we have today:
class Foo {
private $_bar;
public function getBar() { return $this->_bar; }
public function setBar($value) { $this->_bar = $value; }
}
Advantages I see over the RFC:
- It's clearer that the accessors map to regular methods, and you know
how to control visibility of methods and how they're inherited.- It's clearer $bar doesn't exist as a property, and it will never show
up in state inspection.- You know $_bar is a plain PHP property, and you can initialize it
directly in the constructor; we don't need an "initter".- There are no guards/proxies/shadow property to think about
Personally, I don't see why 'default' can't be used:
class Foo {
public $bar { get; set; default 5; }
}
This solves the var_dump()
problem, and if people want dynamic get
returning something other than the property/field value, so be it.
C# does indeed have an internal field per property though, even if it is
an anonymous backing field.
-ralph
Personally, I don't see why 'default' can't be used:
class Foo {
public $bar { get; set; default 5; } }This solves the
var_dump()
problem, and if people want dynamic get
returning something other than the property/field value, so be it.
C# does indeed have an internal field per property though, even if it is an
anonymous backing field.
Is there a reason we cannot just have var_dump/print_r show it more like
this?
object(Time)#1 (2) {
["seconds"]=>
int(16200)
["hours"]=>
NULL
["hours:getter"]=>
float(4.5)
}
(Although I am against the idea of having a hidden value accessible inside
the getter/setter)
First of all, I think the functionality provided by Clint and Nikita's RFC [1] is in demand and would be an asset to PHP, but I also think it can repackaged more simply.
People are comparing the RFC to C# [2], but while they look similar, C# has a simpler implementation we should learn from.
- C# draws a firm line between property and field, keeping both concepts simple.
A "field" is like a PHP property. There's no more to understand.
A "property" is simply a set of functions emulating a field, and $value is available in the setter body. There's no more to understand.
Importantly, I think, the property takes the place of a field; there's never a "shadow" field of the same name to think about. The obvious downside is you must use a distinctly-named field for storage, but the upside is conceptual and behavioral simplicity. There's no need for "guarding" mechanisms and rules about where "$this->prop" will mean field access or trigger getter/setter; we know there is no field "prop", period.
As Sharif Ramadan pointed out, it also makes state snapshots clearer as there is no value stored under "prop" to potentially confuse. Under the RFC, every property would show up as a field even if the accessors didn't use that field.
- C# has no issetter/unsetter.
IMO customizing these functions is completely unneeded for the vast majority of use cases and could be replaced by simpler logic: isset($this->prop) calls the getter and compares to NULL; unset($this->prop) passes
NULL
to the setter.
- C# has no auto-implementations.
IMO auto implementations have some value removing boilerplate, but we should not make them violate #1. We could have the property syntax specify what field to use for underlying storage, or simply conclude that the boilerplate is worth the clarity.
I think the path forward is to determine how we can serve the same goals as this RFC, but retain the conceptual simplicity of the C# implementation and maybe syntax that strays less from current PHP.
I have some ideas that I could start forging into an RFC. Consider this:
class Foo {
private $_bar;
public function get bar { return $this->_bar; }
public function set bar { $this->_bar = $value; }
}Advantages I see over the RFC:
- It's clearer that the accessors map to regular methods, and you know how to control visibility of methods and how they're inherited.
- It's clearer $bar doesn't exist as a property, and it will never show up in state inspection.
- You know $_bar is a plain PHP property, and you can initialize it directly in the constructor; we don't need an "initter".
- There are no guards/proxies/shadow property to think about
As for type-hinting, I think that could be achieved by a separate, simpler mechanism: type-hinting the properties.
class Foo {
private Bar? $_bar;
}$_bar is a regular property, but which can only be set to a Bar or to null ("?" implies nullable). That gives you simple typed properties (useful and simpler to understand without accessors), but also suggests how to combine these to get "typed accessors":
class Foo {
// settable anywhere
public Bar $bar;// "read only"
protected Baz? $_baz;
public function get baz { return $this->_baz; }
}Down the road we could further address how to shorten this syntax but while keeping it clear that accessors are just functions and properties are just value stores.
I'd just like to point out the fact that RFC v1.1 from a year ago was exactly as above but people wanted all of these other features. They were not a property, they had no "guarding", no unset, isset, etc.
The original RFC that was exactly as c# had it, nobody liked it.
It was changed to its current incarnation because it now mimics exactly what everyone is use to with __get(), etc.
[1] https://wiki.php.net/rfc/propertygetsetsyntax-v1.2
[2] http://msdn.microsoft.com/en-us/library/x9fsa0sw(v=vs.80).aspxSteve Clay
I'd just like to point out the fact that RFC v1.1 from a year ago was
exactly as above but people wanted all of these other features. They
were not a property, they had no "guarding", no unset, isset, etc. The
original RFC that was exactly as c# had it, nobody liked it. It was
changed to its current incarnation because it now mimics exactly what
everyone is use to with __get(), etc.
i think you refer to RFC 0.3 (could not find C# in 1.1):
https://wiki.php.net/rfc/propertygetsetsyntax#properties_in_c
cryptocompress
I'd just like to point out the fact that RFC v1.1 from a year ago
was exactly as above but people wanted all of these other features.
They were not a property, they had no "guarding", no unset, isset,
etc. The original RFC that was exactly as c# had it, nobody liked it.
It was changed to its current incarnation because it now mimics
exactly what everyone is use to with __get(), etc.i think you refer to RFC 0.3 (could not find C# in 1.1):
https://wiki.php.net/rfc/propertygetsetsyntax#properties_in_c
Well actually you're right. .3 was written by Dennis long ago and I
wrote accessors to be exactly as that document described except where
there was ambiguity. The 1.1 document had already deviated from his
original RFC by way of discussion but I had not been tracking them as
separate documents.
If you go back in history on
https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented you will
see a point where it matches the original spec more or less completely.
cryptocompress
--
-Clint
Well actually you're right. .3 was written by Dennis long ago and I
wrote accessors to be exactly as that document described except where
there was ambiguity.
@Steve Clay: this would be a perfect point to start with
Steve:
Like your summary. Disagree with a few points but in general I agree
with you. More below.
- C# has no issetter/unsetter.
IMO customizing these functions is completely unneeded for the vast majority
of use cases and could be replaced by simpler logic: isset($this->prop)
calls the getter and compares to NULL; unset($this->prop) passesNULL
to the
setter.
Agreed, but if they are automatically generated then I see no harm in
allow custom isset
and unset
behavior as long as it doesn't get in
the way or complicate things.
I think the path forward is to determine how we can serve the same goals as
this RFC, but retain the conceptual simplicity of the C# implementation and
maybe syntax that strays less from current PHP.I have some ideas that I could start forging into an RFC. Consider this:
class Foo {
private $_bar;
public function get bar { return $this->_bar; }
public function set bar { $this->_bar = $value; }
}Advantages I see over the RFC:
- It's clearer that the accessors map to regular methods, and you know how
to control visibility of methods and how they're inherited.- It's clearer $bar doesn't exist as a property, and it will never show up
in state inspection.- You know $_bar is a plain PHP property, and you can initialize it directly
in the constructor; we don't need an "initter".- There are no guards/proxies/shadow property to think about
Keyword 'function' is unnecessary and please don't use a magic
$value
. Just do a normal parenthesis:
class Foo {
private $_bar;
public get bar() { return $this->_bar; }
public set bar($value) { $this->_bar = $value; }
}
This allows for a function-like feel and type-hints as well. Which
brings me to the shorthand notation:
As for type-hinting, I think that could be achieved by a separate, simpler
mechanism: type-hinting the properties.class Foo {
private Bar? $_bar;
}
I like type-hinting properties as a shorter syntax, but now we have a
private
method that has public getters/setters? Seems odd at first.
In any case it is confusing.
I also don't like the ?
for nullable
. Just stick with PHP
convention and do:
class Foo {
public Bar $bar = NULL;
}
Ralph:
This is too similar to what we have today:
class Foo {
private $_bar;
public function getBar() { return $this->_bar; }
public function setBar($value) { $this->_bar = $value; }}
Is there anything inherently wrong with that method? And the fact
that it is close is a GOOD indication, right? It's familiar and easy
to adopt. In fact, if we did accessors annotations style (which Python
has, so don't knock it blindly):
class Foo {
private $bar;
@bar.getter
public function getBar() { return $this->bar; }
@bar.setter
public function setBar($value) { $this->bar = $value; }
}
Then I'd say that this is perfect style. All we add is some
annotations to allow it to be used in getter and setter syntax:
$foo = new Foo;
$foo->bar = 2; //Foo::setBar
$bar = $foo->bar; //Foo::getBar
I can name my getters and setters however I want. Talk about about options!
Note: I'm not saying we should use annotations for this; I'm just
saying it makes certain cases nicer. I'm sure we can find drawbacks as
well. Discuss at will.
Clint:
I'm sorry that you spent all that time without hearing feedback from a
lot of the "No" voters. Had they been participating all along perhaps
it could have been avoided. We'll never know.
Levi, et al.
class Foo {
private $_bar;
public function get bar { return $this->_bar; }
public function set bar { $this->_bar = $value; }
}
class Foo {
private $_bar;
public get bar() { return $this->_bar; }
public set bar($value) { $this->_bar = $value; }
}class Foo {
private $bar;@bar.getter
public function getBar() { return $this->bar; }@bar.setter
public function setBar($value) { $this->bar = $value; }}
My main issue with all of these is cognitive distance. When I want to look
for a property, I look at the properties defined by a class. The currently
proposed syntax, while perhaps slightly less elegant, has very low
cognitive distance from the base property. I look for a property, and if I
see a getter attached, I know it's there.
With all three of the above, it requires me to shift from property to
property or function with annotation. While this distance isn't gigantic,
it is non-trivial. It requires more than a simple glance to figure out
what's going on. The annotation syntax is especially bad in this regard.
However, it's not a show stopper of an issue.
I do like the certain elegance of the getter annotation, but I wonder what
the benefits really are. It's something perhaps worth exploring (if the
current trend towards rejecting the current RFC continues), but I'm not
sold on it in the least... It feels a bit too disconnected from the concept
of a property...
Anthony
Hello Levi,
Agreed, but if they are automatically generated then I see no harm in
allow customisset
andunset
behavior as long as it doesn't get in
the way or complicate things.
If override of isset/unset is possible, we will end up debugging:
true === isset($this->someUninitializedValue)
or
unset($this->memoryConsuming); // with no effect
What is the benefit of this?
I also don't like the
?
fornullable
. Just stick with PHP
convention and do:class Foo { public Bar $bar = NULL; }
There is no such PHP convention. The PHP convention is not restrict
type (+"loosely typed" addons).
So NULL
is automatically allowed.
cryptocompress
I also don't like the
?
fornullable
. Just stick with PHP
convention and do:class Foo { public Bar $bar = NULL; }
There is no such PHP convention. The PHP convention is not restrict type
(+"loosely typed" addons).
SoNULL
is automatically allowed.
For properties, yes, but the idea it stems from is type-hints. Given
the following type-hint, passing a null is not allowed:
class Foo {
function bar(Bar $bar) {}
}
Whereas in the following NULL
is allowed:
class Foo {
function bar(Bar $bar = NULL) {}
}
This is what I mean by the PHP convention for allowing NULL.
Am 24.01.2013 02:12, schrieb Levi Morrison:
I also don't like the
?
fornullable
. Just stick with PHP
convention and do:class Foo { public Bar $bar = NULL; }
There is no such PHP convention. The PHP convention is not restrict
type
(+"loosely typed" addons).
SoNULL
is automatically allowed.
For properties, yes, but the idea it stems from is type-hints. Given
the following type-hint, passing a null is not allowed:class Foo { function bar(Bar $bar) {} }
Whereas in the following
NULL
is allowed:class Foo { function bar(Bar $bar = NULL) {} }
This is what I mean by the PHP convention for allowing NULL.
Hello Levi,
you absolutly right. I was confused by the syntax. This properties are
normal
methods and should behave the same. Mixing "initialize-with-NULL" and
"optional/nullable" is somewhat messy though.
cryptocompress
Clint: I'm sorry that you spent all that time without hearing feedback
from a lot of the "No" voters. Had they been participating all along
perhaps it could have been avoided. We'll never know.
I appreciate that, I'm hearing through the grapevine that some of the
"no" voters haven't even looked at the patch.
--
-Clint
Clint: I'm sorry that you spent all that time without hearing feedback
from a lot of the "No" voters. Had they been participating all along
perhaps it could have been avoided. We'll never know.I appreciate that, I'm hearing through the grapevine that some of the
"no" voters haven't even looked at the patch.
Just to be fair, I suspect quite a few "yes" voters haven't looked at,
nor understood the patch.
-Rasmus
I appreciate that, I'm hearing through the grapevine that some of the
"no" voters haven't even looked at the patch.
Just to be fair, I suspect quite a few "yes" voters haven't looked at,
nor understood the patch.-Rasmus
I'd bet good money that's true too, neither of which is a good thing.
--
-Clint
Hello!
- not able to vote
- looked at patch
- do not understand the patch
so i have a question regarding guards... e.g.:
guard->in_unset = 1; /* Prevent recursion /
zend_call_method_with_1_params(&object, zobj->ce, &zobj->ce->__unset,
ZEND_UNSET_FUNC_NAME, NULL, member);
guard->in_unset = 0; / Prevent recursion */
this code would only prevent nested/parallel access to same accessor but
not circular?
e.g.: getter -> setter -> null-checker -> null-setter -> getter
sorry it this is a dumb question :)
cryptocompress
guard->in_unset = 1; /* Prevent recursion /
zend_call_method_with_1_params(&object, zobj->ce, &zobj->ce->__unset,
ZEND_UNSET_FUNC_NAME, NULL, member);
guard->in_unset = 0; / Prevent recursion */
a) That applies to __unset (magic method) only
b) guard is name specific (another name would have a different guard)
c) Those three lines are not new code, they have just been moved around
a bit.
The code (for unset accessors) you are looking for is here:
https://github.com/cpriest/php-src/blob/accessors-5.5/Zend/zend_object_handlers.c#L986
this code would only prevent nested/parallel access to same accessor
but not circular?
e.g.: getter -> setter -> null-checker -> null-setter -> getter
sorry it this is a dumb question :)cryptocompress
--
-Clint
Thank you! Will look at it tomorrow.
Am 24.01.2013 00:45, schrieb Clint Priest:
guard->in_unset = 1; /* Prevent recursion /
zend_call_method_with_1_params(&object, zobj->ce, &zobj->ce->__unset,
ZEND_UNSET_FUNC_NAME, NULL, member);
guard->in_unset = 0; / Prevent recursion */a) That applies to __unset (magic method) only
b) guard is name specific (another name would have a different guard)
c) Those three lines are not new code, they have just been moved
around a bit.The code (for unset accessors) you are looking for is here:
https://github.com/cpriest/php-src/blob/accessors-5.5/Zend/zend_object_handlers.c#L986this code would only prevent nested/parallel access to same accessor
but not circular?
e.g.: getter -> setter -> null-checker -> null-setter -> getter
sorry it this is a dumb question :)cryptocompress
Clint: I'm sorry that you spent all that time without hearing feedback
from a lot of the "No" voters. Had they been participating all along
perhaps it could have been avoided. We'll never know.I appreciate that, I'm hearing through the grapevine that some of the "no"
voters haven't even looked at the patch.
Hi,
I voted "no" and I haven't looked at the patch, because my interest in
it doesn't extend that far.
I simply don't like the idea - it adds nothing to the language which
can't already reasonably be done, and for what it does, I find the
code harder to follow.
I'd personally much rather have $foo->setBar(42); than $foo->bar = 42;
doing something magic, even if the accessor would save a few lines of
code.
Even if I agreed with the idea, I think the "show stopper" of the
"parent::$foo" issue is still present. Forcing people to use
reflection to get a parent property is an absurd (albeit nifty)
workaround. People complain about the lack of consistency in PHP
enough already. I also think that in the RFC, it's a bit disingenuous
to only mention this limitation in a comment on the 38th line of a
code example
Regards,
Arpad