PHP Bridge Pattern
A short screencast: the Bridge pattern in PHP — decoupling abstraction from implementation so both can vary independently.
Overview
We build a remote and a device as two independent axes. The Device interface describes a TV, and the abstract RemoteControl takes a device in its constructor. A concrete BasicRemote does not know what device is inside — it only calls the contract. Add a radio or a smart remote, and neither side is affected.
UML diagram
Code
Device.php
interface Device
{
public function on(): void;
public function off(): void;
public function isOn(): bool;
}
TV.php
class TV implements Device
{
private bool $power = false;
public function on(): void
{
$this->power = true;
}
public function off(): void
{
$this->power = false;
}
public function isOn(): bool
{
return $this->power;
}
}
RemoteControl.php
abstract class RemoteControl
{
public function __construct(
protected Device $device
) {}
abstract public function togglePower(): string;
}
BasicRemote.php
class BasicRemote extends RemoteControl
{
public function togglePower(): string
{
$this->device->isOn() ? $this->device->off() : $this->device->on();
return $this->device->isOn() ? 'TV is ON' : 'TV is OFF';
}
}
demo.php
require_once 'Device.php';
require_once 'TV.php';
require_once 'RemoteControl.php';
require_once 'BasicRemote.php';
$tv = new TV();
$remote = new BasicRemote($tv);
echo $remote->togglePower();
echo PHP_EOL;
echo $remote->togglePower();
Note: what the bridge decouples
The Bridge is a reaction to an anti-pattern: without it, remote and device classes multiply (BasicTvRemote, AdvancedTvRemote, BasicRadioRemote…). With a bridge, two axes evolve on their own: the remote talks to the Device interface, and concrete devices are plugged into the constructor. A new type lives either on the remote side or on the device side — never both.
Example output
Running php demo.php in the terminal produces:
TV is ON
TV is OFF
Advantages
- Separates abstraction from implementation so both vary independently.
- Avoids a class explosion when two dimensions combine.
- The implementation can be chosen and changed at runtime.
- Simplifies adding new abstractions and new implementations.
When to use
- When a system has two independently varying dimensions.
- When you want to avoid a permanent binding between abstraction and implementation.
- When the implementation should be chosen at runtime.
- When the abstraction x implementation combination would otherwise spawn many classes.
Usage in Laravel
In Laravel the bridge is the mail layer: the Illuminate\Mail\Mailer abstraction is separated from its implementations Illuminate\Mail\Transport\... (SMTP, log, array). Illuminate\Log\LogManager (a logger and its handlers) and Illuminate\Notifications\ChannelManager (a notification and its channels) are built the same way. The abstraction and the delivery mechanism evolve independently, and the implementation is injected from configuration.