Anthony Ferrara | 12 Aug 2012 20:17
Picon
Gravatar

[PHP-DEV] Decorators Revisited

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);
    }
}
(Continue reading)

Jordi Boggiano | 13 Aug 2012 21:10
Picon
Favicon
Gravatar

Re: [PHP-DEV] Decorators Revisited

Heya,

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

I can't speak for the feasibility, but from a usage point of view I do
like that it opens up some sort of duck-typing by allowing decorators to
be drop-in replacements even if there is no interface to implement.

Cheers

-- 
Jordi Boggiano
 <at> seldaek - http://nelm.io/jordi

--

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

Benjamin Eberlei | 14 Aug 2012 00:24
Picon
Gravatar

Re: [PHP-DEV] Decorators Revisited

Just seeing my previous reply only went to Anthony in person. so i have to
repeat here:

Its an awesome idea, i really like it :)

On Mon, Aug 13, 2012 at 9:10 PM, Jordi Boggiano <j.boggiano <at> seld.be> wrote:

> Heya,
>
> > 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.
>
> I can't speak for the feasibility, but from a usage point of view I do
> like that it opens up some sort of duck-typing by allowing decorators to
> be drop-in replacements even if there is no interface to implement.
>
> Cheers
>
> --
> Jordi Boggiano
>  <at> seldaek - http://nelm.io/jordi
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>
(Continue reading)

Nils Adermann | 14 Aug 2012 01:16
Picon
Favicon
Gravatar

Re: [PHP-DEV] Decorators Revisited

On 08/12/2012 08:17 PM, Anthony Ferrara wrote:
> Thoughts?
Yes, please. I think this would be a great simplification for all kinds
of extensibility / plugin mechanisms implemented in various PHP projects.

Typically these either end up with the boilerplate you described or try
to come up with some kind of hack (e.g. dynamic code generation) to
circumvent the problem of having to explicitly define parent classes if
one wants to dynamically decorate objects multiple times while maintaing
interfaces.

Cheers,
Nils

--

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

Nicholas Curtis | 14 Aug 2012 01:50
Picon
Gravatar

Re: [PHP-DEV] Decorators Revisited

I agree great idea. Would really help to simplify benchmarking code also.

On Mon, Aug 13, 2012 at 4:16 PM, Nils Adermann <naderman <at> naderman.de> wrote:

> On 08/12/2012 08:17 PM, Anthony Ferrara wrote:
> > Thoughts?
> Yes, please. I think this would be a great simplification for all kinds
> of extensibility / plugin mechanisms implemented in various PHP projects.
>
> Typically these either end up with the boilerplate you described or try
> to come up with some kind of hack (e.g. dynamic code generation) to
> circumvent the problem of having to explicitly define parent classes if
> one wants to dynamically decorate objects multiple times while maintaing
> interfaces.
>
> Cheers,
> Nils
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>
Anthony Ferrara | 14 Aug 2012 02:16
Picon
Gravatar

Re: [PHP-DEV] Decorators Revisited

All,

As promised, here's a link to the proof-of-concept implementation:
https://github.com/ircmaxell/php-src/blob/spl_decorator/ext/spl/spl_decorator.c#L77

I'm not really looking for feedback about whether or not we should
implement a decorator helper, but more for this exact method of doing it
(having a "magic" base class which would be extended)...

The interface addition is mainly a quick tool to test it with. I would
think that's too dirty to go with in an official proposal.

In the official proposal I could foresee two main ways of solving it:

1. Add an additional pointer to the zend_class_entry for the wrapped class.
Then change instanceof and the like to check for that pointer as well.

2. Move instanceof to a handler from the current standalone function. Then
each (pecl level) class could choose its own implementation if it is
needed. The function could still exist for BC reasons, but would proxy to
the handler method if it existed... Then, the wrapped class (the additional
pointer) would only need to live on the objects internal store...

Thanks,

Anthony

On Mon, Aug 13, 2012 at 7:50 PM, Nicholas Curtis <nich.curtis <at> gmail.com>wrote:

