Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:56583 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 48285 invoked from network); 24 Nov 2011 20:54:01 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 24 Nov 2011 20:54:01 -0000 Authentication-Results: pb1.pair.com smtp.mail=larry@garfieldtech.com; spf=permerror; sender-id=unknown Authentication-Results: pb1.pair.com header.from=larry@garfieldtech.com; sender-id=unknown Received-SPF: error (pb1.pair.com: domain garfieldtech.com from 66.111.4.26 cause and error) X-PHP-List-Original-Sender: larry@garfieldtech.com X-Host-Fingerprint: 66.111.4.26 out2.smtp.messagingengine.com Received: from [66.111.4.26] ([66.111.4.26:33101] helo=out2.smtp.messagingengine.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id AC/E2-26290-9EEAECE4 for ; Thu, 24 Nov 2011 15:54:01 -0500 Received: from compute2.internal (compute2.nyi.mail.srv.osa [10.202.2.42]) by gateway1.nyi.mail.srv.osa (Postfix) with ESMTP id BDB4220360 for ; Thu, 24 Nov 2011 15:53:58 -0500 (EST) Received: from frontend2.nyi.mail.srv.osa ([10.202.2.161]) by compute2.internal (MEProxy); Thu, 24 Nov 2011 15:53:58 -0500 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=message-id:date:from:mime-version:to :subject:references:in-reply-to:content-type :content-transfer-encoding; s=smtpout; bh=QxylLV7sB+E0GkFHmns5te R+sbU=; b=O54ln3mwcD2Pf7LHZvoeFNaXfizNuuK3yvNN/lO4O9DkKWIBNkk/8D kMLLNnCgRg1JrwIKrIYuBTTPS+A3dn8RDFVdf3DjKuN2FHQn1YgV0b/N3te9669Z MXn8im3D+kYt3dSwCMUfmWXV5VR8+9sYhYbhcFnwKdAIbOMCbk2gc= X-Sasl-enc: MOuyCUtodbKgo6gMPN1NEt9Ly7WvZrTyIVynwAqCLSZh 1322168038 Received: from [192.168.42.6] (c-24-13-85-162.hsd1.il.comcast.net [24.13.85.162]) by mail.messagingengine.com (Postfix) with ESMTPSA id 797814824B1 for ; Thu, 24 Nov 2011 15:53:58 -0500 (EST) Message-ID: <4ECEAEE3.2010806@garfieldtech.com> Date: Thu, 24 Nov 2011 14:53:55 -0600 User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.23) Gecko/20110922 Thunderbird/3.1.15 MIME-Version: 1.0 To: internals@lists.php.net References: In-Reply-To: Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] Thoughts On Implementing Decorator Sugar to core From: larry@garfieldtech.com (Larry Garfield) On 11/23/2011 09:24 AM, Anthony Ferrara wrote: *snip* > Now, with traits we could add a trait for each interface which proxies > back to `$this->object`. But that's still a lot of duplication and > hard-coding. I've run into this issue before myself. It's especially a problem when you're specifying a parameter type, because that means you can't just use __call(). It's also a problem then when you are stacking decorators. class A implements Foo { // a dozen methods from Foo } class B implements Foo { // A dozen methods from Foo, all of which just call $this->a->X(); public function newStuff() {} } class C implements Foo { // A dozen methods from Foo, all of which just call $this->a->X(); } $c = new C(new B(new A))); $c->newStuff(); // Fail, unless you use __call(), in which case you can't type hint on B. > So, I've been trying to think of a few methods to add syntactic sugar > to PHP to make this a lot easier. So here's my thoughts > > Option 1 > > Add a magic interface `\PHP\Decorator` which would declare a > `getDecoratedObject()` method. It would after construction call that > method to figure out what interfaces the decorated object uses. Then, > it would magically implement them (as above) on the class if they > weren't already implemented (overridden). That way the decorated > object could be resolved at runtime. I'm not sold on this concept as > it feels a bit too "magic" to me. > > class MyDecorator implements \PHP\Decorator { > protected $object; > public function __construct($object) { > $this->object = $object; > } > public function getDecoratedObject() { > return $this->object; > } > public function addedFunctionality() { > // blah > } > } I don't see this as too much magic; it's really no more magical than the Countable or Iteratable interfaces. "Funky cool language functionality triggered by an interface". The advantage here is that it would give you lots of flexibility as to what the object to decorate is; it could be one passed in, or one instantiated internally, or one extracted from an object that's passed in, etc. It would require documenting precisely when getDecoratedObject() is called. I'd recommend that it get called right after the constructor finishes. Alternatively, could it be an internal-only setter? Call ->setDecoratedObject() somewhere within the class and that's the object that gets magically passed through to. Not sure if that works with an interface, though, since we wouldn't want that to be public. > Option 2 > > Implement a magic interface `\PHP\Decorator` which would then allow > all declared interfaces to be satisfied by `__call`. This should > require the least change to the core, since the only real change > that's needed is in the core where it checks that the declared > interface is satisfied. > > class MyDecorator implements IteratorAggregate, Countable { > protected $object = null; > > public function __construct($object) { > $this->object = $object; > } > > public function __call($name, $args) { > return call_user_func_array(array($this->object, $name), $args); > } > > public function addedFunctionality() { > // blah > } > } I don't like this approach. One, it basically says "if I put this interface on my class, disable type checking". That defeats the purpose. It also has the problem that you can then only decorate one object (as noted in option 3). Also, stacking __call() and call_user_func_array() is one of the slowest things you can do in the language, in my testing, as well as making life much more difficult for people trying to debug code. > Option 3 > > Add syntax to the member declaration that lets you declare which > methods should be proxied to a member object. So something like this: > > class MyDecorator implements IteratorAggregate, Countable { > protected $object decorates { > public function getIterator(); > public function count(); > } > > public function __construct($object) { > $this->object = $object; > } > > public function addedFunctionality() { > // blah > } > } > > Then, at compile time the core could do a replace on the class to add > the proxy methods. This has a major advantage in that you could use > multiple classes to selectively satisfy an interface. So you could > actually use it to compose an object at compile time that proxies to > multiple objects. I like the flexibility here. It also seems consistent with the trait syntax, I think. My only question is if we'd want to allow a short-hand to say that $object satisfies all methods on a given interface. If your interface has a dozen methods, that's still a dozen lines of code you would need (and need to update if the interface ever changes). So maybe: class MyDecorator implements IteratorAggregate, Countable { protected $object decorates IteratorAggregate, { public function count(); } } (Or something.) > Personally, I like the explicitness of Option 3, but I could get > behind Option 2 as well. Option 1 feels a bit too magic for my > tastes. I could live with 1 or 3, but not 2. 3 seems the most robust, but needs some thinking through first. > Another thought, should a decorator be able to pass the type hint that > the decorated object can pass? For example, should `new > MyDecorator(new PDO)` be able to pass `__construct(PDO $pdo);`? If > so, how would that be handled? Would the `Decorates` line > automagically insert the decorator in the class hiearchy for type > checking only? Or is that a bad idea in general (I have a feeling it > is)... I'm not sure what you're suggesting here. Are you asking if a class should magically inherit the interfaces of an object it decorates? That's... I don't know if that would work, honestly. :-) It also goes to the inevitable tug-of-war behind being a strict language vs. a dynamic language. > What are your thoughts? > > Anthony Cautiously positive on the concept. --Larry Garfield