Use the Command Design Pattern | PHP Code Examples in 2023

Last Updated on

CraftyTechie is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

Using the Command Pattern in PHP

The command pattern is a behavioral design pattern that encapsulates a request into an object (command) that knows all about the receiving entity of this request. It allows you to parameterize objects with different requests, schedules or log requests and supports undoable operations.

Article Highlights

  • The command pattern decouples the invoker of action from the receiver by encapsulating the request for action as a separate object (command object).
  • Benefit – You can separate one application layer from another and use command objects to allow communications between the two.
  • Con – The code becomes complicated with all the new command classes in the application

Command Design Pattern PHP Code Example

<?php


interface Command {


    public function execute();
}


class LightOnCommand implements Command {


     private $light;    //Expects reference to the Light object


     public function __construct($light) {
        $this->light = $light;
     }


     public function execute() {
        $this->light->switchOn();
     }
}


class LightOffCommand implements Command {


    private $light;    //Expects reference to the Light object


    public function __construct($light) {
        $this->light = $light;
    }


     public function execute() {
        $this->light->switchOff();
    }
}


class MicOnCommand implements Command {


    private $mic;    //Expects reference to the Mic object


    public function __construct($mic) {
       $this->mic = $mic;
    }


    public function execute() {
       $this->mic->switchOn();
    }
}


class MicOffCommand implements Command {


    private $mic;    //Expects reference to the Mic object


    public function __construct($mic) {
       $this->mic = $mic;
    }


    public function execute() {
       $this->mic->switchOff();
    }
}


class ACOnCommand implements Command {


    private $ac;    //Expects reference to the Airconditioner object


    public function __construct($ac) {
       $this->ac = $ac;
    }


    public function execute() {
       $this->ac->switchOn();
    }
}


class ACOffCommand implements Command {


    private $ac;    //Expects reference to the Airconditioner object


    public function __construct($ac) {
       $this->ac = $ac;
    }


    public function execute() {
       $this->ac->switchOff();
    }
}


class CameraOnCommand implements Command {


    private $camera;    //Expects reference to the Camera object


    public function __construct($camera) {
       $this->camera = $camera;
    }


    public function execute() {
       $this->camera->switchOn();
    }
}


class CameraOffCommand implements Command {


    private $camera;    //Expects reference to the Camera object


    public function __construct($camera) {
       $this->camera = $camera;
    }


    public function execute() {
       $this->camera->switchOff();
    }
}


class SpeakerOnCommand implements Command {


    private $speaker;    //Expects reference to the Speaker object


    public function __construct($speaker) {
       $this->speaker = $speaker;
    }


    public function execute() {
       $this->speaker->switchOn();
    }
}


class SpeakerOffCommand implements Command {


    private $speaker;    //Expects reference to the Speaker object


    public function __construct($speaker) {
       $this->speaker = $speaker;
    }


    public function execute() {
       $this->speaker->switchOff();
    }
}


class PanelOnCommand implements Command {


    private $panel;    //Expects reference to the Speaker object


    public function __construct($panel) {
       $this->panel = $panel;
    }


    public function execute() {
       $this->panel->switchOn();
    }
}


class PanelOffCommand implements Command {


    private $panel;    //Expects reference to the Speaker object


    public function __construct($panel) {
       $this->panel = $panel;
    }


    public function execute() {
       $this->panel->switchOff();
    }
}




/*
Receiver object which has the implementation for performing an action
*/
class Light {


    public function switchOn() {
        echo "Light is switched on\n";
    }


    public function switchff() {
        echo "Light is switched off\n";
    }


    /*
    Other methods
    */
}


class Mic {


    public function switchOn() {
        echo "Mic is switched on\n";
    }


    public function switchff() {
        echo "Mic is switched off\n";
    }


     /*
    Other methods
    */
}


class AirConditioner {


    public function switchOn() {
        echo "Airconditioner is switched on\n";
    }


    public function switchff() {
        echo "Airconditioner is switched off\n";
    }


