FAQ
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.

1. 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*.

2. 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.

3. 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

Search Discussions

  • Ralph Schindler at Jan 23, 2013 at 8:00 pm
    Hi Steve,
    2. 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.
    I tend to agree here- I don't see a ton of value in isset/unset accessor
    functions.
    3. 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
  • Nathan at Jan 23, 2013 at 8:43 pm

    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)
  • Clint Priest at Jan 23, 2013 at 8:54 pm

    On Jan 23, 2013, at 1:23 PM, Steve Clay wrote:

    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.

    1. 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*.

    2. 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.

    3. 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.
  • Crypto Compress at Jan 23, 2013 at 9:08 pm
    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
  • Clint Priest at Jan 23, 2013 at 9:13 pm

    On 1/23/2013 3:07 PM, Crypto Compress wrote:
    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
  • Crypto Compress at Jan 23, 2013 at 10:27 pm

    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
  • Levi Morrison at Jan 23, 2013 at 9:17 pm
    Steve:

    Like your summary. Disagree with a few points but in general I agree
    with you. More below.
    2. 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.
    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.
  • Anthony Ferrara at Jan 23, 2013 at 9:33 pm
    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
  • Crypto Compress at Jan 23, 2013 at 9:53 pm
    Hello Levi,
    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.
    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 `?` for `nullable`. 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
  • Levi Morrison at Jan 24, 2013 at 1:12 am

    I also don't like the `?` for `nullable`. 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.
    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.
  • Crypto Compress at Jan 24, 2013 at 1:46 pm

    Am 24.01.2013 02:12, schrieb Levi Morrison:
    I also don't like the `?` for `nullable`. 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.
    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 Priest at Jan 23, 2013 at 10:35 pm

    On 1/23/2013 3:17 PM, Levi Morrison wrote:
    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
  • Rasmus Lerdorf at Jan 23, 2013 at 10:40 pm

    On 01/23/2013 02:35 PM, Clint Priest wrote:
    On 1/23/2013 3:17 PM, Levi Morrison wrote:
    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
  • Clint Priest at Jan 23, 2013 at 10:43 pm

    On 1/23/2013 4:40 PM, Rasmus Lerdorf wrote:
    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
  • Crypto Compress at Jan 23, 2013 at 11:06 pm
    Hello!

    1) not able to vote
    2) looked at patch
    3) 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
  • Clint Priest at Jan 23, 2013 at 11:45 pm

    On 1/23/2013 5:04 PM, Crypto Compress wrote:
    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
  • Crypto Compress at Jan 24, 2013 at 12:13 am
    Thank you! Will look at it tomorrow.

    Am 24.01.2013 00:45, schrieb Clint Priest:
    On 1/23/2013 5:04 PM, Crypto Compress wrote:
    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
  • Arpad Ray at Jan 24, 2013 at 2:36 am

    On Wed, Jan 23, 2013 at 10:35 PM, Clint Priest wrote:
    On 1/23/2013 3:17 PM, Levi Morrison wrote:
    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

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupphp-internals @
categoriesphp
postedJan 23, '13 at 7:23p
activeJan 24, '13 at 1:46p
posts19
users9
websitephp.net

People

Translate

site design / logo © 2019 Grokbase