# [PHP-RFC] Property Accessors 1.2 for Final Review before Vote

- Thread: https://externals.io/message/64469
- Original archive: https://news-web.php.net/php.internals/64469
- Started by Clint Priest on 2013-01-02T11:36:22+00:00
- Messages: 52
- RFC: Property Accessors Syntax (Declined) — https://externals.io/rfc/propertygetsetsyntax-v1.2.md
- Page 1 of 3: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/64469.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #64469 — Clint Priest — 2013-01-02T11:36:22+00:00

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

Here is the updated RFC incorporating the feedback from previous rounds 
of discussion.

https://wiki.php.net/rfc/propertygetsetsyntax-v1.2

I'm posting it for final review so I can move to voting on Jan 7th.

Please note that the current fork is not quite up-to-date with the RFC 
but will be within a few more days.

-Clint

-- 
-Clint

---

## #64478 — Levi Morrison — 2013-01-02T16:43:35+00:00

URL: https://externals.io/message/64469#64478 · In reply to: #64469

As is customary for me, I am voicing my opinion against this proposal.
 I do not like the proposed syntax; it is cumbersome and unfamiliar to
current PHP style. I would opt for something more in line with current
method definitions.

I do think that PHP needs something like this proposal, but I dislike
the details of this one. I will vote no on January 7th.

---

## #64479 — Steve Clay — 2013-01-02T17:08:13+00:00

URL: https://externals.io/message/64469#64479 · In reply to: #64469

> Here is the updated RFC incorporating the feedback from previous rounds of discussion.
>
> https://wiki.php.net/rfc/propertygetsetsyntax-v1.2

A few remaining questions. The RFC makes it clear that ReflectionClass::getMethods() does 
not return internal method names like __setSeconds.

1. Are these names visible via `get_class_methods()` / `method_exists()` / `is_callable()`?

2. Inside an accessor, what do __FUNCTION__ and __METHOD__ evaluate as?

3. What happens if a class/subclass contains a regular method __setSeconds?

Steve Clay
-- 
http://www.mrclay.org/

---

## #64481 — Steve Clay — 2013-01-02T17:41:02+00:00

URL: https://externals.io/message/64469#64481 · In reply to: #64469

> Here is the updated RFC incorporating the feedback from previous rounds of discussion.
>
> https://wiki.php.net/rfc/propertygetsetsyntax-v1.2

The RFC does not specify whether it's a fatal error to define a class (directly or via 
extends/traits) which has both a traditional property and accessor with the same name, but 
I think this should be prohibited to avoid confusion.

