The Chain-of-Command Pattern: OOP Techniques in PHP

Posted on 10. Mar, 2009 by Will Fitch in PHP

The chain-of-command pattern, like most others, assists with maintaining a loose coupling within your classes. By providing a series of classes that implement the ICommand interface and do a specific bit of processing, the developer doesn’t have to care which method to execute.

The ICommand Interface

PHP
  1. interface ICommand
  2. {
  3.     /**
  4.      * Run a command
  5.      * @param string $command_type
  6.      * @param args $args
  7.      */
  8.     public function onCommandCall($command_type, $args);
  9. }

This simple interface provides a method for executing a call to initiate a bit of functionality. Along with the classes that execute the functionality, you’ll need a class to handle the chain. We’ll provide this class with another interface requiring the use of a method to add chains and execute commands:

PHP
  1. interface ICommandBuilder
  2. {
  3.         /**
  4.          * Run a command
  5.          *
  6.          * @param string $command_type
  7.          * @param mixed $args
  8.          */
  9.         public function runCommand($command_type, $args);
  10.        
  11.         /**
  12.          * Add a new command class
  13.          *
  14.          * @param mixed $commanding_class
  15.          */
  16.         public function addCommand($commanding_class);
  17. }

The two methods here are used for executing commands and adding new command classes. Let’s build the chain-of-command class.

PHP
  1. class ChainBuilder implements ICommandBuilder
  2. {
  3.        
  4.         private $_commands=array();
  5.        
  6.         public function runCommand($command_type, $args)
  7.         {
  8.                 if (count($this->_commands) > 0)
  9.                 {
  10.                         foreach ($this->_commands as $class)
  11.                         {
  12.                                 if ($class->onCommandCall($command_type, $args))
  13.                                 {
  14.                                         return;
  15.                                 }
  16.                         }
  17.                 }
  18.         }
  19.        
  20.         public function addCommand($commanding_class)
  21.         {
  22.                 $this->_commands[] = $commanding_class;
  23.                 return $this;
  24.         }
  25. }

The class above provides a mechanism for chaining your requests. You add the necessary commands to execute, then pass requests to runCommand, which will loop through all of the commands until one successfully completes the action, or it exhausts all commands. Here you could add interface checks and throw the necessary exceptions for commands not found, etc.

Let’s add a couple of command implementations: email and stream.

PHP
  1. class EmailCommand implements ICommand
  2. {
  3.         public function onCommandCall($command, $args)
  4.         {
  5.                 if ($command == ‘email’)
  6.                 {
  7.                         // send an email with $args
  8.                         return true;
  9.                 }
  10.                 return false;
  11.         }
  12. }
  13.  
  14. class StreamCommand implements ICommand
  15. {
  16.         public function onCommandCall($command, $args)
  17.         {
  18.                 if ($command == ‘writeStream’)
  19.                 {
  20.                         // Write the stream
  21.                         return true;
  22.                 }
  23.                 return false;
  24.         }
  25. }

These commands email a user and write to a stream. Now let’s use them!

PHP
  1. $command = new ChainBuilder();
  2. $command->addCommand(new StreamCommand())->addCommand(new EmailCommand());
  3. $command->runCommand(‘email’,array(‘email’=>‘me@you.com’));
  4. $command->runCommand(‘writeStream’,array(‘content’=>‘Hi, stream!’));

Conclusion

At first glance, the chain-of-command pattern may look like overhead. But on a large code base, this code be useful when making a lot of modifications, etc.

Tags: , , ,

Related Articles

4 Responses to “The Chain-of-Command Pattern: OOP Techniques in PHP”

  1. Clayton

    08. Feb, 2010

    Couldn’t this CoC pattern be described as the Observer pattern?

    Looking at it, it would look very similar albeit with a different name.

    The basis of both is you have subject and a subscriber. A subject acts as a publisher and a subscriber receives updates from that publisher.

    $observer = new Observer();

    // You could optionally specify the nature of the subscription, in the second argument
    $observer->subscribe(new Mailer());

    // This essentially runs the command
    $observer->publish(‘mailer’, array(‘address’ => ‘you@and.me’));

    Another example often seen involves the use of events and triggers.
    $observer->addEvent(new Log());
    $observer->trigger(‘error’, array());

  2. Will Fitch

    09. Feb, 2010

    Both are a subscriber/publisher type of setup. The main difference is purpose. The CoC could be described as a very generic version of the Observer. The CoC goal is to chain events together and execute until an object handles it.

    The Observer, on the other hand, has specific attributes. You may have an observer that is specific to database events, service events, etc. This can also get confused with the Decorator pattern.

    The Decorator is actually closer to CoC than Observer. Rather than executing until an object handles something, the Decorator iterates over all objects to format functionality. Logging is a good example.

    $log = new Logger();
    $log->addLogger(new FileLogger($file_name));
    $log->addLogger(new DBLogger($db_conn));
    ..
    ..
    ..

    $log->log(‘error’,'This is an error!’);

    The above logs a single request to multiple sources via a single interface.

  3. Clayton

    09. Feb, 2010

    Just so we’re clear the only separation between this CoC pattern and the Observer pattern, is that the CoC pattern exits the loop the second an object handles the event?

    Also in your example there’s ‘if (count($commands) > 0)’ I assume this is a typo and should be ‘if (count($this->_commands) > 0)’. In which case, is the if statement required as the foreach wont execute if the array is empty.

    Btw I’m looking forward to your Fever Framework.

  4. Will Fitch

    09. Feb, 2010

    Thanks, Clayton.

    You’re correct on the typo.

    The separation is the execution and “genericism” (a little G W Bush for ya).

    Yes, if the $_commands array is empty, we don’t want to attempt to execute the foreach loop.

    Clayton, would you be interested in joining the alpha dev team? If so, shoot me an email.

Leave a Reply