     /*
    Other methods
    */
}


class Camera {


    public function switchOn() {
        echo "Camera is switched on\n";
    }


    public function switchff() {
        echo "Camera is switched off\n";
    }


     /*
    Other methods
    */
}


class Speaker {


    public function switchOn() {
        echo "Speaker is switched on\n";
    }


    public function switchff() {
        echo "Speaker is switched off\n";
    }


     /*
    Other methods
    */
}


class Panel {


    public function switchOn() {
        echo "Panel is switched on\n";
    }


    public function switchff() {
        echo "Panel is switched off\n";
    }


     /*
    Other methods
    */
}




/*
Invoker object has a reference to the command object
and would pass on request to it.
*/
class Button {


    private $buttonID;
    private $command;   //Expects a command object


    public function __construct($buttonID, $command) {
        $this->buttonID = $buttonID;
        $this->command = $command;
    }


    public function setCommand($command) {
        $this->command = $command;
    }


    public function executeCommand() {
        $this->command->execute();
    }


    public function getButtonID() {
        return $this->buttonID;
    }
}


class Dashboard {
   
    private $buttons;


    public function __construct($buttons) {
        $this->buttons = [];


        foreach($buttons as $v) {
            $this->buttons[$v->getButtonID()] = $v;    //Associate array with buttonIDs as keys.
        }
    }


    public function buttonClicked($ID) {
        $this->buttons[$ID]->executeCommand();
    }
}


function bootstrapDashboard() {


    /*
        Receivers
    */
    $light = new Light();
    $mic = new Mic();
    $ac = new AirConditioner();
    $camera = new Camera();
    $speaker = new Speaker();
    $panel   = new Panel();


    /*
        Commands
    */
    $lightOnCommand = new LightOnCommand($light);
    $lightOffCommand = new LightOffCommand($light);
    $micOnCommand = new MicOnCommand($mic);
    $micOffCommand = new MicOffCommand($mic);
    $acOnCommand = new ACOnCommand($ac);
    $acOffCommand = new ACOffCommand($ac);
    $speakerOnCommand = new SpeakerOnCommand($speaker);
    $speakerOffCommand = new SpeakerOffCommand($speaker);
    $panelOnCommand = new PanelOnCommand($panel);
    $panelOffCommand = new PanelOffCommand($panel);


    /*
        Invokers
    */
    $buttons = [
        new Button('1', $lightOnCommand ),
        new Button('2', $lightOffCommand ),
        new Button('3', $micOnCommand ),
        new Button('4', $micOffCommand ),
        new Button('5', $acOnCommand ),
        new Button('6', $acOffCommand ),
        new Button('7', $speakerOnCommand ),
        new Button('8', $speakerOffCommand ),
        new Button('9', $panelOnCommand ),
        new Button('10', $panelOffCommand )
    ];


    return new Dashboard($buttons);
}


function run() {


    $dashboard = bootstrapDashboard();


    $dashboard->buttonClicked('1');
    $dashboard->buttonClicked('3');
    $dashboard->buttonClicked('5');
    $dashboard->buttonClicked('7');
    $dashboard->buttonClicked('9');
}


/*
OUTPUT
Light is switched on
Mic is switched on
Airconditioner is switched on
Speaker is switched on
Panel is switched on
*/

?>
Command Pattern PHP

Table of Contents

What is the Command Pattern

“The command pattern is a behavioral design pattern that encapsulates a request into an object (command) that knows all about the receiving entity of this request. It allows you to parameterize objects with different requests, schedules or log requests and supports undoable operations.”

Command Pattern PHP

Command Pattern Example

A university wants software for its central auditorium that will help automate the manual switch controls of the electronic components installed throughout the auditorium. They already have hardware APIs for these components and want us to use them.

hardware-apis

They want us to create software with all the control buttons stacked in columns in a dashboard. Users should be able to switch components by clicking the buttons on this dashboard.

dashboard

