As it’s that time of year where small green trees gain functionality through lavish application of colored glass and plastic, it’s time to take another look at the Decorator pattern.

The Need for Decoration

Back in January of 2003, made a first attempt at explaining the Decorator pattern and have often felt since that I failed to do it justice, taking an overly literal implementation from the GoF Design Patterns which was poorly suited to PHP (that said, I’ll assume here you’ve read that discussion and build on it).

Since then I’ve had the opportunity to work on libraries which have resulted in three practical examples of Decorators and have become impressed with the idea that it has a deep significance for PHP. The libraries in question are PEAR::XML_HTMLSax (a pure PHP SAX parser capable of handling badly formed XML), PEAR::Calendar (a library for generating Calendar data structures) and WACT (an all round library of “missing pieces” for PHP).

As mentioned on the WACT Wiki under PHP Application Design Concerns and as anyone who’s been around PHP for a while knows well, PHP is “compiled” and executed at runtime on every request. Sterling Hughes and Thies C. Arntzen discuss the mechanics in detail in PHP Inside Out while John Lim does an excellent job of explaining how to tweak more out of PHP in A HOWTO on Optimizing PHP.

Now the assumption I make is that 95% (at least) of PHP‘s users run their apps in an environment they don’t control (shared hosting) without the benefit of some kind of script cache, such as Zend Performance Suite or Turck MMCache. What’s significant about this is if you’re effectively including your entire class library on every page request, while only 0.1% of it is actually being used for that request, you’ve added a significant “compiling” overhead needlessly. That’s where the Decorator pattern can prove extremely valuable (but be aware this is not a call to PHP developers to attempt to apply Decorators blindly to everything).

Designing for Probable Use

When developing a single “component” for a library, it’s likely that class (classes) has a specific probable intended purpose. Code which uses the class will, 90% of the time, only be using it for the specific “probable” functionality it offers.

The temptation of course, for library designers, is to throw in functionality for every possible use case imaginable. The danger in adding the kitchen sink to your “component” for PHP is two-fold.

First there’s the general issue of ending up with hard-to-maintain, bloated classes that end up boggling the minds of both the original developers and those who are trying to understand and use them.

Second, for PHP, there’s the associated overhead of “compiling” a massive library on every page request, causing a needless slowdown in performance.

By using a Decorator, the “improbable use functionality” can be provided as a separate PHP include for those that need it, without passing on the overhead to users who are only interested only in the “probable use”.

Implementing Decorators in PHP

Going back to the first discussion of Decorators on this site and following various experiences with Decorators this year, the most effective approach to implementing decorators in PHP seems to be to have the base Decorator as the start of a separate class hierarchy, while mirroring the complete API of the class it’s decorating, “routing” calls to an instance of the decorated class the decorator received on instantiation.

Concrete decoration is then simply a matter of extending the base Decorator. This approach is slightly different to the Gang of Four implementation, where the Decorator extends the class it is decorating, but has proved the easiest to implement in PHP.

A quick trivial example (some class for analysing a sentence);

<?php
// wordcount.php
class WordCount {
	var $sentence = '';
	function setSentence($sentence) {
		$this->sentence = $sentence;
	}
	function getWordCount() {
		$words = explode(' ',$this->sentence);
		return count($words);
	}
}
?>

The WordCount class is for the “probable use”. This is what the majority of users will be interested in. To apply a decorator to WordCount, I make sure to use a separate file;

<?php
// wordcountdecorator.php
class WordCountDecorator {
	var $WordCount;
	function WordCountDecorator(& $WordCount) {
		$this->WordCount = & $WordCount;
	}
	function setSentence($sentence) {
		$this->WordCount->setSentence($sentence);
	}
	function getWordCount() {
		return $this->WordCount->getWordCount();
	}
}
?>

The base decorator is essentially “dumb” - it just passes on any calls to the WordCount instance it was given when instantiated.

