CHAPTER 12 ■ ENTERPRISE PATTERNS
You may wonder why this code takes it on trust that the Command class it locates does not require
parameters:
if ( $cmd_class->isSubClassOf( self::$base_cmd ) ) {
return $cmd_class->newInstance();
}
The answer to this lies in the signature of the Command class itself.
Namespace woo\command;
//...
abstract class Command {
final function __construct() { }
function execute( \woo\controller\Request $request ) {
$this->doExecute( $request );
}
abstract function doExecute( \woo\controller\Request $request );
}
By declaring the constructor method final, I make it impossible for a child class to override it. No
Command class, therefore, will ever require arguments to its constructor.
Remember that you should never use input from the user without checking it first. I have included a
test to ensure that there is no path element to the provided "cmd" string, so that only files in the correct
directory can be invoked (and not something like ../../../tmp/DodgyCommand.php). You can make code
even safer by only accepting command strings that match values in a configuration file.
When creating command classes, you should be careful to keep them as devoid of application logic
as you possibly can. As soon as they begin to do application-type stuff, you’ll find that they turn into a
kind of tangled transaction script, and duplication will soon creep in. Commands are a kind of relay
station: they should interpret a request, call into the domain to juggle some objects, and then lodge data
for the presentation layer. As soon as they begin to do anything more complicated than this, it’s
probably time to refactor. The good news is that refactoring is relatively easy. It’s not hard to spot when a
command is trying to do too much, and the solution is usually clear. Move that functionality down to a
facade or domain class.
Request
Requests are magically handled for us by PHP and neatly packaged up in superglobal arrays. You might
have noticed that I still use a class to represent a request. A Request object is passed to CommandResolver,
and later on to Command.
Why do I not let these classes simply query the $_REQUEST, $_POST, or $_GET arrays for themselves? I
could do that, of course, but by centralizing request operations in one place, I open up new options. You
could, for example, apply filters to the incoming request. Or, as the next example shows, you could
gather request parameters from somewhere other than an HTTP request, allowing the application to be
run from the command line or from a test script. Of course, if your application uses sessions, you may
have to provide an alternative storage mechanism for use in a command line context. The Registry
pattern would work well for you there, allowing you to generate different Registry classes according to
the context of the application.