← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Decorator Pattern

general PHP 5.0+ Intermediate
debt(d5/e5/b5/t5)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches' (d5). PHPStan can detect type mismatches when decorators don't implement the expected interface, but detecting the anti-pattern of inheritance explosion instead of composable decorators (as noted in detection_hints.code_pattern) requires careful architectural review. The detection_hints explicitly state automated=no, meaning this is not automatically caught.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor' (e5). The quick_fix describes wrapping objects with decorators implementing the same interface — sounds simple, but refactoring from an inheritance tree (LoggingCache, CachingWithLogging) to composable decorators requires extracting interfaces, creating decorator classes, and updating instantiation points across the codebase. Not a one-liner.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). The pattern applies broadly (web, cli, queue-worker contexts per applies_to) and once adopted shapes how cross-cutting concerns are handled throughout the system. A decorator-based architecture becomes a convention that influences how new features are added — middleware pipelines, logging, caching all follow this shape. Not quite system-defining but more than localised.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5). The misconception field explicitly states developers assume decorators must always implement the same interface, when opaque decorators are valid for specific cases. Common_mistakes reinforce this: confusing Decorator with Proxy (similar structure, different intent), forgetting delegation to the wrapped object. These are documented gotchas that experienced developers eventually learn, but competent newcomers regularly stumble on them.

About DEBT scoring →

Also Known As

decorator wrapper decorator decorating objects

TL;DR

Wraps an object to add new behaviour dynamically without modifying its class or using inheritance.

Explanation

The Decorator pattern wraps an existing object in a new class that implements the same interface, adding behaviour before/after delegating to the wrapped object. Unlike inheritance, decorators compose at runtime and can be stacked. Classic examples: adding logging, caching, or rate limiting to a repository by wrapping it with LoggingRepository, CachingRepository, etc. — each wrapper transparent to callers. In PHP, this is the basis for middleware pipelines in frameworks and PSR-15 HTTP middleware.

Common Misconception

Decorators always need to implement the same interface as the decorated class. While interface parity is the cleanest approach, transparent decorators require it — opaque decorators that add new behaviour without mimicking the original interface are valid for specific use cases.

Why It Matters

The Decorator pattern wraps an object to add behaviour dynamically — stacking decorators composes functionality without subclassing or modifying the original class.

Common Mistakes

  • Not implementing the same interface as the decorated object — breaks transparent substitution.
  • Decorators with too much logic — each decorator should add one distinct concern.
  • Confusing Decorator (wraps an object) with Proxy (controls access) — similar structure, different intent.
  • Not delegating to the wrapped object — forgetting to call parent methods breaks the chain.

Avoid When

  • You need to decorate dozens of methods — every method must be proxied, creating massive boilerplate.
  • The wrapped object's interface changes frequently — every signature change must be updated in all decorators.
  • A simple subclass or trait would achieve the same result with less complexity.
  • The decoration logic is not reusable across multiple contexts — a one-off wrapper is not worth the abstraction.

When To Use

  • Adding cross-cutting concerns (logging, caching, timing, auth checks) to an existing class without modifying it.
  • Building middleware pipelines where each layer wraps the next.
  • Composing behaviour at runtime from interchangeable building blocks.
  • Open-closed principle — extending a finalised or third-party class you cannot subclass.

Code Examples

✗ Vulnerable
// Behaviour added via inheritance — class explosion:
class Logger extends UserRepository {}
class CachingLogger extends Logger {}
class MetricsLogger extends CachingLogger {}
// Every combination needs a new subclass

// Decorator — composable:
$repo = new MetricsDecorator(new CachingDecorator(new UserRepository()));
✓ Fixed
interface Cache {
    public function get(string $key): mixed;
    public function set(string $key, mixed $value, int $ttl = 3600): void;
}

class LoggingCache implements Cache {
    public function __construct(
        private Cache           $inner,
        private LoggerInterface $logger,
    ) {}

    public function get(string $key): mixed {
        $value = $this->inner->get($key);
        $this->logger->debug('cache ' . ($value !== null ? 'hit' : 'miss') . " for $key");
        return $value;
    }
    public function set(string $key, mixed $value, int $ttl = 3600): void {
        $this->inner->set($key, $value, $ttl);
    }
}

$cache = new LoggingCache(new RedisCache($redis), $logger);

Added 15 Mar 2026
Edited 25 Mar 2026
Views 26
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 2 pings S 1 ping S 1 ping M 0 pings T 0 pings W 2 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S
No pings yesterday
Amazonbot 7 Ahrefs 5 Perplexity 5 Unknown AI 2 ChatGPT 2
crawler 20 crawler_json 1
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Medium
⚡ Quick Fix
Add behaviour to an object by wrapping it in a decorator that implements the same interface — stack multiple decorators to combine behaviours without subclassing
📦 Applies To
PHP 5.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Inheritance tree growing to add feature combinations (LoggingCache CachingWithLogging etc) instead of composable decorators
Auto-detectable: ✗ No phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Medium Context: Class Tests: Update

✓ schema.org compliant