Re: [PHP-DEV] [VOTE] [RFC] Anonymous Classes

2015-03-27 Thread Philip Sturgeon
On Sat, Mar 14, 2015 at 6:33 AM, Nikita Popov nikita@gmail.com wrote:
 On Fri, Mar 13, 2015 at 8:33 PM, Philip Sturgeon pjsturg...@gmail.com
 wrote:

 A two week discussion period has been held and there are no outstanding
 issues.

 Serialization has been disabled, and generated names have been
 explained better in the newest version of the RFC

 https://wiki.php.net/rfc/anonymous_classes

 The implementation needs to be updated with changes from master, but
 that can be done at a later point and should not be used as a reason
 to vote against.


 I'm not yet sure which way to vote on this RFC. I don't think there's
 anything principally wrong with anonymous classes, but the current RFC seems
 incomplete to me with regard to scoping (and as future changes in this area
 are not necessarily backwards-compatible, I'd rather solve this now than
 later). Lets start off with an example from the RFC:

 class Outside {
 protected $data;

 public function __construct($data) {
 $this-data = $data;
 }

 public function getArrayAccess() {
 return new class($this-data) extends Outside implements ArrayAccess
 {
 public function offsetGet($offset) { return
 $this-data[$offset]; }
 public function offsetSet($offset, $data) { return
 ($this-data[$offset] = $data); }
 public function offsetUnset($offset) {
 unset($this-data[$offset]); }
 public function offsetExists($offset) { return
 isset($this-data[$offset]); }
 };
 }
 }

 So ... WTF. It probably took me 10 minutes yesterday to finally understand
 the evil, evil things this code is doing and why it is doing them. This is
 what happens: The new anon class extends Outside (the wrapping class) and as
 such also inherits the parent constructor. The new class($this-data) bit
 passes $this-data to the constructor (the one inherited from Outside), as
 such $this-data will be assigned in the inner class as well. Because the
 anonymous class extended from Outside it is allowed to access the protected
 $data member.

 Why is this (imho very weird and unintuitive) approach used? Quoting from
 the RFC: [E]xtending Outer [sic] allows the nested class implementing
 ArrayAccess permission to execute protected methods, declared in the Outer
 [sic] class, on the same $this→data.

 So the reason behind this is that anonymous classes as implemented by this
 RFC - and much unlike the anonymous class implementations you will find in
 other languages like Java or D - are considered to be totally unrelated to
 the wrapping class and have only public access to its scope. I don't think
 this is good. The solution (extends Outside) to work around this that is
 presented in the RFC has a number of problems:

  * As PHP does not support multiple inheritance, extending Outside for scope
 access means no other class may be extended.
  * Extending the wrapping doesn't just give you access to restricted
 methods/properties of the wrapping class - it will also import everything
 into the inner class. This means that the anonymous class will have a bunch
 of additional methods and properties (potentially public ones) which have
 nothing to do with whatever the class is actually for.
  * The approach used to pass the data into the inner class by using the
 constructor of the wrapping class assumes that the constructor a) only
 performs simple assignments and b) accepts the entire state of the wrapping
 class as parameters - if this is not the case you will likely not be able to
 duplicate the state of the wrapping class in the inner anonymous class.

 To solve this without such hacks, two things are necessary:

 a) Assuming that the anonymous class somehow got hold of an instance of the
 wrapping class (i.e. it has an object $obj instanceof Outside), it should be
 able to access private and protected properties and methods of that object.
 This is consistent with the general rule that any code within a class body
 can access it's privates, unless explicitly rebound. For example, the
 following code should work:

 class Outside {
 private $private;
 public function getAnon() {
 return new class($this) {
 private $outside;
 public function __construct($outside) { $this-outside =
 $outside; }
 public function getPrivateOfOutside() { return
 $this-outside-private; }
 };
 }
 }

 b) Providing some easy way to access the instance of the wrapping class. The
 previous example passed it in as a constructor, which is somewhat verbose. A
 possible syntax for accessing the wrapping instance would be Outside::$this
 (to borrow Java syntax):

 class Outside {
 private $private;
 public function getAnon() {
 return new class {
 public function getPrivateOfOutside() { return
 Outside::$this-private; }
 };
 }
 }

 Point a) is the more fundamental issue, 

Re: [PHP-DEV] [VOTE] [RFC] Anonymous Classes

2015-03-27 Thread Stanislav Malyshev
Hi!

 For anyone else concerned, an anon class inside a parent class is only
 able to act with its public interface. Yes, that is weird, but there
 is currently no support in PHP for nested classes.

