Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:64496 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 15159 invoked from network); 3 Jan 2013 10:58:34 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 3 Jan 2013 10:58:34 -0000 Authentication-Results: pb1.pair.com header.from=cpriest@zerocue.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=cpriest@zerocue.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain zerocue.com designates 67.200.53.250 as permitted sender) X-PHP-List-Original-Sender: cpriest@zerocue.com X-Host-Fingerprint: 67.200.53.250 mail.zerocue.com Received: from [67.200.53.250] ([67.200.53.250:53087] helo=mail.zerocue.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 39/D5-35624-95465E05 for ; Thu, 03 Jan 2013 05:58:33 -0500 Received: from [172.17.0.122] (unknown [66.25.151.173]) (using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits)) (No client certificate requested) by mail.zerocue.com (Postfix) with ESMTPSA id ACAAB120340; Thu, 3 Jan 2013 10:58:30 +0000 (UTC) Message-ID: <50E56450.1060801@zerocue.com> Date: Thu, 03 Jan 2013 04:58:24 -0600 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/17.0 Thunderbird/17.0 MIME-Version: 1.0 To: Stas Malyshev CC: PHP Internals References: <50E41BB6.4030901@zerocue.com> <50E4A43E.6030302@zerocue.com> <50E4B232.5000505@mrclay.org> <50E4BDDE.8050509@zerocue.com> <50E4D0BB.7060701@mrclay.org> <50E4FA09.7030001@zerocue.com> <50E529B6.1050903@sugarcrm.com> In-Reply-To: <50E529B6.1050903@sugarcrm.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote From: cpriest@zerocue.com (Clint Priest) 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