Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 02.01.2013 12:36, Clint Priest wrote: Here is the updated RFC incorporating the feedback from previous rounds of discussion. https://wiki.php.net/rfc/propertygetsetsyntax-v1.2 One thing that I have not found in the RFC is how do you specify a default value AND accessors? Many examples show things like: protected $Seconds = 3600; public $Hours { get { return $this-Seconds / 3600; } set { $this-Seconds = $value * 3600; } } But what if I want to store something in a different internal representation for example with a default. Is this valid syntax? public $hexFoo = 10 { get { return dechex($this-foo); } set { $this-hexFoo = hexdec($value); } } Or is it this? public $hexFoo { get { return dechex($this-foo); } set { $this-hexFoo = hexdec($value); } } = 10; Is it possible at all? Cheers -- Jordi Boggiano @seldaek - http://nelm.io/jordi -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/3/13 5:43 PM, Stas Malyshev wrote: The whole problem here is that the only reason why it is a problem is because of the accessors that have hidden state in guards. If it were regular variables (and for all the API consumer knows, they are) there Please ignore this if it's been debated before: AFAICT C# strictly separates fields (properties in PHP) and properties (a set of accessors that emulate a field). So the RFC provides 3 features (using C# terms): 1. A property API 2. A built-in storage variable so you don't need a separate field 3. Access to the storage variable as if it were a field of the same name I think #2 is useful, avoiding the need to make a separate field just to make properties read-only or type-hinted. However I think the complexity and confusion we're running into is mostly caused by #3. I think we might be better served by having another way to access this storage variable. What if instead, we have the storage var available as $prop inside all the accessors? These would be the default implementations: get { return $prop; } set($value) { $prop = $value; } isset { return $prop !== NULL; } unset { $prop = NULL; } Pros: * Makes clear that $prop is regular var access, and that $this-PropertyName *always* goes through accessors * Gives isset/unset full access to the storage var, which allows doing things that can't be done via setter/getter. E.g. you could actually implement a property being unset, which would be different from having it set to NULL. Cons: * Allows evil, like having reads affect the storage var. * Allows authors to hang themselves with recursive accessor calls, BUT those mistakes would be apparent from looking at the code. What functionality possible in the RFC would be lost by this? Steve Clay -- http://www.mrclay.org/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
I like the alternate idea here, but I'm not sure what advantage it has over the current situation? This line of reasoning revealed a difference between what your verbiage suggestion from a few days ago suggests and what is true. With the guards it's pretty simple what occurs. If an unset (accessor) is called, and another unset is called on that property, the same internal function which handles unset is called. In the 2nd instance, it see's that the unset is already being guarded and so the ordinary unset functionality is invoked. Therefore, take a look at this gist: https://gist.github.com/4463107 I will need to update the RFC because your previous summary which I took verbatim I missed being incorrect. It's really, in my book, a lot simpler to say that if an accessor is already in the call chain then it will not be called again and standard property rules take effect. This is why a getter can get its value directly. It's also why if that getter calls some other code which tries to get the value, that get also bypasses the getter (which prevents infinite loops). -Clint On 1/5/2013 12:06 PM, Steve Clay wrote: On 1/3/13 5:43 PM, Stas Malyshev wrote: The whole problem here is that the only reason why it is a problem is because of the accessors that have hidden state in guards. If it were regular variables (and for all the API consumer knows, they are) there Please ignore this if it's been debated before: AFAICT C# strictly separates fields (properties in PHP) and properties (a set of accessors that emulate a field). So the RFC provides 3 features (using C# terms): 1. A property API 2. A built-in storage variable so you don't need a separate field 3. Access to the storage variable as if it were a field of the same name I think #2 is useful, avoiding the need to make a separate field just to make properties read-only or type-hinted. However I think the complexity and confusion we're running into is mostly caused by #3. I think we might be better served by having another way to access this storage variable. What if instead, we have the storage var available as $prop inside all the accessors? These would be the default implementations: get { return $prop; } set($value) { $prop = $value; } isset { return $prop !== NULL; } unset { $prop = NULL; } Pros: * Makes clear that $prop is regular var access, and that $this-PropertyName *always* goes through accessors * Gives isset/unset full access to the storage var, which allows doing things that can't be done via setter/getter. E.g. you could actually implement a property being unset, which would be different from having it set to NULL. Cons: * Allows evil, like having reads affect the storage var. * Allows authors to hang themselves with recursive accessor calls, BUT those mistakes would be apparent from looking at the code. What functionality possible in the RFC would be lost by this? Steve Clay -- -Clint
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/5/13 2:05 PM, Clint Priest wrote: I like the alternate idea here, but I'm not sure what advantage it has over the current situation? See the Pros I listed. The primary being a clear differentiation between calling accessors and handling of the storage value associated with the property. if an accessor is already in the call chain then it will not be called again and standard property rules take effect. I see that as not simpler, and in fact leading to small bugs whenever getters/setters start becoming complex enough to call outside methods. Consider this: class Foo { public $bar = 2 { get { return $this-calcBar(); } } public function calcBar() { return $this-bar * 3; } } echo $foo-bar; // 6 Within calcBar, $this-bar is the raw property (2), because hidden state has unwrapped (if you will) the property. echo $foo-calcBar(); // 18 But here, within the first call of calcBar $this-bar triggers the getter. Now, of course, this is a foolish implementation, but within any method that could be called from the getter/setter, the symbol $this-bar could mean two completely different things; I think this is bad. If, as I proposed, the storage var were only accessible as $prop in the accessor scopes, that would force authors to pass $prop to any supporting methods, clarifying intent. $this-bar would *always* be accessor calls. In this model, I think infinite-loop-causing recursions would be easier to spot. If absolutely necessary we could always throw a fatal error whenever a getter was called twice in the same call chain. Steve AFAICT C# strictly separates fields (properties in PHP) and properties (a set of accessors that emulate a field). So the RFC provides 3 features (using C# terms): 1. A property API 2. A built-in storage variable so you don't need a separate field 3. Access to the storage variable as if it were a field of the same name I think #2 is useful, avoiding the need to make a separate field just to make properties read-only or type-hinted. However I think the complexity and confusion we're running into is mostly caused by #3. I think we might be better served by having another way to access this storage variable. What if instead, we have the storage var available as $prop inside all the accessors? These would be the default implementations: get { return $prop; } set($value) { $prop = $value; } isset { return $prop !== NULL; } unset { $prop = NULL; } Pros: * Makes clear that $prop is regular var access, and that $this-PropertyName *always* goes through accessors * Gives isset/unset full access to the storage var, which allows doing things that can't be done via setter/getter. E.g. you could actually implement a property being unset, which would be different from having it set to NULL. Cons: * Allows evil, like having reads affect the storage var. * Allows authors to hang themselves with recursive accessor calls, BUT those mistakes would be apparent from looking at the code. What functionality possible in the RFC would be lost by this? Steve Clay Steve Clay -- http://www.mrclay.org/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
The problem I see with your proposal is that get/isset/unset could all bypass the setter which I think is a bad idea. If the problem is simply this hidden state that keeps being referred to, I thought the solution I mentioned before would be better. To re-iterate that prior idea is to make it behave identically to __get(). The first call to a getter would initiate the (calling) guard, any other access outside of the getter from that point forward would return NULL, but the access by the getter would return the property. It would stop the infinite loop, stop invalid access, the author could see where they've gone wrong (a warning could be emitted even) and we maintain the current solution without introducing more complexity (which would probably lead to further problems). For your pro's listed * I don't think a local variable named $prop (for example) is very clear that it's going to modify a property of $this, I would expect $this-prop and that $prop would be modifying a local value. * That's a misconception that I accidentally let perpetuate because I wasn't careful when I confirmed your alternate verbiage. And then it adds the evil's listed. -Clint On 1/5/2013 3:03 PM, Steve Clay wrote: On 1/5/13 2:05 PM, Clint Priest wrote: I like the alternate idea here, but I'm not sure what advantage it has over the current situation? See the Pros I listed. The primary being a clear differentiation between calling accessors and handling of the storage value associated with the property. if an accessor is already in the call chain then it will not be called again and standard property rules take effect. I see that as not simpler, and in fact leading to small bugs whenever getters/setters start becoming complex enough to call outside methods. Consider this: class Foo { public $bar = 2 { get { return $this-calcBar(); } } public function calcBar() { return $this-bar * 3; } } echo $foo-bar; // 6 Within calcBar, $this-bar is the raw property (2), because hidden state has unwrapped (if you will) the property. echo $foo-calcBar(); // 18 But here, within the first call of calcBar $this-bar triggers the getter. Now, of course, this is a foolish implementation, but within any method that could be called from the getter/setter, the symbol $this-bar could mean two completely different things; I think this is bad. If, as I proposed, the storage var were only accessible as $prop in the accessor scopes, that would force authors to pass $prop to any supporting methods, clarifying intent. $this-bar would *always* be accessor calls. In this model, I think infinite-loop-causing recursions would be easier to spot. If absolutely necessary we could always throw a fatal error whenever a getter was called twice in the same call chain. Steve AFAICT C# strictly separates fields (properties in PHP) and properties (a set of accessors that emulate a field). So the RFC provides 3 features (using C# terms): 1. A property API 2. A built-in storage variable so you don't need a separate field 3. Access to the storage variable as if it were a field of the same name I think #2 is useful, avoiding the need to make a separate field just to make properties read-only or type-hinted. However I think the complexity and confusion we're running into is mostly caused by #3. I think we might be better served by having another way to access this storage variable. What if instead, we have the storage var available as $prop inside all the accessors? These would be the default implementations: get { return $prop; } set($value) { $prop = $value; } isset { return $prop !== NULL; } unset { $prop = NULL; } Pros: * Makes clear that $prop is regular var access, and that $this-PropertyName *always* goes through accessors * Gives isset/unset full access to the storage var, which allows doing things that can't be done via setter/getter. E.g. you could actually implement a property being unset, which would be different from having it set to NULL. Cons: * Allows evil, like having reads affect the storage var. * Allows authors to hang themselves with recursive accessor calls, BUT those mistakes would be apparent from looking at the code. What functionality possible in the RFC would be lost by this? Steve Clay Steve Clay -- -Clint
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
Hi! The first call to a getter would initiate the (calling) guard, any other access outside of the getter from that point forward would return NULL, but the access by the getter would return the property. How we implement that? Would we have each variable access check whether we are in the getter or not? We don't want to slow down regular property accesses... It would stop the infinite loop, stop invalid access, the author could see where they've gone wrong (a warning could be emitted even) and we Yes, I think we want to produce a notice or even warning there if we return NULL as this is clearly a bug in the code and should not happen in proper code. -- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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. On 1/2/2013 11:52 PM, Steve Clay wrote: On Jan 2, 2013, at 10:24 PM, Clint Priest cpri...@zerocue.com wrote: 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 -- -Clint
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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. On 1/2/2013 3:08 PM, Clint Priest wrote: On 1/2/2013 11:08 AM, Steve Clay wrote: 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 On 1/2/2013 11:41 AM, Steve Clay wrote: 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
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/3/13 1:48 AM, Stas Malyshev wrote: 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/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/3/13 5:58 AM, Clint Priest wrote: 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/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/3/2013 8:21 AM, Steve Clay wrote: On 1/3/13 1:48 AM, Stas Malyshev wrote: 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. __get()/__set() already implement infinite recursion guards, Accessors are just doing the same thing. 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 -- -Clint -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/3/2013 9:33 AM, Steve Clay wrote: On 1/3/13 5:58 AM, Clint Priest wrote: 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; } } } 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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 3 January 2013 17:41, Clint Priest cpri...@zerocue.com wrote: 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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/3/13 11:41 AM, Clint Priest wrote: class Foo { private $realbar; public $bar = 2 { get { return $this-realbar; } set { $this-realbar = $value; } } } 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/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/3/13 12:42 PM, Michael Wallner wrote: On 3 January 2013 17:41, Clint Priest cpri...@zerocue.com wrote: 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? 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/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
Clint, ...snip... 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. Well, just speaking in general, we should not try to rush through these kinds of design decisions. They should only be done incrementally if it makes sense to do it incrementally. That really needs to be thought out. IMHO there's no problem delaying anything a year. We're in yearly releases now, so it's not like the 5.1 and 5.2 days where the next release it may get in was literally 5 or 10 years off... I'd much rather see it done right, even if later, rather than rushed in and sticking us with the implementation forever... Just pointing it out (in case it needed to be said)... Anthony
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
Hi! 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. The whole problem here is that the only reason why it is a problem is because of the accessors that have hidden state in guards. If it were regular variables (and for all the API consumer knows, they are) there wouldn't be any question about if we're allowed to read $user-username or not - if it's public, of course we can read it. So the problem exists because the hidden state exists. It's not the problem of the code that uses public APIs in completely legal way, it is the problem of our implementation that we have hidden state that changes how variable access works. With __get we mostly ignored it since recursion blocker leads to the same result as if the variable did not exist - which is the case for __get anyway, kind of - so it was less explicit. With accessors it may become more painful. -- Stanislav Malyshev, Software Architect SugarCRM: http://www.sugarcrm.com/ (408)454-6900 ext. 227 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
Not really sure what to say about this, we can either guard against recursion or not, I see no reason *not* to guard against recursion except that it could allow unauthorized direct access when the guard is active. I guess a third option could be that if the property is attempted to be accessed while being guarded and that access is not from within the accessor, then it issues a warning (or not) and returns NULL. Thoughts? On 1/3/2013 4:43 PM, Stas Malyshev wrote: Hi! 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. The whole problem here is that the only reason why it is a problem is because of the accessors that have hidden state in guards. If it were regular variables (and for all the API consumer knows, they are) there wouldn't be any question about if we're allowed to read $user-username or not - if it's public, of course we can read it. So the problem exists because the hidden state exists. It's not the problem of the code that uses public APIs in completely legal way, it is the problem of our implementation that we have hidden state that changes how variable access works. With __get we mostly ignored it since recursion blocker leads to the same result as if the variable did not exist - which is the case for __get anyway, kind of - so it was less explicit. With accessors it may become more painful. -- -Clint -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
[PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/2/13 6:36 AM, Clint Priest wrote: 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/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/2/13 6:36 AM, Clint Priest wrote: 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/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On Wed, Jan 2, 2013 at 10:41 AM, Steve Clay st...@mrclay.org wrote: On 1/2/13 6:36 AM, Clint Priest wrote: 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. 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. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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? -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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 On Wed, Jan 2, 2013 at 6:36 AM, Clint Priest cpri...@zerocue.com wrote: Here is the updated RFC incorporating the feedback from previous rounds of discussion. https://wiki.php.net/rfc/**propertynot getsetsyntax-v1.2https://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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/2/2013 11:08 AM, Steve Clay wrote: 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 On 1/2/2013 11:41 AM, Steve Clay wrote: 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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/2/2013 12:44 PM, Philip Graham wrote: 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 On Wed, Jan 2, 2013 at 6:36 AM, Clint Priest cpri...@zerocue.com wrote: Here is the updated RFC incorporating the feedback from previous rounds of discussion. https://wiki.php.net/rfc/**propertynot getsetsyntax-v1.2https://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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php -- -Clint -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/2/13 4:18 PM, Clint Priest wrote: 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/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/2/2013 4:18 PM, Steve Clay wrote: On 1/2/13 4:18 PM, Clint Priest wrote: 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. 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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On 1/2/13 6:08 PM, Clint Priest wrote: 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/ -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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: On 1/2/2013 6:28 PM, Steve Clay wrote: On 1/2/13 6:08 PM, Clint Priest wrote: 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); } 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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
On Jan 2, 2013, at 10:24 PM, Clint Priest cpri...@zerocue.com wrote: 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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote
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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php