The hardware APIs are pretty versatile, with no standard interface.  Also, it is highly likely to see more components in the future. Should we bind these concrete classes to the dashboard buttons? Is that an excellent idea? 

Though the dashboard will work as expected, the design has many shortcomings.

Problem 🙁

Single responsibility principle

“A class should have one and only one responsibility.”, states the single responsibility principle. A class in which a set of methods based around a similar context or aspect is said to have high cohesion. For example, a class Downloader has to have methods to support video download functionality only and a Converter to support video convert functionality. Mixing up both these in one class is generally a bad idea.

Classes with high cohesion are focused and specialized; hence, any change happening outside their domain has the least or no effect on them. Contrary to that, changing a system with low cohesion is not less than a migraine for developers because the change may propagate farther than its radius.

In the dashboard, we have elements like buttons, columns, a container, and a layout to arrange them in the container. All these visual elements fall in the presentational layer. Anything related to the component function falls in the logical, implementational, or business layers. 

The proposed solution won’t mind the distinctions between these two layers and merge them all into one. Consequently, we will have code with cluttered and less cohesive modules with little or no flexibility.

Open/closed principle

“A class should be closed for modification but open for extension.”, states the open/closed principle. When a class uses concrete objects of another class, introducing new types of objects is often difficult because that usually means changing the existing system.

Contrary to this, programming to abstractions leads to more flexible designs where we can add objects without breaking the existing code. When different objects follow one common interface, we can leverage the power of polymorphism to refer to them by their abstraction.

Our solution doesn’t care much about this principle and hardcodes the logic in the presentational layer. This implementation is a huge miss in terms of maintainability and flexibility. Users won’t be able to customize the dashboard because buttons use concrete classes. Also, any change to the hardware API could break the dashboard software. 

Lack of abstraction

We don’t have abstraction in the current design. The dashboard refers to concrete hardware API objects with no common interface in between. A lack of abstraction in a system is a red flag when assessing a software design.

It won’t be wrong to call lack of abstraction the root cause of non-flexible and non-maintainable software. Program to interface, not implementation, is key to remember if we want to create software for today and tomorrow.

Solution 🙂

Separation of concerns

The dashboard and its elements are purely visual. They don’t have to worry about the gnarly implementation details of any hardware API object. In other words, we should define a presentational and implementational layer to separate the concerns.

Command Pattern PHP

The buttons should have a function solely responsible for delegating the action to another object. That way, the UI will act more independently as a layer and pass the request to another entity. With that architecture, the software modules will be loosely coupled, and the dashboard can be much more reusable and extendable.

Invoker & receiver

Continuing along the same line, we can call the dashboard the “Invoker” because the dashboard is the origin of a request for action or call to action. Action means some operation on the hardware.

The hardware API objects are the “Receivers” as they communicate and perform actions on the hardware. The invoker initiates a request for action which a receiver receives and fulfills. That sounds like the old proposed system with the invoker and receivers interacting directly.

Something missing could help us create that abstraction and layered architecture by sitting between the invoker and receivers.

Introducing command objects

A command object will be an intermediary between the two layers. It will receive the request from the invoker and forward it to the appropriate object by calling the action function on it. Every command object will have a reference to an API object and will call the appropriate action method on it.

These command objects implement a standard interface Command.

interface Command {

    public function execute();
}


For example, the LightOnCommand object will refer to a Light object and will call the switchOn() on it when the invoker requests it. Consequently, we have command objects for every action on every component.

class LightOnCommand implements Command {


     private $light;    //Expects reference to the Light object


     public function __construct($light) {
        $this->light = $light;
     }


     public function execute() {
        $this->light->switchOn();
     }
}


/*
Receiver object which has the implementation for performing an action
*/
class Light {


    public function switchOn() {
        echo "Light is switched on\n";
    }


    public function switchff() {
        echo "Light is switched off\n";
    }


    /*
    Other methods
    */
}

