Decorating PHP classes without a common ancestor

The decorator pattern is commonly described as a pair of classes—a component and a decorator—which derive from a common ancestor. Using language-level features in PHP, we can relax this restriction and still reap the benefits of runtime object-behavior modification.

To change the functionality of the component, the decorator redefines one or more methods. Within these methods, the original behavior is wrapped, extended, and the result is passed back through to the decorated class. All other method calls are automatically passed through to the component class e-sigarett

There are situations where it just doesn’t make sense to include the decorator in the same inheritance hierarchy as the decorated class. For example, I may want my decorator class to inherit from some other class. This poses a problem, because PHP doesn’t support multiple inheritance. In PHP, this limitation can be overcome through some (admittedly gnarly) use of the built-in call_user_func_array function in conjunction with the __call magic method.

Take the Textile markup library, for example. It’s an extremely useful library, and the PHP implementation is well done. For better or worse, it’s all wrapped up into a single class and operates as a unit with a single entry and exit point. All of its methods are public, so we could simply inherit fromTextile and overwrite where appropriate. However, this would require us to continue subclassing for every single decoration we apply. Decorating should be agnostic to the class hierarchy; we can add any decorators we want to specific instances of the class and leave other decorators off other instances.

<?php
class Textile {
function Textile() {
// lots of constructor-y stuff
}
function TextileThis( $text, $lite=, $encode=, $noimage=, $strict=, $rel= ) {
// text is inputted here, and then returned–after filtering–from here
}
/**
* Lots of other methods, most of which should never be called in isolation
*/
}
view rawtextile-2.0.php hosted with ❤ by GitHub

In sextile, for example, I decorate the Textile class in order to add LaTeX compatibility at runtime. Originally, I went into the Textile class itself and hard-coded my own rule. This worked great, until the next version of Textile was released. In order to use the new features, I would have had to delve into the new code and find out how to add my exception in again. Notice that this is going to be a continual problem for me. Whenever the thing I modify releases a new version, I’m forced to go in and update my code. That smells bad.

By moving the LaTeX-handling code out into a decorator, I’m now able to drop different versions of Textile in without worrying about it—at least provided that the markup mechanism for images hasn’t changed between releases (and that the public interface hasn’t changed).

<?php
class sextile {
protected $textile;
public function TextileThis( $text, $lite = , $encode = , $noimage = , $strict = , $rel = ) {
return $this->textile->TextileThis( $this->handle_latex( $text ), $lite, $encode, $noimage, $strict, $rel );
}
/**
* Spoof interface compatibility
*/
public function __call( $name, $params ) {
$ret = call_user_func_array( array( $this->textile, $name ), $params );
if( $ret === false )
throw new BadMethodCallException(Method {$name} does not exist in class Textile);
return $ret;
}
public function __construct( Textile $textile ) { /* … */ }
private function handle_latex( $text ){ /* … */ }
}
view rawsextile-excerpt.php hosted with ❤ by GitHub

Of course, the alternative to this would be a situation where sextile simply inherits from Textile. Then, the TextileThis method would just be a call toparent::TextileThis(...) with the wrapper applied to the first parameter. If I wanted to add an additional decorator—modifying hyperlinks, for example—I would have to subclass in a similar manner. Notice that this kind of behavior wouldn’t be unheard of in, for example, a wiki environment where mathematical markup is needed, and all relative links (but maybe not actually relative to the place the link originates) should go to a predefined location. Decorating allows me to plug in these bits of functionality as needed, independently of one another.

So what is this about not having a common ancestor? Well, one requirement of the decorator pattern is that both classes implement the same interface. In the example above, there were two options: create a Textile subclass to serve as the common parent class for all possible decorators. This would be like introducing a common ancestor into the inheritance hierarchy. Useful, yes, but it would add an extra class to the hierarchy. We can use the “flexibility” of PHP to achieve the same thing while only adding a single class.

The call_user_func_array function accepts two parameters: a callback and an array of parameters to pass to that function. This means that we can call any method at runtime, provided that we have its name and an instance of its containing class. In conjunction with the __call magic method available to classes in PHP5, we can intercept method calls to an object and redirect them anywhere we please. That means that with a little magic, sextile andTextile—although they have different interfaces—can serve the same purpose in any situation. This is great for decoration because it allows us to pass non-decorated method calls through to the concrete instance of the decorated class. If one were interested in decorating all methods of a class with a common functionality change, this would be a huge benefit. For example, adding logging at runtime.

One thing to remember about using __call in combination with call_user_func_array would be that type hints and calls to the instanceof operator will fail when comparing instances of class Textile to instances of class sextile. This is a negative consequence of forgoing a common ancestor.

Note that by forgoing a common parent for the decorator classes, we are forced to add this __call method to every single decorator. This would constitute a pretty clear violation of the DRY principle, because the same code would exist in multiple classes. I would argue for using your marginal instinct in this case; if you have a handful of decorators, the benefits of duplication might outweigh the costs of maintaining another class. In the case of many possible decorators, the smelly code might merit a refactoring to a higher place in the hierarchy. Personally I think my balance point would be around 2-3 decorators.