Now I can extend this with a concrete decorator, designed purely for counting the number of times PHP appears in a sentence;

<?php
// phpcount.php
require_once 'wordcount/wordcountdecorator.php';
class PHPCount extends WordCountDecorator {
	function PHPCount(& $WordCount) {
		parent::WordCountDecorator($WordCount);
	}
	function getWordCount() {
		$matches = array();
		return preg_match_all ("/php/i",$this->WordCount->sentence,$matches);
	}
}
?>

The PHPCount class is clearly only going to be interesting to a small number of users. It’s kept in a separate file from the base decorator as well because I don’t know how may concrete decorators I’ll be implementing (there might be a lot), so bundling the lot in a single file is again going to be overhead.

Using these classes might look like;

<?php
// The probably use case...
require_once 'wordcount/wordcount.php';

$wc = new WordCount();
$text = "You can also see how popular PHP is relative to other Apache modules
		 at SecuritySpace's Web Survey. Spoiler: PHP is the most popular.";
$wc->setSentence($text);

echo "The text contains ".$wc->getWordCount()." words<br>";

// The improbable use case...
require_once 'wordcount/phpcount.php';

$pc = new PHPCount($wc);

echo "The word PHP appears ".$pc->getWordCount()." times.";
?>

Note the above is not the only way to implement Decorators in PHP. If you take the loosest sense of a Decorator pattern as just being a class which adds functionality to another without directly extending it, there are many ways to go.

A Quick Look at HTMLSax

The PEAR::XML_HTMLSax Decorator implementation (and much more of HTMLSax 2.x+) is thanks to Jeff Moore, lead developer of WACT. HTMLSax, from the point of view of end users, is very much like working with PHP‘s native Expat SAX parser (although HTMLSax only provides the OO API in common with PHP-Expat).

When users specify their “SAX handlers” with HTMLSax, behind the scenes HTMLSax makes (referenced) copies of the handler object those methods reside within and calls those instances when specific XML events (e.g. the opening or closing of a tag) occur. Decorators are used to handle specific “XML options” such as whether to convert XML entities to their equivalent characters and work by “wrapping” the user specified handler object. HTMLSax doesn’t save on the parsing overhead (currently) by using decorators but does cut out a significant processing overhead and make help make HTMLSax maintainable. The alternative would be some large if/else condition to examine the XML options the user has specified and respond accordingly, the if condition having to be examined on every XML event (slow).

HTMLSax as a whole takes some understanding / explaining so I’ll leave it to you to explore the code if you’re interested.

Data Driven Programming

Simon Willison’s recently blogged a nice example of how data driven programming can be used as an alternative to “strict” OOP in How not to use OOP. In the The Art of Unix Programming, Eric Raymond discusses Data-Driven Programming further, FYI.

There’s a fundamental relationship here between the general notions of data driven programming and the idea of database-driven websites. Perhaps the most common “process” in web apps, whatever the language, is to select a set of results from a database then iterate over them, perhaps rendering an HTML table as you go.

If it’s possible to manipulate the data set before (or better yet as) it’s being iterated over, there’s no need modify the code that renders the table. There’s a big discussion here I’ll leave for another time and stick to discussion of Decorators today but it’s interesting to consider the “common tasks” web developers have to perform and how it shapes web based applications.

At the point where we prepare a data structure from a database, if, instead of passing on a “raw array”, we work on the basis of “wrapping” each row in an object to begin with and we make sure the code which uses the data structure knows about this “row API” then it becomes easy to Decorate the rows with additional functionality.

PEAR::Calendar

One example of a combination of data structures and decorators is PEAR::Calendar. PEAR::Calendar is a library for generating calendars as data structures, without tying the users of the class to any particular output format. The data structures themselves allow users to render a calendar on (say) a web page in much the same fashion as generating a table from a query result set. I’ll leave it to the documentation to explain the specifics, in particular the In a Hurry example (note there’s more examples of PEAR::Calendar 0.3 online here if you’re interested).