> I agree great idea. Would really help to simplify benchmarking code also.
(Continue reading)

Stas Malyshev | 14 Aug 2012 08:36
Favicon
Gravatar

Re: [PHP-DEV] Decorators Revisited

Hi!

> 2. Move instanceof to a handler from the current standalone function. Then
> each (pecl level) class could choose its own implementation if it is
> needed. The function could still exist for BC reasons, but would proxy to
> the handler method if it existed... Then, the wrapped class (the additional
> pointer) would only need to live on the objects internal store...

I don't think overriding instanceof is a very good idea. instanceof has
very defined meaning, which implies certain contract between classes and
their clients, and allowing everybody to override it to mean whatever
one likes looks very dangerous to me.

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

Benjamin Eberlei | 14 Aug 2012 10:52
Picon
Gravatar

Re: [PHP-DEV] Decorators Revisited

On Tue, Aug 14, 2012 at 8:36 AM, Stas Malyshev <smalyshev <at> sugarcrm.com>wrote:

> Hi!
>
> > 2. Move instanceof to a handler from the current standalone function.
> Then
> > each (pecl level) class could choose its own implementation if it is
> > needed. The function could still exist for BC reasons, but would proxy to
> > the handler method if it existed... Then, the wrapped class (the
> additional
> > pointer) would only need to live on the objects internal store...
>
> I don't think overriding instanceof is a very good idea. instanceof has
> very defined meaning, which implies certain contract between classes and
> their clients, and allowing everybody to override it to mean whatever
> one likes looks very dangerous to me.
>

I think you are excagerating, the proposal is not allowing everybody to
overwrite instanceof it to do whatever. That would be if we could overwrite
the implementation of instanceof in userland.
Instead it simplifies applying the decorator pattern on the language level,
and the instanceof only in this use-case.

 <at> Anthony: Does this handle get_class() as well and all the Reflection APIs?
Stan Vass | 14 Aug 2012 12:22

Re: [PHP-DEV] Decorators Revisited

Would this work with methods which take arguments by reference or return by 
reference?

Stan 

--

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

Pierre Joye | 14 Aug 2012 13:59
Picon
Gravatar

Re: [PHP-DEV] Decorators Revisited

hi Anthony,

On Sun, Aug 12, 2012 at 8:17 PM, Anthony Ferrara <ircmaxell <at> gmail.com> wrote:

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

I very much like this idea! And I'm convinced that more
tests/efforts/thoughts should be put into this.

One point I worry about is performance. As of 5.3 & 5.4, the object
usage performance has been significantly improved. I wonder what would
be the impact of this change on this gain. Did you look at that
already? Surely too early but still good to get an idea or to keep
this parameter in mind:).

Keep the great work!

Cheers,
-- 
Pierre

 <at> pierrejoye | http://blog.thepimp.net | http://www.libgd.org

--

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

Ralph Schindler | 14 Aug 2012 18:49
Favicon
Gravatar

Re: [PHP-DEV] Decorators Revisited

In general, I think it would be nice to have something that does this 
for you, but I am not necessarily a fan of changing the meaning of 
instanceof.

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

Couldn't a dynamic trait be a better?  It would work like this:

trait SplDynamicProxyTrait {

   protected $proxyObject;

   // enumerate all public method with the following:
   public function $name($signature) {
     return $this->proxyObject::$name($signature);
   }

}

usage would then be the following:

class MyDecorator implements FooInterface {
   use SplDynamicProxyTrait; // compile time "code generation"
   public function __construct(Foo $foo) {
      $this->proxyObject = $foo;
   }
   // override trait
   public function method2($a) {
(Continue reading)

Anthony Ferrara | 14 Aug 2012 20:35
Picon
Gravatar

Re: [PHP-DEV] Decorators Revisited

Ralph,

On Tue, Aug 14, 2012 at 12:49 PM, Ralph Schindler
<ralph <at> ralphschindler.com>wrote:

> In general, I think it would be nice to have something that does this for
> you, but I am not necessarily a fan of changing the meaning of instanceof.
>
>
>  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.
>>
>
> Couldn't a dynamic trait be a better?  It would work like this:
>
> trait SplDynamicProxyTrait {
>
>   protected $proxyObject;
>
>   // enumerate all public method with the following:
>   public function $name($signature) {
>     return $this->proxyObject::$name($**signature);
>   }
>
> }
>
> usage would then be the following:
>
(Continue reading)

Ralph Schindler | 14 Aug 2012 21:30
Favicon
Gravatar

Re: [PHP-DEV] Decorators Revisited


> Well, I like it at first glance. There are two main problems that I see
> with it:
>
> 1. It still requires a separate decorator class for every combination of
> interfaces you want to decorate. So, for example if you wanted to
> decorate a Foo interface, but sometimes it's used with Iterator as well.
> Then you'd need two classes. If there was a third interface, you'd need
> four separate classes. Same problem with class bloat minus a lot of the
> boilerplate.

You'd need a class anyway for your custom logic to live, that custom 
logic is the reason you want a Proxy in the first place.  In your 
example, you want to proxy and object, but for one of the method, 
presumable an expensive method, you want to be able to cache the output 
before needing to go to the actual object.

If Iterator was required for the Foo contract/interface, that would have 
bee in the Foo interface definition as well.

Even in your example, you still have this:

   class Bar extends SplDecorator {}

But what you are really intending is:

   class Bar extends MagicToMakeThisActLikeAnyType {}

Ultimately, the instance you are decorating and decorator itself need to 
be "type equivalent".  Meaning the generally accepted understanding of 
(Continue reading)

Stas Malyshev | 14 Aug 2012 21:58
Favicon
Gravatar

Re: [PHP-DEV] Decorators Revisited

Hi!

> Simply because your object responds to all the same methods of, for 
> example, the FooInterface, does not make it a FooInterface subtype.  It 
> just means that in the "duck typing" sense of the phrase, it can act 
> like a FooInterface for people that are not necessarily concerned that 
> it's actually not is_a() FooInterface.

Excellent point here. I have a feeling that with these proposals people
want to eat a cake and have it too. To have strictly defined typing
structure enforced by strict parameter checks, instanceof checks, etc.
and at the same time have the freedom of duck typing. I don't think it's
going to work well - if you want duck typing, that's one thing, if you
want class hierarchy, that's another thing. Both are viable models for
different cases, but I don't see how they can work using the same
operators and language constructs. They should be distinct.

Now, we could probably make duck typing a bit easier by allowing to
check if specific object can respond to specific interface. But I'm not
sure if it's worth the effort - why not just have it implement the
interface then? In any case, I think duck typing improvement may be a
good place for proposals, but let's not confuse it with inheritance
hierarchy.
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

--

-- 
PHP Internals - PHP Runtime Development Mailing List
(Continue reading)

Andrew Faulds | 15 Aug 2012 12:40

Re: [PHP-DEV] Decorators Revisited


On 14 August 2012 at 20:58 Stas Malyshev <smalyshev <at> sugarcrm.com> wrote:

> Hi!
>
> > Simply because your object responds to all the same methods of, for
> > example, the FooInterface, does not make it a FooInterface subtype.  It
> > just means that in the "duck typing" sense of the phrase, it can act
> > like a FooInterface for people that are not necessarily concerned that
> > it's actually not is_a() FooInterface.
>
> Excellent point here. I have a feeling that with these proposals people
> want to eat a cake and have it too. To have strictly defined typing
> structure enforced by strict parameter checks, instanceof checks, etc.
> and at the same time have the freedom of duck typing. I don't think it's
> going to work well - if you want duck typing, that's one thing, if you
> want class hierarchy, that's another thing. Both are viable models for
> different cases, but I don't see how they can work using the same
> operators and language constructs. They should be distinct.
>

This is something which bothers me about PHP as it is at the moment. PHP seems
to me like it should be a dynamic language, and it is quite dynamic: no static
typing, has weak typing, you can add instance members at runtime, etc.

However, there has been a trend for adding more... non-dynamic (?) features to
the language. Interfaces for example. Yet you can also duck-type things. I think
PHP risks becoming very unclear as to what the "right" way to do things is. It
must be confusing to write code that has to deal with both duck-typing and
interfaces. I just think perhaps PHP should decide if it's supposed to be
(Continue reading)

Anthony Ferrara | 14 Aug 2012 23:03
Picon
Gravatar

Re: [PHP-DEV] Decorators Revisited

Ralph,

On Tue, Aug 14, 2012 at 3:30 PM, Ralph Schindler
<ralph <at> ralphschindler.com>wrote:

>
>  Well, I like it at first glance. There are two main problems that I see
>> with it:
>>
>> 1. It still requires a separate decorator class for every combination of
>> interfaces you want to decorate. So, for example if you wanted to
>> decorate a Foo interface, but sometimes it's used with Iterator as well.
>> Then you'd need two classes. If there was a third interface, you'd need
>> four separate classes. Same problem with class bloat minus a lot of the
>> boilerplate.
>>
>
> You'd need a class anyway for your custom logic to live, that custom logic
> is the reason you want a Proxy in the first place.  In your example, you
> want to proxy and object, but for one of the method, presumable an
> expensive method, you want to be able to cache the output before needing to
> go to the actual object.
>
> If Iterator was required for the Foo contract/interface, that would have
> bee in the Foo interface definition as well.
>

No, it would be required by the class itself. That's the point. I don't
want to decorate an interface, but an instance. Otherwise it makes
decorating real world classes much more difficult (which was the pain I'm
(Continue reading)

Ralph Schindler | 15 Aug 2012 01:18
Favicon
Gravatar

Re: [PHP-DEV] Decorators Revisited

Hey Anthony,

There's a lot here, so I'm only going to address a few things.

> It sounds to me like you haven't tried to use decorators for any complex
> logic. Making it type equivalent leads to very vebose code. And a PITA

I actually have used both Decorators and Proxies (it's cousin) a number 
of times and some in complex situations (these are all ZF2) 
http://git.io/HdRe6g (Zend\Di), http://git.io/6EGv4Q 
(Zend\ServiceManager), and http://git.io/DhXDFA (Zend\Db Sql 
Abstraction), all that aside ...

> to write. This is one of the reasons that traits are so heralded.
> Because problems that are easy to solve with decorators (in general) are
> difficult to solve with PHP, so people wind up writing copy/paste

If what you're trying to do is decorate an instance of a particular 
concrete type, the basic structure/foundation for creating a Decorator 
(as defined by wikipedia), can be achieved in PHP with about 3 lines of 
code:

class FooDecorator extends Foo {
     protected $subject;
     public function __construct(Foo $foo) {
         $this->subject = $foo;
         foreach (get_object_vars($foo) as $n => $v) {
             $this->{$n} =& $foo->{$n};
         }
     }
(Continue reading)

Anthony Ferrara | 15 Aug 2012 02:23
Picon
Gravatar

Re: [PHP-DEV] Decorators Revisited

Ralph,

to write. This is one of the reasons that traits are so heralded.
>> Because problems that are easy to solve with decorators (in general) are
>> difficult to solve with PHP, so people wind up writing copy/paste
>>
>
> If what you're trying to do is decorate an instance of a particular
> concrete type, the basic structure/foundation for creating a Decorator (as
> defined by wikipedia), can be achieved in PHP with about 3 lines of code:
>
> class FooDecorator extends Foo {
>     protected $subject;
>     public function __construct(Foo $foo) {
>         $this->subject = $foo;
>         foreach (get_object_vars($foo) as $n => $v) {
>             $this->{$n} =& $foo->{$n};
>         }
>     }
> }
>
> At this point, any instance of new Foo is statefully and type equivalent
> to new FooDecorator($foo).  Now, you're free to add in your custom
> decoration logic, overridden methods, additional methods, new interfaces,
> etc.  An instance of FooDecorator can be used an any place an instance of
> Foo would have been used.  i.e., it's bound to the Foo context.

That's an alternative? Really? Some hackery involving inheritance abuse...?

Let me pose a real world example.
(Continue reading)


Gmane