PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
138: The Command Pattern in PHP
I see this all the time when developers first encounter the Command Pattern: they look at the boilerplate and think, "Why on earth would I wrap a simple method call inside a whole new class? I can just call $light->turnOn() directly. This is just adding three files to do something that takes one line of code."
It's Just a Wrapper for a Method Call
If your only goal is to make something happen right now, then yes, the Command Pattern is overkill. If you just want to turn on a light, calling the method is the right move. But the misconception is thinking that the pattern is about execution. It's actually about encapsulation.
Imagine you're building a smart home system. If you hard-code every action, your "Remote Control" class becomes a nightmare. It has to know exactly which devices exist and which methods they have. If you add a new brand of smart bulb that uses $bulb->activate() instead of $light->turnOn(), you have to go back and change the logic in your remote control. You've tightly coupled the "trigger" to the "implementation."
Turning an Action into a First-Class Object
The shift in thinking is this: stop thinking about "calling a function" and start thinking about "creating a request object." When you turn a request into an object, that object can be stored in a queue, passed as a parameter, or pushed onto a stack. It becomes a "thing" you can manipulate.
// First, we define the contract. Every command must be executable.
interface Command {
public function execute(): void;
public function undo(): void;
}
// The Receiver: The actual object that knows how to do the work.
class Light {
public function turnOn() { echo "The light is ON\n"; }
public function turnOff() { echo "The light is OFF\n"; }
}
// The Concrete Command: This bridges the gap.
class LightOnCommand implements Command {
private $light;
public function __construct(Light $light) {
$this->light = $light;
}
public function execute(): void {
$this->light->turnOn();
}
public function undo(): void {
$this->light->turnOff();
}
}
Now, notice that the LightOnCommand doesn't just "do" the action; it knows how to reverse it. Because the command is an object, it can hold the state necessary to undo itself. You can't "undo" a method call once it's gone, but you can call undo() on a command object.
Decoupling the Trigger from the Logic
This is where the "Invoker" comes in. The Invoker doesn't know what the command does; it just knows that it can execute it. This allows you to create complex behaviors—like macros or undo history—without the Invoker ever knowing the details of your business logic.
class RemoteControl {
private $history = [];
public function submit(Command $command) {
$command->execute();
$this->history[] = $command;
}
public function pressUndo() {
if (!empty($this->history)) {
$command = array_pop($this->history);
$command->undo();
}
}
}
// Usage
$livingRoomLight = new Light();
$turnOn = new LightOnCommand($livingRoomLight);
$remote = new RemoteControl();
$remote->submit($turnOn); // Output: The light is ON
$remote->pressUndo(); // Output: The light is OFF
I'll be honest: in a tiny project, this feels like a lot of typing. But when you're building something like a text editor with an undo/redo stack, or a job queue where commands are serialized and pushed to Redis to be processed later, this pattern is a lifesaver. You've effectively decoupled the when (the remote button) from the how (the light's internal API).
📋 Practical Task
Build a Transactional File System Command Manager
You need to implement a basic "File Manager" that supports creating files and deleting files, but with a critical requirement: every action must be undoable.
Your Task:
- Create a
Commandinterface withexecute()andundo()methods. - Create a
FileSystemclass (the Receiver) with two methods:createFile($name)anddeleteFile($name). These should just print a message to the screen (e.g., "File 'test.txt' created"). - Implement a
CreateFileCommandand aDeleteFileCommand.- The
CreateFileCommandshould callcreateFile()on execution anddeleteFile()on undo. - The
DeleteFileCommandshould calldeleteFile()on execution andcreateFile()on undo.
- The
- Implement a
CommandHistoryclass (the Invoker) that maintains a stack of executed commands and has a methodundoLastAction().
Test Case: Create a file named "document.txt", then create a file named "notes.txt". Call undoLastAction() once, and verify that "notes.txt" is deleted. Call it again, and verify that "document.txt" is deleted.
There are no comments for now.