← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Chain of Responsibility Pattern

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

Closest to 'only careful code review or runtime testing' (d7). The detection_hints note that automated detection is 'no' and phpstan is listed as a tool but cannot reliably detect structural misuse like missing default handlers or stateful handler reuse — these surface only through careful code review or runtime testing when requests silently disappear or race conditions manifest.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix describes building a handler chain, but common_mistakes like stateful handlers reused across chains or chains that are too long require revisiting every handler class, adjusting the chain construction, and potentially refactoring across multiple files. It's more than a one-line fix but not necessarily a full architectural rework.

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

Closest to 'persistent productivity tax' (b5). The pattern applies to both web and cli contexts. Once adopted, every new handler must conform to the chain interface and ordering decisions affect system behaviour. It imposes an ongoing design overhead — new team members must understand chain composition, handler ordering, and the absence of a default handler — but it doesn't define the entire system's shape.

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

Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field directly identifies the core trap: developers conflate CoR with middleware, assuming handlers always pass the request along unconditionally. In reality CoR short-circuits when a handler processes the request. This is a well-documented gotcha that experienced developers learn but frequently trips up those coming from middleware-centric frameworks like PSR-15.

About DEBT scoring →

Also Known As

CoR pattern handler chain middleware pattern

TL;DR

Passes a request along a chain of handlers, each deciding whether to process it or pass it to the next handler.

Explanation

The Chain of Responsibility decouples senders from receivers by giving multiple objects the chance to handle a request. Each handler in the chain either processes the request and stops, or passes it to the next handler. This is the basis of PHP middleware pipelines (PSR-15), event propagation chains, and request processing pipelines. It replaces large if/elseif chains with composable, single-responsibility handler objects. The chain can be built dynamically, handlers can be reordered, and new handlers can be added without modifying existing ones — following Open/Closed Principle.

Diagram

flowchart LR
    REQ2[Request] --> H1[Handler 1<br/>Auth check]
    H1 -->|pass| H2[Handler 2<br/>Rate limit]
    H2 -->|pass| H3[Handler 3<br/>Validation]
    H3 -->|pass| H4[Handler 4<br/>Business logic]
    H1 -->|fail| R1[401 Unauthorized]
    H2 -->|fail| R2[429 Rate limited]
    H3 -->|fail| R3[422 Invalid input]
    subgraph PHP_Examples
        MW[Laravel Middleware pipeline]
        PIPE[PHP League Pipeline]
    end
style H4 fill:#238636,color:#fff
style R1 fill:#f85149,color:#fff
style R2 fill:#f85149,color:#fff
style R3 fill:#f85149,color:#fff

Common Misconception

Chain of responsibility is the same as middleware. Middleware typically passes to the next handler unconditionally; CoR lets each handler decide whether to pass the request along, short-circuiting the chain when it handles the request itself.

Why It Matters

Chain of Responsibility decouples request senders from handlers — each handler decides to process or pass along, making it easy to add, remove, or reorder handlers without changing the sender.

Common Mistakes

  • Not providing a default handler at the end of the chain — unhandled requests silently disappear.
  • Making handlers stateful and reusing them across chains — causes race conditions and unexpected behaviour.
  • Using the pattern when a simple switch or strategy would be clearer — over-engineering for a handful of cases.
  • Chains that are too long causing performance issues — every request traverses the full chain even for early matches.

Code Examples

✗ Vulnerable
// Monolithic if-else instead of chain — hard to extend:
function handle(Request $r): Response {
    if ($r->type === 'auth') return $this->handleAuth($r);
    elseif ($r->type === 'cache') return $this->handleCache($r);
    elseif ($r->type === 'log') return $this->handleLog($r);
    // Adding a new type requires modifying this method
}
✓ Fixed
abstract class Handler {
    private ?Handler $next = null;
    public function setNext(Handler $h): Handler { $this->next = $h; return $h; }
    protected function passOn(Request $req): ?Response {
        return $this->next?->handle($req);
    }
    abstract public function handle(Request $req): ?Response;
}

class AuthHandler extends Handler {
    public function handle(Request $req): ?Response {
        if (!$req->user()) return new Response(401);
        return $this->passOn($req);
    }
}
class RateLimitHandler extends Handler {
    public function handle(Request $req): ?Response {
        if ($this->isThrottled($req)) return new Response(429);
        return $this->passOn($req);
    }
}

$auth = new AuthHandler();
$auth->setNext(new RateLimitHandler())->setNext(new AppHandler());

Added 15 Mar 2026
Edited 22 Mar 2026
Views 110
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings S 0 pings M 1 ping T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 1 ping T 1 ping W 1 ping T 1 ping F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M
No pings yet today
No pings yesterday
Ahrefs 19 PetalBot 10 Amazonbot 9 SEMrush 7 Bing 6 Perplexity 5 Twitter/X 3 Sogou 3 Unknown AI 2 ChatGPT 2 Scrapy 2 Brave Search 2 Applebot 2 Google 1
crawler 71 crawler_json 2
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Medium
⚡ Quick Fix
Build a handler chain where each handler either processes the request or passes it to the next — PHP middleware (PSR-15) is the most common real-world implementation
📦 Applies To
PHP 5.0+ web cli laravel symfony
🔗 Prerequisites
🔍 Detection Hints
Long if/elseif chain checking request type to determine handler; middleware checks not composable
Auto-detectable: ✗ No phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Medium Context: Class Tests: Update


✓ schema.org compliant