The invoker will call execute() on the LightOnCommand object, which in turn would call the switchOn() on the Light object.

command-flow


A Button object acts as an invoker since it initiates the request on a click event.

/*
Invoker object has a reference to the command object
and would pass on request to it.
*/
class Button {


    private $buttonID;
    private $command;   //Expects a command object


    public function __construct($buttonID, $command) {
        $this->buttonID = $buttonID;
        $this->command = $command;
    }


    public function setCommand($command) {
        $this->command = $command;
    }


    public function executeCommand() {
        $this->command->execute();
    }


    public function getButtonID() {
        return $this->buttonID;
    }
}

Since we have an invoker, command, and receiver object, we are ready for a quick test run.

function run() {


    $light = new Light(); //Receiver


    $lightOnCommand = new LightOnCommand($light); //Command


    $lightOnButton = new Button('1', $lightOnCommand ); //Invoker


    $lightOnButton->executeCommand();
}


//Output
//Light is switched on

Awesome! The pseudocode includes a complete example. We have commands for switch operations only. All other actions and their commands will follow the same pattern. If you understand the command pattern, implementing them won’t be a hard code to crack.

Benefits of the Command Pattern

  • Decouples invoker from the receiver object by passing requests to the command object.
  • It is possible to parameterize the command object, chain them with other commands, or switch them at runtime.
  • Could implement undo, scheduling, and logging operations via command objects.

Complete Architecture | Command Pattern in PHP

Command Pattern PHP

Command Pattern PHP Pseudocode Example

<?php


interface Command {


    public function execute();
}


class LightOnCommand implements Command {


     private $light;    //Expects reference to the Light object


     public function __construct($light) {
        $this->light = $light;
     }


     public function execute() {
        $this->light->switchOn();
     }
}


class LightOffCommand implements Command {


    private $light;    //Expects reference to the Light object


    public function __construct($light) {
        $this->light = $light;
    }


     public function execute() {
        $this->light->switchOff();
    }
}


class MicOnCommand implements Command {


    private $mic;    //Expects reference to the Mic object


    public function __construct($mic) {
       $this->mic = $mic;
    }


    public function execute() {
       $this->mic->switchOn();
    }
}


class MicOffCommand implements Command {


    private $mic;    //Expects reference to the Mic object


    public function __construct($mic) {
       $this->mic = $mic;
    }


    public function execute() {
       $this->mic->switchOff();
    }
}


class ACOnCommand implements Command {


    private $ac;    //Expects reference to the Airconditioner object


    public function __construct($ac) {
       $this->ac = $ac;
    }


    public function execute() {
       $this->ac->switchOn();
    }
}


class ACOffCommand implements Command {


    private $ac;    //Expects reference to the Airconditioner object


    public function __construct($ac) {
       $this->ac = $ac;
    }


    public function execute() {
       $this->ac->switchOff();
    }
}


class CameraOnCommand implements Command {


    private $camera;    //Expects reference to the Camera object


    public function __construct($camera) {
       $this->camera = $camera;
    }


    public function execute() {
       $this->camera->switchOn();
    }
}


class CameraOffCommand implements Command {


    private $camera;    //Expects reference to the Camera object


    public function __construct($camera) {
       $this->camera = $camera;
    }


    public function execute() {
       $this->camera->switchOff();
    }
}


class SpeakerOnCommand implements Command {


    private $speaker;    //Expects reference to the Speaker object


    public function __construct($speaker) {
       $this->speaker = $speaker;
    }


    public function execute() {
       $this->speaker->switchOn();
    }
}


class SpeakerOffCommand implements Command {


    private $speaker;    //Expects reference to the Speaker object


    public function __construct($speaker) {
       $this->speaker = $speaker;
    }


    public function execute() {
       $this->speaker->switchOff();
    }
}


class PanelOnCommand implements Command {


    private $panel;    //Expects reference to the Speaker object


    public function __construct($panel) {
       $this->panel = $panel;
    }


    public function execute() {
       $this->panel->switchOn();
    }
}


