Re: [PHP-DEV] [PHP-RFC] Property Accessors 1.2 for Final Review before Vote

2013-01-05 Thread Jordi Boggiano
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

2013-01-05 Thread Steve Clay

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

2013-01-05 Thread Clint Priest
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

2013-01-05 Thread Steve Clay

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

2013-01-05 Thread Clint Priest
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

2013-01-05 Thread Stas Malyshev
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

2013-01-03 Thread Clint Priest

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

2013-01-03 Thread 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

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

2013-01-03 Thread Clint Priest

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

2013-01-03 Thread Steve Clay

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

2013-01-03 Thread Steve Clay

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

2013-01-03 Thread Clint Priest


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

2013-01-03 Thread Clint Priest


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

2013-01-03 Thread Michael Wallner
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

2013-01-03 Thread Steve Clay

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

2013-01-03 Thread Steve Clay

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

2013-01-03 Thread Anthony Ferrara
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

2013-01-03 Thread Stas Malyshev
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

2013-01-03 Thread Clint Priest


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

2013-01-02 Thread Clint Priest
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

2013-01-02 Thread Levi Morrison
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

2013-01-02 Thread Steve Clay

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

2013-01-02 Thread Steve Clay

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

2013-01-02 Thread Levi Morrison
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

2013-01-02 Thread Levi Morrison
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

2013-01-02 Thread Philip Graham
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

2013-01-02 Thread Clint Priest


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

2013-01-02 Thread Clint Priest

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

2013-01-02 Thread Steve Clay

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

2013-01-02 Thread Clint Priest


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

2013-01-02 Thread Steve Clay

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

2013-01-02 Thread Clint Priest

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

2013-01-02 Thread Steve Clay
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

2013-01-02 Thread Stas Malyshev
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