One might expect this to work if the traditional property is private in a parent class, 
but I think even if the patch allowed that special case (I've not tried it), it should not.

Steve Clay
-- 
http://www.mrclay.org/

---

## #64483 — Levi Morrison — 2013-01-02T18:34:42+00:00

URL: https://externals.io/message/64469#64483 · In reply to: #64481

> 
> > […]
>
>
> The RFC does not specify whether it's a fatal error to define a class
> (directly or via extends/traits) which has both a traditional property and
> accessor with the same name, but I think this should be prohibited to avoid
> confusion.

I think it is explained in the RFC:
https://wiki.php.net/rfc/propertygetsetsyntax-v1.2#shadowing but the
code example doesn't reflect that. Perhaps my understanding is flawed.

---

## #64484 — Levi Morrison — 2013-01-02T18:37:41+00:00

URL: https://externals.io/message/64469#64484 · In reply to: #64469

Also, I was under the impression that we wanted to remove the magic
`$value` for the setter.  It seems the RFC intentionally left it in
there. Any real reason why?

---

## #64485 — Philip Graham — 2013-01-02T18:44:55+00:00

URL: https://externals.io/message/64469#64485 · In reply to: #64469

I am confused by one thing about the RFC.  There is a section for default
accessor implementations where you specify an accessor without a body,
however many of the examples omit the isset and unset accessors.  I would
assuming that omitting an accessor would provide the automagic
implementation.  If this is the case what is the need for the special
syntax?  If this is not the case then what is the effect of omitting an
accessor?

I do see that omitting the setter creates a read-only property, however I
think the syntax would be less ambiguous and easier to use by introducing a
`readonly` keyword:

    class MyClass
    {
        public readonly $myProp {
            // ...
        }
    }

This would also eliminate the need for additional syntax for default
accessors.  There is one problem I see with this however, what happens when
a setter is provided for a readonly property?

If this has already been discussed, please accept my apologies and maybe
provide a link to the discussion.

Regards,
Philip



> Here is the updated RFC incorporating the feedback from previous rounds of
> discussion.
>
> https://wiki.php.net/rfc/**propertynot getsetsyntax-v1.2<https://wiki.php.net/rfc/propertygetsetsyntax-v1.2>
>
> I'm posting it for final review so I can move to voting on Jan 7th.
>
> Please note that the current fork is not quite up-to-date with the RFC but
> will be within a few more days.
>
> -Clint
>
> --
> -Clint

---

## #64486 — Clint Priest — 2013-01-02T21:08:41+00:00

URL: https://externals.io/message/64469#64486 · In reply to: #64479

> A few remaining questions. The RFC makes it clear that 
> ReflectionClass::getMethods() does not return internal method names 
> like __setSeconds.
> 1. Are these names visible via `get_class_methods()` / `method_exists()` / 
> `is_callable()`?
This is the only remaining point of contention but I would expect 
however it is resolved, all methods of reflection would match.
> 2. Inside an accessor, what do __FUNCTION__ and __METHOD__ evaluate as?
I would have to test them but they are methods so they should evaluate 
as you would expect, I'll test.
> 3. What happens if a class/subclass contains a regular method 
> __setSeconds?
Same thing as a duplicate function declaration error, since that's what 
it is, remember that the prefix __ is reserved for php internal use, if 
I recall correctly userland code should not be using a double underscore.
> Steve Clay


> The RFC does not specify whether it's a fatal error to define a class 
> (directly or via extends/traits) which has both a traditional property 
> and accessor with the same name, but I think this should be prohibited 
> to avoid confusion.
>
> One might expect this to work if the traditional property is private 
> in a parent class, but I think even if the patch allowed that special 
> case (I've not tried it), it should not.
As of the current fork there is no difference between a property and a 
property with accessors except that a property with accessors will 
always route through the accessor methods.  In other words a 
property_info structure is created for either type.

Ergo, the same rules apply, a second declaration of a property with the 
same name will cause a compilation error.
-- 
-Clint

---

## #64487 — Clint Priest — 2013-01-02T21:18:54+00:00

URL: https://externals.io/message/64469#64487 · In reply to: #64485

> I am confused by one thing about the RFC.  There is a section for default
> accessor implementations where you specify an accessor without a body,
> however many of the examples omit the isset and unset accessors.  I would
> assuming that omitting an accessor would provide the automagic
> implementation.  If this is the case what is the need for the special
> syntax?  If this is not the case then what is the effect of omitting an
> accessor?
Omitting get/set declaration (and body) makes the property read only or 
write only.

Omitting isset/unset has the same effect as declaring it without a 
body.  This is described in the RFC under Automatic Implementations with 
this line:

Note that isset/unset implementations will always be provided if they 
are not defined or if they are explicitly auto-defined (as above).

>
> I do see that omitting the setter creates a read-only property, however I
> think the syntax would be less ambiguous and easier to use by introducing a
> `readonly` keyword:
>
>      class MyClass
>      {
>          public readonly $myProp {
>              // ...
>          }
>      }
>
> This would also eliminate the need for additional syntax for default
> accessors.  There is one problem I see with this however, what happens when
> a setter is provided for a readonly property?
>
> If this has already been discussed, please accept my apologies and maybe
> provide a link to the discussion.
This point of contention was discussed ad nauseam and nobody wanted the 
read-only/write-only keywords, they were removed from 1.1 -> 1.2

Please see this document: 
https://wiki.php.net/rfc/propertygetsetsyntax-as-implemented/change-requests

Which documents all points of contention from 1.1 and what their 
resolution was.

>
> Regards,
> Philip
>
> 
>
> > […]

-- 
-Clint

---

## #64488 — Steve Clay — 2013-01-02T22:18:26+00:00

URL: https://externals.io/message/64469#64488 · In reply to: #64487

> Omitting isset/unset has the same effect as declaring it without a body.  This is
> described in the RFC under Automatic Implementations with this line:
>
> Note that isset/unset implementations will always be provided if they are not defined or
> if they are explicitly auto-defined (as above).

I think the RFC could make this clearer: "isset & unset are always provided with the 
default implementations unless the author provides his/her own."


Looking closer at the default implementations of isset/unset, I'm worried these could lead 
to confusion. Consider this code:

class TimePeriod {
     private $Seconds = 3600;
     public $Hours {
         get { return $this->Seconds / 3600; }
         set { $this->Seconds = $value; }
     }
}

The RFC's default implementation is always bound to the shadow property, so here's what 
you really get:

class TimePeriod {
     private $Seconds = 3600;
     public $Hours {
         get { return $this->Seconds / 3600; }
         set { $this->Seconds = $value; }

         // auto-generated
         isset { return $this->Hours != NULL; }
         unset { $this->Hours = NULL; }
     }
}

Note the resulting behavior:

$t = new TimePeriod;
$t->Hours; // 1
isset($t->Hours); // false !?
unset($t->Hours);
$t->Hours; // still 1

Effectively, authors who don't base their getters/setters on the shadowed property must be 
urged to create their own isset/unset because the default ones would be useless. I'm not 
crazy about this.


I'd prefer these default implementations:

   isset { return $this->__getHours() != NULL; }
   unset { $this->__setHours(NULL); }

$t = new TimePeriod;
$t->Hours; // 1
isset($t->Hours); // true
unset($t->Hours);
$t->Hours; // null
isset($t->Hours); // false

Note these also work as expected when using the default get/set implementations. Of, 
course, my implementations don't actually *work* because you can't call an accessor from 
an accessor...


Steve Clay
-- 
http://www.mrclay.org/

---

## #64489 — Clint Priest — 2013-01-02T23:08:14+00:00

URL: https://externals.io/message/64469#64489 · In reply to: #64488

> 
> > […]
>
> I think the RFC could make this clearer: "isset & unset are always 
> provided with the default implementations unless the author provides 
> his/her own."
>
I can do that, no problem.
>
> Looking closer at the default implementations of isset/unset, I'm 
> worried these could lead to confusion. Consider this code:
>
> class TimePeriod {
>     private $Seconds = 3600;
>     public $Hours {
>         get { return $this->Seconds / 3600; }
>         set { $this->Seconds = $value; }
>     }
> }
>
> The RFC's default implementation is always bound to the shadow 
> property, so here's what you really get:
>
> class TimePeriod {
>     private $Seconds = 3600;
>     public $Hours {
>         get { return $this->Seconds / 3600; }
>         set { $this->Seconds = $value; }
>
>         // auto-generated
>         isset { return $this->Hours != NULL; }
>         unset { $this->Hours = NULL; }
>     }
> }
>
> Note the resulting behavior:
>
> $t = new TimePeriod;
> $t->Hours; // 1
> isset($t->Hours); // false !?
> unset($t->Hours);
> $t->Hours; // still 1
>
> Effectively, authors who don't base their getters/setters on the 
> shadowed property must be urged to create their own isset/unset 
> because the default ones would be useless. I'm not crazy about this.
>
Sorry, there was a typo in that RFC there, this line:
         isset { return $this->Hours != NULL; }
Should have been with !==:
         isset { return $this->Hours !== NULL; }

I've already updated the 1.2 doc to reflect the correct way.

Given what I mentioned above, I'm assuming you did not test this with 
the fork, right?   Just based your comments on how it should logically 
work (with the incorrect != vs !==?)

One last thing about that, the isset/unset with $this->Hours calls the 
getter to retrieve the $this->Hours value, so it behaves as your example 
below indicates.
>
> I'd prefer these default implementations:
>
>   isset { return $this->__getHours() != NULL; }
>   unset { $this->__setHours(NULL); }
>
> $t = new TimePeriod;
> $t->Hours; // 1
> isset($t->Hours); // true
> unset($t->Hours);
> $t->Hours; // null
> isset($t->Hours); // false
>
> Note these also work as expected when using the default get/set 
> implementations. Of, course, my implementations don't actually *work* 
> because you can't call an accessor from an accessor...
>
Steve, are you testing these w/ the fork?  Sounds like you are... But 
your above sentence is not accurate, you can call an accessor from an 
accessor.  isset { return $this->Hours !== NULL; } calls the getter for 
the value and compares it with NULL.
>
> Steve Clay

-- 
-Clint

---

## #64490 — Steve Clay — 2013-01-03T00:28:43+00:00

URL: https://externals.io/message/64469#64490 · In reply to: #64489

> Sorry, there was a typo in that RFC there, this line:
>          isset { return $this->Hours != NULL; }
> Should have been with !==:
>          isset { return $this->Hours !== NULL; }
>
> I've already updated the 1.2 doc to reflect the correct way.
>
> Given what I mentioned above, I'm assuming you did not test this with the fork, right?
> Just based your comments on how it should logically work (with the incorrect != vs !==?)

I haven't tested the fork. I just borrowed your logic with the typo :)

> One last thing about that, the isset/unset with $this->Hours calls the getter to retrieve
> the $this->Hours value, so it behaves as your example below indicates.

The RFC says, "only the accessors themselves may directly access the shadowed property." I 
read that as:

   Within get,   $this->Hours is the raw shadowed property.
   Within set,   $this->Hours is the raw shadowed property.
   Within isset, $this->Hours is the raw shadowed property.
   Within unset, $this->Hours is the raw shadowed property.

But you seem to imply:

   Within get,   $this->Hours is the raw shadowed property.
   Within set,   $this->Hours is the raw shadowed property.
   Within isset, $this->Hours is accessed via __getHours()/__setHours().
   Within unset, $this->Hours is accessed via __getHours()/__setHours().

So really the default implementations behave like this:

   isset { return $this->__getHours() !== NULL; }
   unset { $this->__setHours(NULL); }

I think the RFC should be much clearer about what property access actually means within 
each accessor method, as I expect users to be very surprised by this behavior.

This is also looks like it could lead to surprises:

   Within get, $this->Hours is the raw shadowed property.
   Within get, parent::$Hours is accessed via parent::__getHours()/parent::__setHours().

Also, is there no way to access the shadow property within isset/unset? If not, is there a 
good reason to not allow it?


Also, do/should multiple property accessors interact? Consider:

class Foo {
     public $a {
         get { $this->a = 1; return 2; }
     }
     public $b {
         get { return $this->a; }
     }
}

$foo = new Foo;
$foo->a; // 2 (but shadowed property is 1)
$foo->b; // 1 or 2?


Steve Clay
-- 
http://www.mrclay.org/

---

## #64491 — Clint Priest — 2013-01-03T03:24:57+00:00

URL: https://externals.io/message/64469#64491 · In reply to: #64490

All great questions Steve, doesn't quite work the way you have here.

Specifically each get/set/isset/unset have their own guards (just like 
__get(), __set(), __isset() and __unset()) which means that:

Within get: $this->Hours can read the underlying property but not write 
to it, if it attempts to write, that write would go through the setter.
Within set: $this->Hours = 1 can write to the underlying property but a 
read of the property would go through the getter.
Within isset/unset: the same rules apply, a read goes through the getter 
and a write goes through the setter.

I've updated the Shadowing section of the RFC which I hope clears this 
up, it also includes a slightly modified version of your example at the 
bottom with comments.

More comments below:


> 
> > […]
>
> I haven't tested the fork. I just borrowed your logic with the typo :)
>
> > […]
>
> The RFC says, "only the accessors themselves may directly access the 
> shadowed property." I read that as:
>
>   Within get,   $this->Hours is the raw shadowed property.
>   Within set,   $this->Hours is the raw shadowed property.
>   Within isset, $this->Hours is the raw shadowed property.
>   Within unset, $this->Hours is the raw shadowed property. 
> But you seem to imply:
>
>   Within get,   $this->Hours is the raw shadowed property.
>   Within set,   $this->Hours is the raw shadowed property.
>   Within isset, $this->Hours is accessed via __getHours()/__setHours().
>   Within unset, $this->Hours is accessed via __getHours()/__setHours().
>
> So really the default implementations behave like this:
>
>   isset { return $this->__getHours() !== NULL; }
>   unset { $this->__setHours(NULL); }
Technically this is an accurate translation of what happens with the RFC 
example, but this would work as well.
>
> I think the RFC should be much clearer about what property access 
> actually means within each accessor method, as I expect users to be 
> very surprised by this behavior.
>
> This is also looks like it could lead to surprises:
>
>   Within get, $this->Hours is the raw shadowed property.
>   Within get, parent::$Hours is accessed via 
> parent::__getHours()/parent::__setHours().
I'm not sure I understand what you mean here... within get the parent 
accessor is accessed via parent::$Hours, internally that is translated 
to what you have above but none of this parent::__getHours() needs to be 
typed out, parent::$Hours will suffice.
>
> Also, is there no way to access the shadow property within 
> isset/unset? If not, is there a good reason to not allow it?

Yes, it would bypass the getter and setter which may be dynamic and 
never set the underlying property.

> Also, do/should multiple property accessors interact? Consider:
>
> class Foo {
>     public $a {
>         get { $this->a = 1; return 2; }
>     }
>     public $b {
>         get { return $this->a; }
>     }
> }
>
> $foo = new Foo;
> $foo->a; // 2 (but shadowed property is 1)
> $foo->b; // 1 or 2?
This would cause a "Warning, unable to set property Foo::$a, no setter 
defined." Both of your $foo->a and $foo->b lines would return the return 
value of the Foo::$a getter which is always 2.

The reason it would produce that warning is because you do not have a 
setter for $a defined and therefore it is read only, even to its-self.  
Only the setter may set the underlying value.
>
> Steve Clay

-- 
-Clint

---

## #64492 — Steve Clay — 2013-01-03T05:52:40+00:00

URL: https://externals.io/message/64469#64492 · In reply to: #64491

> I've updated the Shadowing section of the RFC which I hope clears this up, it also includes a slightly modified version of your example at the bottom with comments.

Updated RFC really helps. The notion of $this->prop access semantics depending on which accessor you're in seems important for the RFC as I think it will seem foreign to a lot of devs.

When I make traditional PHP "accessor" methods, I have complete control; if I want getFoo() to set private $foo without calling setFoo(), I can. Not so with real accessors. Probably a good thing :)

One more concern, sorry if it was covered already: will case-insensitivity of methods mean you can't define getters for both $foo and $Foo?

Steve

---

## #64493 — Stas Malyshev — 2013-01-03T06:48:22+00:00

URL: https://externals.io/message/64469#64493 · In reply to: #64491

Hi!

> Within get: $this->Hours can read the underlying property but not write 
> to it, if it attempts to write, that write would go through the setter.
> Within set: $this->Hours = 1 can write to the underlying property but a 
> read of the property would go through the getter.

Are the accesses also applying to called functions/accessors? I.e.
consider this:

class SuperDate {
	private $date {
		get;
		set(DateTime $x) { $this->date = $x; $this->timestamp =
$x->getTimestamp();
	}
	private $timestamp {
		get;
		set($t) { $t = (int)$t; $this->timestamp = $t; $this->date = new
DateTime("@$t"); }
	}
}

What happens to it? Would it get into infinite loop or will just set the
value twice? What would be the correct way to write such a code (note
the real code of course could be much more complicated and probably
involve dozen of properties with complex dependencies between them).

Also, if this applies to functions called from getter/setter (which
seems to be the case from the code, unless I miss something), consider this:

class UserContext {
	protected $user;
	public $logger;
	public $username {
		get() { $this->logger->log("Getting username"); return $user->name; }
		set($n) { $this->user = User::get_by_name($n); }
	}
}

class Logger {
	protected $ctx;
	public function __construct(UserContext $ctx) {
		$this->ctx = $ctx;
		$this->logfile = fopen("/tmp/log", "a+");
	}
	public function log($message) {
		fwrite($this->logfile, "[$this->ctx->username] $message\n");
	}
}

$u = new UserContext();
$u->logger = new Logger($u);
$u->username = "johndoe";
echo $u->username;

What would happen with this code? Will the log be able to log the actual
user name, and if not, how you protect from such thing? $username is a
part of public API of UserContext, so whoever is writing Logger has
right to use it. On the other hand, whoever is using logger->log in
UserContext has absolutely no way to know that Logger is using
ctx->username internally, as these components can change completely
independently and don't know anything about each other besides public APIs.
What I am getting at here is that shadowing seems to create very tricky
hidden state that can lead to very bad error situations when using
public APIs without knowledge of internal implementation.

> Within isset/unset: the same rules apply, a read goes through the getter 
> and a write goes through the setter.

With this code:

class Foo {
	public $bar {
		get;
		set;
	}
}

How could I make it set to 2 by default and isset() return true when I
instantiate the class? Currently, I see no way to assign default values
for properties. Is it planned?
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #64494 — Clint Priest — 2013-01-03T10:40:06+00:00

URL: https://externals.io/message/64469#64494 · In reply to: #64492

That has not been covered and it is a problem, just tested it.

Anyone have any preferences on a resolution?

Only thing that really needs to occur is that the function names need to 
be unique, we could prefix any capitals in a variable name with an 
additional _ such that:

class A {
     public $Foo { get; set; }
     public $foo { get; set; }
}

Would mean there would be these function names:
A::__get_Foo();
A::__set_Foo();
A::__getfoo();
A::__setfoo();

Along w/ their unset/isset'rs.

This is what happens as of right now:

Fatal error: Cannot redeclare a::__getfoo() in 
/opt/php-core/git/trunk-accessor/cpriest/php/quick2.php on line 9

Good catch on that one.


> 
> > […]
> Updated RFC really helps. The notion of $this->prop access semantics depending on which accessor you're in seems important for the RFC as I think it will seem foreign to a lot of devs.
>
> When I make traditional PHP "accessor" methods, I have complete control; if I want getFoo() to set private $foo without calling setFoo(), I can. Not so with real accessors. Probably a good thing :)
>
> One more concern, sorry if it was covered already: will case-insensitivity of methods mean you can't define getters for both $foo and $Foo?
>
> Steve

-- 
-Clint

---

## #64496 — Clint Priest — 2013-01-03T10:58:24+00:00

URL: https://externals.io/message/64469#64496 · In reply to: #64493

> Hi!
>
> > […]
> Are the accesses also applying to called functions/accessors? I.e.
> consider this:
>
> class SuperDate {
> 	private $date {
> 		get;
> 		set(DateTime $x) { $this->date = $x; $this->timestamp =
> $x->getTimestamp();
> 	}
> 	private $timestamp {
> 		get;
> 		set($t) { $t = (int)$t; $this->timestamp = $t; $this->date = new
> DateTime("@$t"); }
> 	}
> }
>
> What happens to it? Would it get into infinite loop or will just set the
> value twice? What would be the correct way to write such a code (note
> the real code of course could be much more complicated and probably
> involve dozen of properties with complex dependencies between them).

This recursion is protected in the same way that the code above would be 
protected using __get/__set in that the first set::$date locks the 
setter, so technically in this case the 2nd call to $this->date = new 
DateTime(...); would directly access the underlying date property.

I don't like this personally (because now the $timestamp setter is 
directly accessing the underlying $date variable) but this is precisely 
what would happen if this same code were implemented with __get() and 
__set().

> Also, if this applies to functions called from getter/setter (which
> seems to be the case from the code, unless I miss something), consider this:
>
> class UserContext {
> 	protected $user;
> 	public $logger;
> 	public $username {
> 		get() { $this->logger->log("Getting username"); return $user->name; }
> 		set($n) { $this->user = User::get_by_name($n); }
> 	}
> }
>
> class Logger {
> 	protected $ctx;
> 	public function __construct(UserContext $ctx) {
> 		$this->ctx = $ctx;
> 		$this->logfile = fopen("/tmp/log", "a+");
> 	}
> 	public function log($message) {
> 		fwrite($this->logfile, "[$this->ctx->username] $message\n");
> 	}
> }
>
> $u = new UserContext();
> $u->logger = new Logger($u);
> $u->username = "johndoe";
> echo $u->username;
>
> What would happen with this code? Will the log be able to log the actual
> user name, and if not, how you protect from such thing? $username is a
> part of public API of UserContext, so whoever is writing Logger has
> right to use it. On the other hand, whoever is using logger->log in
> UserContext has absolutely no way to know that Logger is using
> ctx->username internally, as these components can change completely
> independently and don't know anything about each other besides public APIs.
> What I am getting at here is that shadowing seems to create very tricky
> hidden state that can lead to very bad error situations when using
> public APIs without knowledge of internal implementation.
Again this is creating recursion and the same rules that apply to 
__get()/__set() apply here, your `fwrite()` access of 
UserContext::$username when called through the getter for $username is 
already locked and thus the `fwrite()` line directly accesses the 
underlying property.

Same as before, this is exactly the same behavior you would get with 
__get() and __set().  These guard mechanisms were put in place with a 
revision to __get()/__set() shortly after they were first released 
(first release didn't allow recursion at all).

We could possibly also catch this scenario and either show a warning on 
the `fwrite()` direct access or a fatal error or just allow the direct 
access.  Since __get() and __set() already work this way, it's probably 
fine as-is, even if not perfect OO.

Anyone know what would happen in such a case with another language that 
supports accessors?  My guess would be infinite recursion... (no guards)...

> > […]
> With this code:
>
> class Foo {
> 	public $bar {
> 		get;
> 		set;
> 	}
> }
>
> How could I make it set to 2 by default and isset() return true when I
> instantiate the class? Currently, I see no way to assign default values
> for properties. Is it planned?

In the changing over to accessors being distinct from properties into 
"properties with accessors" I had considered this and think it would be 
great.  Unless I had some trouble with the lexer it should be trivial to 
add, would be something like this:

class Foo {

	public $bar = 2 {
		get;
		set;
	}

}

Anyone object to this addition to the spec?


-- 
-Clint

---

## #64497 — Clint Priest — 2013-01-03T11:08:33+00:00

URL: https://externals.io/message/64469#64497 · In reply to: #64486

Just getting back to you on #1 and #2...

#1: It seems the majority want to have these internally created accessor 
functions visible, which they presently are through get_class_methods, 
etc.  They are currently hidden by Reflection. I favor the latter, as is 
implemented in Reflection since an accessor is "like a method" but is 
not quite a method.  At any rate, unless anyone voices support for 
accessors being hidden from the non-reflection methods, then the current 
Reflection changes regarding filtering them from getMethods() will go away.

One alternative here is that I could add a parameter to getMethods() 
which would either filter or not filter accessors, which would let 
whomever work the way they want to.  Defaulting to... which way?

#2:
class a {
     public $Foo {
         get {
             echo "Getting \$Foo, __FUNCTION__ = ".__FUNCTION__.", 
__METHOD__ = ".__METHOD__.PHP_EOL;
             return 5;
         }
     }
}

$o= new a();
echo $o->Foo;

Outputs:
Getting $Foo, __FUNCTION__ = __getFoo, __METHOD__ = a::__getFoo
5

I will add to the RFC that __FUNCTION__ and __METHOD__ work as expected.


>
> 
> > […]
> This is the only remaining point of contention but I would expect 
> however it is resolved, all methods of reflection would match.
> > […]
> I would have to test them but they are methods so they should evaluate 
> as you would expect, I'll test.
> > […]
> Same thing as a duplicate function declaration error, since that's 
> what it is, remember that the prefix __ is reserved for php internal 
> use, if I recall correctly userland code should not be using a double 
> underscore.
> > […]
>
> 
> > […]
> As of the current fork there is no difference between a property and a 
> property with accessors except that a property with accessors will 
> always route through the accessor methods.  In other words a 
> property_info structure is created for either type.
>
> Ergo, the same rules apply, a second declaration of a property with 
> the same name will cause a compilation error.

-- 
-Clint

---

## #64501 — Steve Clay — 2013-01-03T14:21:19+00:00

URL: https://externals.io/message/64469#64501 · In reply to: #64493

> class SuperDate {
> 	private $date {
> 		get;
> 		set(DateTime $x) { $this->date = $x; $this->timestamp =
> $x->getTimestamp();
> 	}
> 	private $timestamp {
> 		get;
> 		set($t) { $t = (int)$t; $this->timestamp = $t; $this->date = new
> DateTime("@$t"); }
> 	}
> }
>
> What happens to it? Would it get into infinite loop or will just set the
> value twice? What would be the correct way to write such a code (note

I think infinite recursion is a potential issue for lots of logging setups ("let's log 
when someone calls the logger!") and situations where you have multiple values to keep in 
sync. The accessor implementation shouldn't try to solve these design problems.


> class UserContext {
> 	protected $user;
> 	public $logger;
> 	public $username {
> 		get() { $this->logger->log("Getting username"); return $user->name; }
> 		set($n) { $this->user = User::get_by_name($n); }
> 	}
> }
>
> class Logger {
> 	protected $ctx;
> 	public function __construct(UserContext $ctx) {
> 		$this->ctx = $ctx;
> 		$this->logfile = fopen("/tmp/log", "a+");
> 	}
> 	public function log($message) {
> 		fwrite($this->logfile, "[$this->ctx->username] $message\n");
> 	}
> }
>
> $u = new UserContext();
> $u->logger = new Logger($u);
> $u->username = "johndoe";
> echo $u->username;
>
> What would happen with this code? Will the log be able to log the actual
> user name, and if not, how you protect from such thing? $username is a
> part of public API of UserContext, so whoever is writing Logger has
> right to use it. On the other hand, whoever is using logger->log in
> UserContext has absolutely no way to know that Logger is using
> ctx->username internally, as these components can change completely
> independently and don't know anything about each other besides public APIs.
> What I am getting at here is that shadowing seems to create very tricky
> hidden state that can lead to very bad error situations when using
> public APIs without knowledge of internal implementation.

Again, the problem is not shadowing (not even in use here) but really general information 
hiding. You can create these problems anytime you have hidden information and 
interdependent objects, and it's an API design problem.

Steve Clay
-- 
http://www.mrclay.org/

---

## #64503 — Steve Clay — 2013-01-03T15:33:47+00:00

URL: https://externals.io/message/64469#64503 · In reply to: #64496

> class Foo {
>      public $bar = 2 {
>          get;
>          set;
>      }
> }

Consider properties not based on shadowed values:

class Foo {
     private $realbar;

     public $bar = 2 {
         get { return $this->realbar; }
         set { $this->realbar = $value; }
     }
}

Here, initializing the shadow property is useless. It's similar to the case where you want 
to have the initial value of a traditional property be the result of an expression (and 
you can't because it would create a chicken-egg problem during construction).


Option 1: The most powerful solution I see is to have an init function that's implicitly 
called before first access (and with direct write access to the shadow property if 
needed). Consider trying to emulate the crazy document.cookie API:

class Document {
     private $cookieProps;

     public $cookie {
         get { return /* based on cookieProps */; }
         set { /* set some cookieProps */ }

         // called implicitly before the first access (not before the constructor runs)
         // and would have direct access to the shadow property.
         init { /* set up cookieProps */ }
     }
}

Pros: can run any code necessary to initialize the property
Cons: must make a function just to have a default


Option 2: Keep the traditional syntax, but instead of always setting the shadow property, 
call the setter with the given initial value directly before the first access.

Pros: familiar syntax; more expected behavior in some cases
Cons: what if there's no setter?; cannot run arbitrary setup code; setter can't 
distinguish between a "user" set and the implicitly set before first access.


Option 3: A mix of both. The shadow property is always initialized with the value from the 
traditional syntax. If needed, you can have an init that's called before first access.

Pros: For simple shadowed properties, just works as you'd expect and with familiar syntax; 
Still allows robust initialization before first access if needed.
Cons: Author must remember that traditional syntax only useful for shadow prop.


Steve Clay
-- 
http://www.mrclay.org/

---

## #64505 — Clint Priest — 2013-01-03T16:36:44+00:00

URL: https://externals.io/message/64469#64505 · In reply to: #64501

> 
> > […]
>
> I think infinite recursion is a potential issue for lots of logging 
> setups ("let's log when someone calls the logger!") and situations 
> where you have multiple values to keep in sync. The accessor 
> implementation shouldn't try to solve these design problems.
>
__get()/__set() already implement infinite recursion guards, Accessors 
are just doing the same thing.
>
> > […]
>
> Again, the problem is not shadowing (not even in use here) but really 
> general information hiding. You can create these problems anytime you 
> have hidden information and interdependent objects, and it's an API 
> design problem.
>
> Steve Clay

-- 
-Clint

---

## #64506 — Clint Priest — 2013-01-03T16:41:24+00:00

URL: https://externals.io/message/64469#64506 · In reply to: #64503

> 
> > […]
>
> Consider properties not based on shadowed values:
>
> class Foo {
>     private $realbar;
>
>     public $bar = 2 {
>         get { return $this->realbar; }
>         set { $this->realbar = $value; }
>     }
> }
>
What would be the point of this?  Why not just do this:

class Foo {
     private $realbar = 2;

     public $bar {
         get { return $this->realbar; }
         set { $this->realbar = $value; }
     }
}

> Here, initializing the shadow property is useless. It's similar to the 
> case where you want to have the initial value of a traditional 
> property be the result of an expression (and you can't because it 
> would create a chicken-egg problem during construction).
>
>
> Option 1: The most powerful solution I see is to have an init function 
> that's implicitly called before first access (and with direct write 
> access to the shadow property if needed). Consider trying to emulate 
> the crazy document.cookie API:
>
> class Document {
>     private $cookieProps;
>
>     public $cookie {
>         get { return /* based on cookieProps */; }
>         set { /* set some cookieProps */ }
>
>         // called implicitly before the first access (not before the 
> constructor runs)
>         // and would have direct access to the shadow property.
>         init { /* set up cookieProps */ }
>     }
> }
>
> Pros: can run any code necessary to initialize the property
> Cons: must make a function just to have a default
>
>
> Option 2: Keep the traditional syntax, but instead of always setting 
> the shadow property, call the setter with the given initial value 
> directly before the first access.
>
> Pros: familiar syntax; more expected behavior in some cases
> Cons: what if there's no setter?; cannot run arbitrary setup code; 
> setter can't distinguish between a "user" set and the implicitly set 
> before first access.
>
>
> Option 3: A mix of both. The shadow property is always initialized 
> with the value from the traditional syntax. If needed, you can have an 
> init that's called before first access.
>
> Pros: For simple shadowed properties, just works as you'd expect and 
> with familiar syntax; Still allows robust initialization before first 
> access if needed.
> Cons: Author must remember that traditional syntax only useful for 
> shadow prop.
>
I like the idea of an init function, but that would have to wait for a 
further release I think, or delay this whole project a year.

Adding the ability to specify an = X value to a property with accessors 
would be trivial.
>
> Steve Clay

-- 
-Clint

---

## #64507 — Michael Wallner — 2013-01-03T17:42:09+00:00

URL: https://externals.io/message/64469#64507 · In reply to: #64506

> I like the idea of an init function, but that would have to wait for a
> further release I think, or delay this whole project a year.

We have constructors, shouldn't those be sufficient for that task?

-- 
Regards,
Mike

---

## #64508 — Steve Clay — 2013-01-03T19:38:40+00:00

URL: https://externals.io/message/64469#64508 · In reply to: #64506

> > […]
> What would be the point of this?

I think it would be more readable for someone using the class. As a user it helps to know 
the default of the property I'll actually be interacting with. In a non-trivial getter the 
default may be much harder to tease out.

But yes, I agree that it's straightforward enough for the class author to set defaults as 
needed, so I see no problem moving forward. The RFC should make clear that only the shadow 
value is set (a setter, if present, is not used).


In the features docs I suggest finding a term for a non-traditional property, like 
"guarded property".

Also I think you should eliminate all "shadowing" language. IMO it adds more confusion 
than it removes. Here's some language that may/may not be useful:


Almost all interaction with a guarded property is "proxied", meaning the getter and setter 
methods are used instead of direct reading/writing of the value. There are only two 
exceptions:

1. Within a getter's scope, reads are not proxied: the property is read directly.

2. Within a setter's scope, writes are not proxied; the property is written to directly.

Accessors are free to interact with other properties which are visible to them, but access 
to other guarded properties is always proxied.

Accessors are not required to use the property value, but it always exists.


Steve Clay
-- 
http://www.mrclay.org/

---

## #64509 — Steve Clay — 2013-01-03T19:51:48+00:00

URL: https://externals.io/message/64469#64509 · In reply to: #64507

> 
>
> > […]
>
> We have constructors, shouldn't those be sufficient for that task?

My initial view was that property inits would encourage encapsulation of property setup 
logic, and make sure the property was ready before access in the constructor. Basically 
the same reasons we allow initializing regular properties now.

But now I think it would embolden authors to make overly complex properties (like 
document.cookie) that should really be separate objects. Lets not do that.

Steve Clay
-- 
http://www.mrclay.org/

---

Next page: https://externals.io/message/64469.md?page=2