class PanelOffCommand implements Command {


    private $panel;    //Expects reference to the Speaker object


    public function __construct($panel) {
       $this->panel = $panel;
    }


    public function execute() {
       $this->panel->switchOff();
    }
}




/*
Receiver object which has the implementation for performing an action
*/
class Light {


    public function switchOn() {
        echo "Light is switched on\n";
    }


    public function switchff() {
        echo "Light is switched off\n";
    }


    /*
    Other methods
    */
}


class Mic {


    public function switchOn() {
        echo "Mic is switched on\n";
    }


    public function switchff() {
        echo "Mic is switched off\n";
    }


     /*
    Other methods
    */
}


class AirConditioner {


    public function switchOn() {
        echo "Airconditioner is switched on\n";
    }


    public function switchff() {
        echo "Airconditioner is switched off\n";
    }


     /*
    Other methods
    */
}


class Camera {


    public function switchOn() {
        echo "Camera is switched on\n";
    }


    public function switchff() {
        echo "Camera is switched off\n";
    }


     /*
    Other methods
    */
}


class Speaker {


    public function switchOn() {
        echo "Speaker is switched on\n";
    }


    public function switchff() {
        echo "Speaker is switched off\n";
    }


     /*
    Other methods
    */
}


class Panel {


    public function switchOn() {
        echo "Panel is switched on\n";
    }


    public function switchff() {
        echo "Panel is switched off\n";
    }


     /*
    Other methods
    */
}




/*
Invoker object has a reference to the command object
and would pass on request to it.
*/
class Button {


    private $buttonID;
    private $command;   //Expects a command object


    public function __construct($buttonID, $command) {
        $this->buttonID = $buttonID;
        $this->command = $command;
    }


    public function setCommand($command) {
        $this->command = $command;
    }


    public function executeCommand() {
        $this->command->execute();
    }


    public function getButtonID() {
        return $this->buttonID;
    }
}


class Dashboard {
   
    private $buttons;


    public function __construct($buttons) {
        $this->buttons = [];


        foreach($buttons as $v) {
            $this->buttons[$v->getButtonID()] = $v;    //Associate array with buttonIDs as keys.
        }
    }


    public function buttonClicked($ID) {
        $this->buttons[$ID]->executeCommand();
    }
}


function bootstrapDashboard() {


    /*
        Receivers
    */
    $light = new Light();
    $mic = new Mic();
    $ac = new AirConditioner();
    $camera = new Camera();
    $speaker = new Speaker();
    $panel   = new Panel();


    /*
        Commands
    */
    $lightOnCommand = new LightOnCommand($light);
    $lightOffCommand = new LightOffCommand($light);
    $micOnCommand = new MicOnCommand($mic);
    $micOffCommand = new MicOffCommand($mic);
    $acOnCommand = new ACOnCommand($ac);
    $acOffCommand = new ACOffCommand($ac);
    $speakerOnCommand = new SpeakerOnCommand($speaker);
    $speakerOffCommand = new SpeakerOffCommand($speaker);
    $panelOnCommand = new PanelOnCommand($panel);
    $panelOffCommand = new PanelOffCommand($panel);


    /*
        Invokers
    */
    $buttons = [
        new Button('1', $lightOnCommand ),
        new Button('2', $lightOffCommand ),
        new Button('3', $micOnCommand ),
        new Button('4', $micOffCommand ),
        new Button('5', $acOnCommand ),
        new Button('6', $acOffCommand ),
        new Button('7', $speakerOnCommand ),
        new Button('8', $speakerOffCommand ),
        new Button('9', $panelOnCommand ),
        new Button('10', $panelOffCommand )
    ];


    return new Dashboard($buttons);
}


function run() {


    $dashboard = bootstrapDashboard();


    $dashboard->buttonClicked('1');
    $dashboard->buttonClicked('3');
    $dashboard->buttonClicked('5');
    $dashboard->buttonClicked('7');
    $dashboard->buttonClicked('9');
}


