BehavioralPHPverifiedVerified
Mediator Pattern in PHP
Defines an object that encapsulates how a set of objects interact, promoting loose coupling by keeping objects from referring to each other explicitly.
How to Implement the Mediator Pattern in PHP
1Step 1: Define the mediator interface
interface Mediator
{
public function notify(Component $sender, string $event): void;
}2Step 2: Define the base component that knows its mediator
abstract class Component
{
protected ?Mediator $mediator = null;
public function setMediator(Mediator $mediator): void
{
$this->mediator = $mediator;
}
}3Step 3: Implement concrete components
class Button extends Component
{
public function click(): void
{
echo "Button clicked\n";
$this->mediator?->notify($this, 'click');
}
}
class TextBox extends Component
{
private string $text = '';
public function setText(string $text): void
{
$this->text = $text;
$this->mediator?->notify($this, 'textChanged');
}
public function getText(): string { return $this->text; }
}
class Label extends Component
{
public function display(string $text): void
{
echo "Label: {$text}\n";
}
}4Step 4: Implement the concrete mediator that coordinates components
class FormMediator implements Mediator
{
public function __construct(
private readonly Button $submitButton,
private readonly TextBox $input,
private readonly Label $status,
) {
$submitButton->setMediator($this);
$input->setMediator($this);
$status->setMediator($this);
}
public function notify(Component $sender, string $event): void
{
match (true) {
$sender === $this->submitButton && $event === 'click'
=> $this->status->display("Submitted: {$this->input->getText()}"),
$sender === $this->input && $event === 'textChanged'
=> $this->status->display("Typing: {$this->input->getText()}"),
default => null,
};
}
}<?php
declare(strict_types=1);
// [step] Define the event-driven mediator interfaces
interface MediatorInterface
{
public function register(string $channel, ColleagueInterface $colleague): void;
public function send(string $channel, MediatorEvent $event, ColleagueInterface $sender): void;
}
interface ColleagueInterface
{
public function receive(string $channel, MediatorEvent $event): void;
public function getName(): string;
}
final readonly class MediatorEvent
{
public function __construct(
public string $type,
/** @var array<string, mixed> */
public array $data = [],
public float $timestamp = 0,
) {}
}
interface LoggerInterface
{
public function info(string $message, array $context = []): void;
public function warning(string $message, array $context = []): void;
}
// [step] Implement the mediator with channel-based routing and logging
final class EventMediator implements MediatorInterface
{
/** @var array<string, ColleagueInterface[]> */
private array $channels = [];
/** @var MediatorEvent[] */
private array $eventLog = [];
public function __construct(
private readonly LoggerInterface $logger,
) {}
public function register(string $channel, ColleagueInterface $colleague): void
{
$this->channels[$channel][] = $colleague;
$this->logger->info("Registered {$colleague->getName()} on channel: {$channel}");
}
public function send(string $channel, MediatorEvent $event, ColleagueInterface $sender): void
{
$this->eventLog[] = $event;
$subscribers = $this->channels[$channel] ?? [];
$this->logger->info("Broadcasting on {$channel}", [
'sender' => $sender->getName(),
'subscribers' => count($subscribers),
]);
foreach ($subscribers as $colleague) {
if ($colleague !== $sender) {
try {
$colleague->receive($channel, $event);
} catch (\Throwable $e) {
$this->logger->warning("Delivery failed", [
'channel' => $channel,
'recipient' => $colleague->getName(),
'error' => $e->getMessage(),
]);
}
}
}
}
/** @return MediatorEvent[] */
public function getEventLog(): array { return $this->eventLog; }
}
// [step] Implement concrete colleagues
final class ChatUser implements ColleagueInterface
{
/** @var MediatorEvent[] */
private array $inbox = [];
public function __construct(
private readonly string $name,
private readonly MediatorInterface $mediator,
) {}
public function getName(): string { return $this->name; }
public function sendMessage(string $channel, string $text): void
{
$event = new MediatorEvent(
type: 'message',
data: ['text' => $text, 'author' => $this->name],
timestamp: microtime(true),
);
$this->mediator->send($channel, $event, $this);
}
public function receive(string $channel, MediatorEvent $event): void
{
$this->inbox[] = $event;
}
/** @return MediatorEvent[] */
public function getInbox(): array { return $this->inbox; }
}Mediator Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Mediator Pattern in the Real World
“An air traffic control tower is the classic example. Instead of every plane communicating directly with every other plane—a chaotic and dangerous mess—all aircraft talk only to the control tower. The tower mediates all interactions, directing each plane based on the overall picture it maintains. Planes are decoupled from each other entirely.”