Decorator Design Pattern in PHP

In the Decorator pattern, a class will add functionality to another class, without changing the other classes' structure.

In this example, the Book class will have its title shown in different ways by the BookTitleDecorator and it's child classes BookTitleExclaimDecorator and BookTitleStarDecorator.

In my example I do this by having BookTitleDecorator make a copy of Book's title value, which is then changed for display. Depending on the implementation, it might be better to actually change the original object.

<?php

class Book {
    private $author;
    private $title;
    function __construct($title_in, $author_in) {
        $this->author = $author_in;
        $this->title  = $title_in;
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
    function getAuthorAndTitle() {
      return $this->getTitle().' by '.$this->getAuthor();
    }
}

class BookTitleDecorator {
    protected $book;
    protected $title;
    public function __construct(Book $book_in) {
        $this->book = $book_in;
        $this->resetTitle();
    }	
    //doing this so original object is not altered
    function resetTitle() {
        $this->title = $this->book->getTitle();
    }
    function showTitle() {
        return $this->title;
    }
}

class BookTitleExclaimDecorator extends BookTitleDecorator {
    private $btd;
    public function __construct(BookTitleDecorator $btd_in) {
        $this->btd = $btd_in;
    }
    function exclaimTitle() {
        $this->btd->title = "!" . $this->btd->title . "!";
    }
}

class BookTitleStarDecorator extends BookTitleDecorator {
    private $btd;
    public function __construct(BookTitleDecorator $btd_in) {
        $this->btd = $btd_in;
    }
    function starTitle() {
        $this->btd->title = Str_replace(" ","*",$this->btd->title);
    }
}

  writeln('BEGIN TESTING DECORATOR PATTERN');
  writeln('');

  $patternBook = new Book('Gamma, Helm, Johnson, and Vlissides', 'Design Patterns');
 
  $decorator = new BookTitleDecorator($patternBook);
  $starDecorator = new BookTitleStarDecorator($decorator);
  $exclaimDecorator = new BookTitleExclaimDecorator($decorator);
 
  writeln('showing title : ');
  writeln($decorator->showTitle());
  writeln('');
 
  writeln('showing title after two exclaims added : ');
  $exclaimDecorator->exclaimTitle();
  $exclaimDecorator->exclaimTitle();
  writeln($decorator->showTitle());
  writeln('');
 
  writeln('showing title after star added : ');
  $starDecorator->starTitle();
  writeln($decorator->showTitle());
  writeln('');
 
  writeln('showing title after reset: ');
  writeln($decorator->resetTitle());
  writeln($decorator->showTitle());
  writeln('');

  writeln('END TESTING DECORATOR PATTERN');

  function writeln($line_in) {
    echo $line_in."<br/>";
  }

?>

Output

BEGIN TESTING DECORATOR PATTERN

showing title : 
Design Patterns

showing title after two exclaims added : 
!!Design Patterns!!

showing title after star added : 
!!Design*Patterns!!

showing title after reset: 
Design Patterns

END TESTING DECORATOR PATTERN

Code examples