PHP Chain of Responsibility Pattern
A short screencast: the Chain of Responsibility pattern — pass a request along a chain of handlers until one of them handles it. Let us approve a request through a team lead and a manager.
Overview
The Chain of Responsibility passes a request along a chain of handlers until one of them handles it. A request amount first reaches the team lead: up to 1000 they approve it themselves, otherwise they pass it to the manager. The manager approves up to 5000, and a larger amount has no one further to pass to — null is returned. The client calls only the first handler and does not know who will answer.
UML diagram
Code
Handler.php
interface Handler
{
public function setNext(Handler $next): Handler;
public function handle(int $amount): ?string;
}
AbstractHandler.php
abstract class AbstractHandler implements Handler
{
private ?Handler $next = null;
public function setNext(Handler $next): Handler
{
$this->next = $next;
return $next;
}
public function handle(int $amount): ?string
{
return $this->next?->handle($amount);
}
}
TeamLead.php
class TeamLead extends AbstractHandler
{
public function handle(int $amount): ?string
{
if ($amount <= 1000) {
return 'Team lead approved '.$amount;
}
return parent::handle($amount);
}
}
Manager.php
class Manager extends AbstractHandler
{
public function handle(int $amount): ?string
{
if ($amount <= 5000) {
return 'Manager approved '.$amount;
}
return parent::handle($amount);
}
}
demo.php
require_once 'Handler.php';
require_once 'AbstractHandler.php';
require_once 'TeamLead.php';
require_once 'Manager.php';
$teamLead = new TeamLead;
$manager = new Manager;
$teamLead->setNext($manager);
echo $teamLead->handle(500).PHP_EOL;
echo $teamLead->handle(3000).PHP_EOL;
var_dump($teamLead->handle(99999));
Example output
Running php demo.php shows the request travelling along the chain: 500 is approved by the team lead, 3000 already by the manager, and for 99999 no handler matches, so NULL is returned:
Team lead approved 500
Manager approved 3000
NULL
Advantages
- The sender does not know which handler will end up handling the request.
- Handlers can be added, removed and reordered without touching the client.
- Each handler is responsible for a single condition (single responsibility principle).
- The chain is assembled dynamically and may differ per scenario.
When to use
- When several candidates can handle a request and it is not known in advance which one will.
- When handlers should be chained and pass the request on.
- When the set and order of handlers change at runtime.
- When responsibility must be split between separate handlers.
Usage in Laravel
In Laravel the Chain of Responsibility is HTTP middleware: Illuminate\Pipeline\Pipeline runs a request through the app/Http/Middleware/* layer, and each link either continues the chain or breaks it. The same pipeline powers the router and job processing, and the middleware stack assembled by the core kernel (Illuminate\Foundation\Http\Kernel) is exactly the chain every request travels along.