/*
OUTPUT
Light is switched on
Mic is switched on
Airconditioner is switched on
Speaker is switched on
Panel is switched on
*/


?>

Pros and Cons of the Command Pattern in PHP

ProsCons
Satisfies the single responsibility principle as we have classes focused on doing one kind of a job.The code becomes complicated with all the new command classes in the application.
Satisfies the open/closed principle because we can add as many commands and receivers without affecting the existing code.
You can separate one application layer from another and use command objects to allow communications between the two.
You can have macro commands that execute multiple commands in one invocation.

Where is the Command Pattern Used

  • Graphical user interfaces use command objects to bind UI elements and event-based actions.
  • Schedulers, job queues, thread pools, and timers can use command objects to implement job and thread scheduling.
  • Logging and transactional application where we can store command objects and roll back or redo operations using them.

What is the Command pattern?

The command pattern is a behavioral design pattern that encapsulates a request into an object (command) that knows all about the receiving entity of this request. It allows you to parameterize objects with different requests, schedules or log requests and supports undoable operations.

Using the Command Pattern in PHP Today

Voila! That’s all about the command pattern. Let’s have an overview of what we have seen in this article. The command pattern is a behavioral design pattern that encapsulates a request into an object (command) that knows all about the receiving entity of this request. It allows you to parameterize objects with different requests, schedules or log requests and supports undoable operations.

The command pattern decouples the invoker from the receiver of the action by introducing command objects. The command object encapsulates the request and knows the receiver that is supposed to fulfill the request. The article features dashboard software for automating electronic components in a university auditorium.

Developers devise a solution where the invoker (dashboard buttons) directly calls methods on the receiver object. The design has many shortcomings in terms of design, and due to a lack of abstraction, it lacks the flexibility to support any future instances of receivers.

The design also overlooks fundamental design principles. Thus the developers rethink a solution where they could add a layer of abstraction between the two communicating entities, and therefore they program the command interface.

The invoker calls the common execute() method on the command objects regardless of their concrete type and leaves the rest to the command object. The command objects encapsulate a receiver object and call methods on it. Such a design allows for layering our application into presentational and implementation layers and letting the command objects bind the two.

The application modules are then more cohesive and flexible because we can add as many receiver objects and commands without affecting the existing codebase.

That’s all. See you in another design pattern article.

Books on Design Patterns

Want to learn more about Design Patterns? There are many great resources online. We recommend the following books for your collection as they both can teach you the theoretical and the application of using design patterns in your day-to-day programming. Feel free to use the following Amazon affiliate links if you’d like to purchase them and a way to support our efforts.

Design Patterns: Elements of Reusable Object-Oriented Software

Design Patterns: Elements of Reusable Object-Oriented Software book

This is the book that started it all. I believe that every programmer should have a referenced copy to this book at some point in their career. There have been many updates and excellent newer content through the years, but this is a classic. It still stands the test of time and is just as relevant for today as it was in the original printing in the 90s.

Check it out on amazon

Learning PHP Design Patterns

This is an excellent book to go beyond the theory and apply it to writing good PHP code. Learning PHP Design Patterns is published by the popular O’Reilly media company. O’Reilly consistently publishes some of the most useful reference material related to software development. They are known to provide materials that thoroughly cover a topic in a way that is simple to understand. I recommend this book to every PHP developer.

Check it out on amazon

Want to see our full review of books on design patterns? Read our huge review article on over 15 design pattern books.

Design Patterns in PHP Learning Series.

This article is part of our series of design patterns in PHP. We are going through all of the patterns and showing how they can help you build better applications. Browse through our full list of patterns below.

Did you find this article helpful?

Join the best weekly newsletter where I deliver content on building better web applications. I curate the best tips, strategies, news & resources to help you develop highly-scalable and results-driven applications.

Build Better Web Apps

I hope you're enjoying this article.

Get the best content on building better web apps delivered to you.