On 1/3/2013 12:48 AM, Stas Malyshev wrote:
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).

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

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?

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

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to