# C# properties vs. accessors RFC

- Thread: https://externals.io/message/65120
- Original archive: https://news-web.php.net/php.internals/65120
- Started by Steve Clay on 2013-01-23T19:23:53+00:00
- Messages: 19
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #65120 — Steve Clay — 2013-01-23T19:23:53+00:00

URL: https://externals.io/message/65120#65120

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
-- 
http://www.mrclay.org/

---

## #65121 — Ralph Schindler — 2013-01-23T20:00:22+00:00

URL: https://externals.io/message/65120#65121 · In reply to: #65120

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

---

## #65122 — nathan@starin.biz — 2013-01-23T20:43:56+00:00

URL: https://externals.io/message/65120#65122 · In reply to: #65121

>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)

---

## #65123 — Clint Priest — 2013-01-23T20:54:09+00:00

URL: https://externals.io/message/65120#65123 · In reply to: #65120

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

> 
> [1] https://wiki.php.net/rfc/propertygetsetsyntax-v1.2
> [2] http://msdn.microsoft.com/en-us/library/x9fsa0sw(v=vs.80).aspx
> 
> Steve Clay
> -- 
> http://www.mrclay.org/

---

## #65125 — Crypto Compress — 2013-01-23T21:07:20+00:00

URL: https://externals.io/message/65120#65125 · In reply to: #65123

> 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

---

## #65126 — Clint Priest — 2013-01-23T21:13:25+00:00

URL: https://externals.io/message/65120#65126 · In reply to: #65125

> > […]
> 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

---

## #65127 — Levi Morrison — 2013-01-23T21:17:24+00:00

URL: https://externals.io/message/65120#65127 · In reply to: #65123

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.

---

## #65129 — Anthony Ferrara — 2013-01-23T21:33:36+00:00

URL: https://externals.io/message/65120#65129 · In reply to: #65127

Levi, et al.

> class Foo {
> > […]
>     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

---

## #65130 — Crypto Compress — 2013-01-23T21:51:59+00:00

URL: https://externals.io/message/65120#65130 · In reply to: #65127

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

---

## #65131 — Crypto Compress — 2013-01-23T22:25:31+00:00

URL: https://externals.io/message/65120#65131 · In reply to: #65126

> 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

---

## #65132 — Clint Priest — 2013-01-23T22:35:36+00:00

URL: https://externals.io/message/65120#65132 · In reply to: #65127

> 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

---

## #65133 — Rasmus Lerdorf — 2013-01-23T22:40:50+00:00

URL: https://externals.io/message/65120#65133 · In reply to: #65132

> 
> 
> > […]
> 
> 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

---

## #65134 — Clint Priest — 2013-01-23T22:42:34+00:00

URL: https://externals.io/message/65120#65134 · In reply to: #65133

> > […]
> 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

---

## #65135 — Crypto Compress — 2013-01-23T23:04:36+00:00

URL: https://externals.io/message/65120#65135 · In reply to: #65134

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

---

## #65136 — Clint Priest — 2013-01-23T23:45:34+00:00

URL: https://externals.io/message/65120#65136 · In reply to: #65135

> 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

---

## #65137 — Crypto Compress — 2013-01-24T00:12:01+00:00

URL: https://externals.io/message/65120#65137 · In reply to: #65136

Thank you! Will look at it tomorrow.

Am 24.01.2013 00:45, schrieb Clint Priest:
> 
> > […]
> 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 
>
>
> > […]

---

## #65138 — Levi Morrison — 2013-01-24T01:12:54+00:00

URL: https://externals.io/message/65120#65138 · In reply to: #65130

> > […]
>
>
> 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.

---

## #65140 — Arpad Ray — 2013-01-24T02:36:00+00:00

URL: https://externals.io/message/65120#65140 · In reply to: #65132

> 
> > […]
>
> 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

---

## #65147 — Crypto Compress — 2013-01-24T13:45:06+00:00

URL: https://externals.io/message/65120#65147 · In reply to: #65138

Am 24.01.2013 02:12, schrieb Levi Morrison:
> > […]
> 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
