Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:85508 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 85596 invoked from network); 27 Mar 2015 15:06:24 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 27 Mar 2015 15:06:24 -0000 Authentication-Results: pb1.pair.com header.from=pjsturgeon@gmail.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=pjsturgeon@gmail.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.217.178 as permitted sender) X-PHP-List-Original-Sender: pjsturgeon@gmail.com X-Host-Fingerprint: 209.85.217.178 mail-lb0-f178.google.com Received: from [209.85.217.178] ([209.85.217.178:34602] helo=mail-lb0-f178.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 68/27-38005-DE175155 for ; Fri, 27 Mar 2015 10:06:22 -0500 Received: by lboc7 with SMTP id c7so8805285lbo.1 for ; Fri, 27 Mar 2015 08:06:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:content-transfer-encoding; bh=KgYe4UBknD5mdVWWYheivnaf8277/pv+lqHq9yFNGLU=; b=dsHy4w2ncCc7Igie+4Z121Y8mZU+nD6T/7mHIhoP3Z8Pt3Q4YQr1dB0Zv1PLw5JO6O ogcwOQjWEwodOFnmcTpOApe6JvNncSNsfTouVk+d2HeedsPZitxx/6RuTUVZxxpyn/UH JuSYydlsDFTrGTo72u9NOTbu3Z+PH+mlKuvmwIxDPedFxoC8xgJn6QndgVxvmsaETI4H 7V3y0eTl6Glv/MDs95xmVHy6BbpAPb1sUEWVSewn6ihehf179WBZX/fuqmy3Cln8jfOO qMV40+v9wEkr/wyG5OCLOtl6Yb+02ShNTDFfwKb48dzBc//x0NQ5buh2HhjVSsAJldkf 0Yew== MIME-Version: 1.0 X-Received: by 10.112.85.165 with SMTP id i5mr18652875lbz.7.1427468778700; Fri, 27 Mar 2015 08:06:18 -0700 (PDT) Received: by 10.114.26.34 with HTTP; Fri, 27 Mar 2015 08:06:18 -0700 (PDT) In-Reply-To: References: Date: Fri, 27 Mar 2015 11:06:18 -0400 Message-ID: To: Nikita Popov Cc: "internals@lists.php.net" Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] [VOTE] [RFC] Anonymous Classes From: pjsturgeon@gmail.com (Philip Sturgeon) On Sat, Mar 14, 2015 at 6:33 AM, Nikita Popov wrote: > On Fri, Mar 13, 2015 at 8:33 PM, Philip Sturgeon > 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 se= ems > incomplete to me with regard to scoping (and as future changes in this ar= ea > 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 =3D $data; > } > > public function getArrayAccess() { > return new class($this->data) extends Outside implements ArrayAcc= ess > { > public function offsetGet($offset) { return > $this->data[$offset]; } > public function offsetSet($offset, $data) { return > ($this->data[$offset] =3D $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 understan= d > the evil, evil things this code is doing and why it is doing them. This i= s > 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), a= s > 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 protect= ed > $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 Oute= r > [sic] class, on the same $this=E2=86=92data". > > So the reason behind this is that anonymous classes as implemented by thi= s > RFC - and much unlike the anonymous class implementations you will find i= n > other languages like Java or D - are considered to be totally unrelated t= o > the wrapping class and have only "public" access to its scope. I don't th= ink > this is good. The solution ("extends Outside") to work around this that i= s > presented in the RFC has a number of problems: > > * As PHP does not support multiple inheritance, extending Outside for sc= ope > 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 bun= ch > 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 wrappi= ng > 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 t= he > 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 objec= t. > This is consistent with the general rule that any code within a class bod= y > 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 = =3D > $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::$th= is > (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 point. Point b) is pretty much just syntax sugar once a) stands. (T= he > example of a) can be further simplified by using a trait that automatical= ly > implements that constructor and property, to avoid the boilerplate.) > > Nikita I really dont know how I missed this email, but I'm glad we talked about this elsewhere. 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. This is what Nikita wants: http://3v4l.org/QLFoJ/rfc#rfc-anonymous_classes Right now that gives: Fatal error: Cannot access private property Outside::$private in /in/QLFoJ on line 12 Joe plans to bring back Nested Classes (https://wiki.php.net/rfc/nested_classes) and when that is done - hopefully for 7.1 - this code will start to work. Doing things this way is perfectly BC because it currently fatal errors and in the future will actually work. So, this should be all good. It's sad we don't have all of everything right now, but this is a good step on the way. :)