Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a decorator in PHP?

Tags:

oop

php

decorator

Suppose there is a class called "Class_A", it has a member function called "func".

I want the "func" to do some extra work by wrapping Class_A in a decorator class.

$worker = new Decorator(new Original());

Can someone give an example? I've never used OO with PHP.

Is the following version right?

class Decorator
{
    protected $jobs2do;

    public function __construct($string) {
        $this->jobs2do[] = $this->do;
    }

    public function do() {
        // ...
    }
}

The above code intends to put some extra work to a array.

like image 212
omg Avatar asked Jun 04 '09 03:06

omg


People also ask

What is a Decorator in PHP?

Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators. Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface.

What is Decorator in OOP?

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.

What is the use of a Decorator template?

Purpose. The decorator template has the tag <isreplace/> identifying where the decorated content shall be included. Typically, only one tag ( <isreplace/> ) is used in the decorator template. However, multiple or zero <isreplace/> tags can also be used.


3 Answers

I would suggest that you also create a unified interface (or even an abstract base class) for the decorators and the objects you want decorated.

To continue the above example provided you could have something like:

interface IDecoratedText {     public function __toString(); } 

Then of course modify both Text and LeetText to implement the interface.

class Text implements IDecoratedText { ...//same implementation as above }  class LeetText implements IDecoratedText {         protected $text;      public function __construct(IDecoratedText $text) {         $this->text = $text;     }      public function __toString() {         return str_replace(array('e', 'i', 'l', 't', 'o'), array(3, 1, 1, 7, 0), $this->text->toString());     }  } 

Why use an interface?

Because then you can add as many decorators as you like and be assured that each decorator (or object to be decorated) will have all the required functionality.

like image 155
catchdave Avatar answered Sep 21 '22 02:09

catchdave


That is pretty easy, especially in a dynamically typed language like PHP:

class Text {      protected $string;      /**      * @param string $string      */     public function __construct($string) {         $this->string = $string;     }      public function __toString() {         return $this->string;     } }  class LeetText {      protected $text;      /**      * @param Text $text A Text object.      */     public function __construct($text) {         $this->text = $text;     }      public function __toString() {         return strtr($this->text->__toString(), 'eilto', '31170');     } }  $text = new LeetText(new Text('Hello world')); echo $text; // H3110 w0r1d 

You may want to have a look at the wikipedia article, too.

like image 42
soulmerge Avatar answered Sep 22 '22 02:09

soulmerge


None of these answers implements Decorator properly and elegantly. mrmonkington's answer comes close, but you don't need to use reflection to enact the Decorator pattern in PHP. In another thread, @Gordon shows how to use a decorator for logging SOAP activity. Here's how he does it:

class SoapClientLogger
{
    protected $soapClient;

    // this is standard. Use your constuctor to set up a reference to the decorated object.
    public function __construct(SoapClient $client)
    {
        $this->soapClient = $client;
    }

    ... overridden and / or new methods here ...

    // route all other method calls directly to soapClient
    public function __call($method, $args)
    {
        // you could also add method_exists check here
        return call_user_func_array(array($this->soapClient, $method), $args);
    }
}

And he's a slight modification where you can pass the desired functionality to the constructor:

class Decorator {

    private $o;

    public function __construct($object, $function_name, $function) {
        $this->o = $object;
        $this->$function_name = $function;
    }
    public function __call($method, $args)
    {
        if (!method_exists($this->o, $method)) {
            throw new Exception("Undefined method $method attempt in the Url class here.");
        }
        return call_user_func_array(array($this->o, $method), $args);
    }   
}
like image 33
Darth Egregious Avatar answered Sep 23 '22 02:09

Darth Egregious