Design patterns are vital tools for writing clean, maintainable, and scalable code. One powerful yet often overlooked structural pattern is the Decorator Design Pattern in PHP. This pattern allows you to add responsibilities to objects dynamically without changing their structure. That’s right—no need to crack open a single line of existing class code. It’s a perfect choice for developers who prioritize flexibility and clean architecture.
In this post, we’ll explore how the Decorator works, when you should use it, and how to implement it in native PHP. You’ll also see a bad code example (which violates the open/closed principle) and a good one (that follows the decorator pattern perfectly). By the end, you’ll have a solid understanding of this pattern and how to use it in real-world applications.
What is the Decorator Design Pattern in PHP?
The Decorator Design Pattern in PHP allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.
It wraps the original class inside another class, providing a flexible alternative to subclassing for extending functionality. This pattern is especially useful when you want to add features in a pluggable and layered way.
Bad Example: Violating Open/Closed Principle
Here’s a bad approach that shows what not to do:
class Coffee {
public function getCost(): int {
return 5;
}
public function getDescription(): string {
return 'Simple coffee';
}
}
class FancyCoffee extends Coffee {
public function getCost(): int {
return parent::getCost() + 2;
}
public function getDescription(): string {
return parent::getDescription() . ', with milk';
}
}
What’s wrong here?
- Subclassing
Coffee
every time we need a variation causes a class explosion. - It violates the Open/Closed Principle: to extend behavior, we have to modify or create subclasses.
Good Example: Using the Decorator Design Pattern in PHP
Let’s do it the right way using the Decorator pattern.
interface Coffee {
public function getCost(): int;
public function getDescription(): string;
}
class SimpleCoffee implements Coffee {
public function getCost(): int {
return 5;
}
public function getDescription(): string {
return 'Simple coffee';
}
}
class CoffeeDecorator implements Coffee {
protected Coffee $coffee;
public function __construct(Coffee $coffee) {
$this->coffee = $coffee;
}
public function getCost(): int {
return $this->coffee->getCost();
}
public function getDescription(): string {
return $this->coffee->getDescription();
}
}
class MilkDecorator extends CoffeeDecorator {
public function getCost(): int {
return parent::getCost() + 2;
}
public function getDescription(): string {
return parent::getDescription() . ', with milk';
}
}
class SugarDecorator extends CoffeeDecorator {
public function getCost(): int {
return parent::getCost() + 1;
}
public function getDescription(): string {
return parent::getDescription() . ', with sugar';
}
}
// Usage
$coffee = new SimpleCoffee();
$coffee = new MilkDecorator($coffee);
$coffee = new SugarDecorator($coffee);
echo $coffee->getDescription(); // Simple coffee, with milk, with sugar
echo $coffee->getCost(); // 8
Why is this better?
- It respects the Open/Closed Principle—you can add functionality without changing existing code.
- It avoids subclass overload by composing behaviors.
- It’s flexible and easy to extend.
When Should You Use the Decorator Pattern?
Use the Decorator Design Pattern in PHP when:
- You need to add responsibilities to objects without subclassing.
- You want to modify object behavior at runtime.
- You prefer composition over inheritance for scalability.
Examples in the real world include:
- Wrapping a logger to add timestamps.
- Adding user roles/permissions.
- Building layered UI components.
Benefits of Using the Decorator Design Pattern in PHP
Some solid reasons to love this pattern:
- Flexible: Easily add/remove features at runtime.
- Reusable: Compose different decorators for custom behavior.
- Clean code: Each decorator has a single responsibility.
- No inheritance mess: Keeps your class hierarchy shallow.
Final Thought
The Decorator Design Pattern in PHP is a powerful tool for adding behavior to objects dynamically and cleanly. It keeps your code modular, readable, and scalable by avoiding unnecessary subclassing. Whether you’re adding logging features, modifying UI components, or extending business logic, the Decorator pattern is your go-to solution.
Don’t let your code get stuck in subclass hell. Decorate it instead.
Interested in More Design Patterns?
Discover other powerful patterns like Factory, Strategy, and Observer in this full guide: