Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:62117 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 94845 invoked from network); 12 Aug 2012 18:17:27 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 12 Aug 2012 18:17:27 -0000 Authentication-Results: pb1.pair.com smtp.mail=ircmaxell@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=ircmaxell@gmail.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.216.170 as permitted sender) X-PHP-List-Original-Sender: ircmaxell@gmail.com X-Host-Fingerprint: 209.85.216.170 mail-qc0-f170.google.com Received: from [209.85.216.170] ([209.85.216.170:44518] helo=mail-qc0-f170.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 2A/97-00812-733F7205 for ; Sun, 12 Aug 2012 14:17:27 -0400 Received: by qcmt36 with SMTP id t36so2061320qcm.29 for ; Sun, 12 Aug 2012 11:17:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:date:message-id:subject:from:to:content-type; bh=4eaj7dgc78pxhrJ5X6LlGwVrEOaNBhrZkroPLhCoifs=; b=kSku4gdfjQBXRYUAFCdCituiaEskUrzmFBTDAfJjAXd1PwGxvXRyIINuuXpAnVVkxY q0ZFpKnt4hSwSMceMwI1mjaKlP/cXSf+Zqy94kX+t3FUPQ4K/zy4Wah7SUZOSZkDpWRs U2TxYZ9rFs9BJTh9n56BhH76HflZdUiVJjUXx46dKVmypesUyrPnSkS08N9Q2Ajvqjhh mEFAwqoWIHoDypWuCkLb89vhtBHNGBsAAJOCRmQL4as3u1GptbJqsuxULDLGKCfeXgdM RC+ZAWeVl0vVlmwW8HIWPiL6hxufriswenTrDItzozItSa4lpvbG1Sq+x5/CchvOD49e rHuA== MIME-Version: 1.0 Received: by 10.224.213.194 with SMTP id gx2mr20330385qab.11.1344795444080; Sun, 12 Aug 2012 11:17:24 -0700 (PDT) Received: by 10.229.54.213 with HTTP; Sun, 12 Aug 2012 11:17:24 -0700 (PDT) Date: Sun, 12 Aug 2012 14:17:24 -0400 Message-ID: To: internals@lists.php.net Content-Type: multipart/alternative; boundary=20cf30051498bd71ba04c71596c1 Subject: Decorators Revisited From: ircmaxell@gmail.com (Anthony Ferrara) --20cf30051498bd71ba04c71596c1 Content-Type: text/plain; charset=ISO-8859-1 Hey all I've posted before about adding the ability to do dynamic decorators before. I think I have come up with a method to do so in core. Basically, the problem is that I can't create a decorator for a class at all. I would have to extend that class (with all of the coupling issues that brings). I can create a decorator for interfaces, but I'd need to list proxy methods for each and every interface combination in each decorator. This leads to tons of boilerplate issues. For example: class CachableFooDecorator implements foo { protected $parent; public function __construct(Foo $obj) { $this->parent = $obj; } public function __call($method, $args) { return call_user_func_array(array($this->parent, $method), $args); } public function method1($a, $b) { return $this->parent->method1($a, $b); } public function method2($a) { if (!$this->hasCache('method2', $a)) { $ret = $this->parent->method2($a); $this->setCache('method2', $a, $ret); } return $this->getCache('method2', $a); } } That's a lot of boilerplate for each possible iteration. This is one reason people like traits so much, as it's easier to just do automated copy/paste than use the proper patterns. So, I've wanted to add a dynamic way of being able to decorate classes at the core level. So you'd declare the class in a special way and it would handle that boilerplate for you. I've come up with a method to do so. Basically, I created a class SplDecorator which looks like this: class SplDecorator { private $parent; public function __construct($obj) { $this->parent = $obj; } public function __call($method, $args) { return call_user_func_array(array($this->parent, $method), $args); } public function getDecoratedObject() { return $this->parent; } } The other difference, is there's an object creation handler which adds the parent's class entry to the current instances interface list. Basically: Z_OBJCE_P(this)->interfaces = safe_realloc(Z_OBJCE_P(object)->interfaces, Z_OBJCE_P(object)->num_interfaces + 1, sizeof(zend_class_entry), 0); Z_OPJCE_P(object)->interfaces[Z_OBJCE_P(object)->num_interfaces] = Z_OBJCE_P(parent); Z_OBJCE_P(object)->num_interfaces++; Now, the internal instanceof function is recursive, so it will return true when tested against this class list. So, example code like: class Foo {} class Bar extends SplDecorator {} $b = new Bar(new Foo); var_dump($b instanceof Foo); // true It also works with type hints: function test(Foo $f) {} test($b); Now, there's a lot more to do (property cascading, interface validation, etc), but the initial proof-of-concept is there. I have it working locally, I'll push it to github on my branch in a day or two so that you can play around with it... What do you think? Is this a route that I should continue down? Or is there something fundamental that I'm missing here? I know that Reflection, get_interfaces(), etc would need to be updated to account for this. Thoughts? Anthony --20cf30051498bd71ba04c71596c1--