What the example shows is how you can generate a calendar using a “result set” iterator (the fetch() method) to get object representing a row, from which further information can be obtained.

The base class Calendar of PEAR::Calendar plus it’s associated subclasses, which represent individual calendar units (such as a day or a month) already come packing a fair amount of functionality. At the same time, given the nature of the package, there’s clearly going to be many different problems people want to apply it to and adding methods to support them with the core collection of calendar classes is asking for trouble.

That’s where Decorators come in, as explained here. PEAR::Calendar provides a class Calendar_Decorator (plus a few concrete decorators to help with some more obvious problems) which mirrors the complete API of the base Calendar class and all it’s direct subclasses. Decorators can be applied “upstream” to the calendar data structure itself, rather then needing to modify the code which renders the calendar.

Because the concrete decorators are implemented as separate includes, no one needs to “pay the parsing price” for functionality they aren’t using. Also, given PHP4’s relaxed object model, users of PEAR::Calendar can extend Calendar_Decorator and be reasonably sure they’re not accidentally re-declaring some member variable, the only member of Calendar_Decorator being the variable $calendar into which the instance of “real” calendar being decorated is stored.

So I’m happy because I can implement new feature requests without messing with the (hopefully) stable core of PEAR::Calendar while people using it get only what they want without (significant) concern for what the performance overhead is (plus they have a structure for adding their own functionality without needing to update the core codebase).

WACT Record Sets

WACT, brainchild of Jeff Moore is a library I could rave about without end (release coming soon, honest!). One of (many of) Jeff’s brilliant stokes is WACT’s RecordSet implementation, which combines an iterator with an implicit API for accessing the elements within a row.

In other words, when dealing with a database result set, the WACT Record Set API allows you not only to loop over the set of rows but also to access the contents of each individual row while still “looping”.

The notion of the Record Set is one of the design patterns defined in Martin Fowler’s Patterns of Enterprise Application Architecture although Martin discusses it in terms of the entire Record Set being held in memory (bad news for PHP) while WACT relies on the database cursor instead, placing only a row at a time in memory.

Typically, when working with WACT, the PHP that actually renders the table won’t be written by you (rather it’s generated by WACT’s template engine) so this introduces the problem of how to manipulate the data between the points of data access and presentation. And how to do so while sticking to the principle of “one row in memory at a time please”? Enter decorator...

The WACT DataSet Decorator allows you to add functionality to any Record Set in WACT. The example (that link) suggests how it can be used to “reformat” data for output, changing a Unix timestamp to a human readable date. The power of the decorator doesn’t end there though - Jeff recently put it to use in implementing what is effectively a “subselect” for a database query (the DB in question being some pre-subselects version of MySQL) and, in theory, the DataSetDecorator could be the “launching point” for a Domain Model.

Because the functionality provided by any concrete decorator is only used when the Record Set it decorates is actually accessed, WACT is still able to restrict itself to one row of data at a time, keeping performance at the max while users enjoy the benefits of useful OO abstraction.

In Summary

My opinion is the Decorator pattern has a very deep significance to PHP. As decorating functionality is applied at runtime, it is “natural” pattern to PHP as a dynamically typed language with solid reflection functionality (e.g. get_class(). It allows provides a powerful mechanism to avoid code bloat and often delivers a more performant solution to a more “straightforward” procedural approach, where extensive if/else may be required. It also seems to fit well with the notion of “from database table to HTML table” so common in web applications. What’s more, if you’re developing libraries for other people to use, it provides a clear “dropping off point” for adding custom functionality without needing to mess with library internals. Obviously not all problems can have decorators usefully applied but by being aware of approaches to implementing decorators in PHP, you may find opportunities where it presents a better alternative to the first thing that came to mind.

Anyway, something to think about while hanging stuff on the tree. Merry Christmas and a Happy New Year!


design/the_decorator_pattern_redecorated.txt · Last modified: 2005/10/15 21:47