True. I'm just thinking we'll end up in the same position as closures in
5.3 where scope support is sorely missing and I have to deal a lot with
apps having to still support 5.3 and doing weird tricks because of that
lack of support. OTOH, implementing it may not be easy as we have to
reconcile 2 scopes there. Not a reason not to do it (I voted yes :) but
a reason to prioritize fixing it soon.

-- 
Stas Malyshev
smalys...@gmail.com

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



Re: [PHP-DEV] [VOTE] [RFC] Anonymous Classes

2015-03-26 Thread Pascal Martin, AFUP

Le 13/03/2015 20:33, Philip Sturgeon a écrit :

https://wiki.php.net/rfc/anonymous_classes

Hi,

We've discussed this with other people at AFUP, and are on the +1 side.

Thanks for this!

--
Pascal MARTIN, AFUP - French UG
http://php-internals.afup.org/


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



Re: [PHP-DEV] [VOTE] [RFC] Anonymous Classes

2015-03-14 Thread Nikita Popov
On Fri, Mar 13, 2015 at 8:33 PM, Philip Sturgeon pjsturg...@gmail.com
wrote:

 A two week discussion period has been held and there are no outstanding
 issues.

 Serialization has been disabled, and generated names have been
 explained better in the newest version of the RFC

 https://wiki.php.net/rfc/anonymous_classes

 The implementation needs to be updated with changes from master, but
 that can be done at a later point and should not be used as a reason
 to vote against.


I'm not yet sure which way to vote on this RFC. I don't think there's
anything principally wrong with anonymous classes, but the current RFC
seems incomplete to me with regard to scoping (and as future changes in
this area are not necessarily backwards-compatible, I'd rather solve this
now than later). Lets start off with an example from the RFC:

class Outside {
protected $data;

public function __construct($data) {
$this-data = $data;
}

public function getArrayAccess() {
return new class($this-data) extends Outside implements ArrayAccess {
public function offsetGet($offset) { return $this-data[$offset]; }
public function offsetSet($offset, $data) { return
($this-data[$offset] = $data); }
public function offsetUnset($offset) { unset
http://www.php.net/unset($this-data[$offset]); }
public function offsetExists($offset) { return isset
http://www.php.net/isset($this-data[$offset]); }
};
}}

So ... WTF. It probably took me 10 minutes yesterday to finally understand
the evil, evil things this code is doing and why it is doing them. This is
what happens: The new anon class extends Outside (the wrapping class) and
as such also inherits the parent constructor. The new class($this-data)
bit passes $this-data to the constructor (the one inherited from Outside),
as such $this-data will be assigned in the inner class as well. Because
the anonymous class extended from Outside it is allowed to access the
protected $data member.

Why is this (imho very weird and unintuitive) approach used? Quoting from
the RFC: [E]xtending Outer [sic] allows the nested class implementing
ArrayAccess permission to execute protected methods, declared in the Outer
[sic] class, on the same $this→data.

So the reason behind this is that anonymous classes as implemented by this
RFC - and much unlike the anonymous class implementations you will find in
other languages like Java or D - are considered to be totally unrelated to
the wrapping class and have only public access to its scope. I don't
think this is good. The solution (extends Outside) to work around this
that is presented in the RFC has a number of problems:

 * As PHP does not support multiple inheritance, extending Outside for
scope access means no other class may be extended.
 * Extending the wrapping doesn't just give you access to restricted
methods/properties of the wrapping class - it will also import everything
into the inner class. This means that the anonymous class will have a bunch
of additional methods and properties (potentially public ones) which have
nothing to do with whatever the class is actually for.
 * The approach used to pass the data into the inner class by using the
constructor of the wrapping class assumes that the constructor a) only
performs simple assignments and b) accepts the entire state of the wrapping
class as parameters - if this is not the case you will likely not be able
to duplicate the state of the wrapping class in the inner anonymous class.

To solve this without such hacks, two things are necessary:

a) Assuming that the anonymous class somehow got hold of an instance of the
wrapping class (i.e. it has an object $obj instanceof Outside), it should
be able to access private and protected properties and methods of that
object. This is consistent with the general rule that any code within a
class body can access it's privates, unless explicitly rebound. For
example, the following code should work:

class Outside {
private $private;
public function getAnon() {
return new class($this) {
private $outside;
public function __construct($outside) { $this-outside =
$outside; }
public function getPrivateOfOutside() { return
$this-outside-private; }
};
}
}

b) Providing some easy way to access the instance of the wrapping class.
The previous example passed it in as a constructor, which is somewhat
verbose. A possible syntax for accessing the wrapping instance would be
Outside::$this (to borrow Java syntax):

class Outside {
private $private;
public function getAnon() {
return new class {
public function getPrivateOfOutside() { return
Outside::$this-private; }
};
}
}

Point a) is the more fundamental issue, which I think should be addressed
from the start. It's also not backwards compatible to introduce it as a
later