PHP Adapter Pattern
A short screencast: the Adapter pattern in PHP — fitting an alien API into a single contract without touching legacy code.
Overview
The client has a contract, PaymentInterface::process(). The old LegacyPaymentGateway is incompatible with it: it has charge() and returns a bool. The PaymentAdapter implements the contract, holds the legacy gateway inside, and translates its calls into the expected answer. The legacy code is never changed.
UML diagram
Code
PaymentInterface.php
interface PaymentInterface
{
public function process(float $amount): string;
}
LegacyPaymentGateway.php
class LegacyPaymentGateway
{
public function charge(float $amount): bool
{
return $amount > 0;
}
}
PaymentAdapter.php
class PaymentAdapter implements PaymentInterface
{
public function __construct(
private LegacyPaymentGateway $gateway
) {}
public function process(float $amount): string
{
$ok = $this->gateway->charge($amount);
return $ok ? "paid {$amount}" : 'declined';
}
}
demo.php
require_once 'PaymentInterface.php';
require_once 'LegacyPaymentGateway.php';
require_once 'PaymentAdapter.php';
function checkout(PaymentInterface $payment, float $amount): void
{
echo $payment->process($amount) . '!';
}
$gateway = new LegacyPaymentGateway();
$payment = new PaymentAdapter($gateway);
checkout($payment, 99.5);
echo PHP_EOL;
checkout($payment, 0);
Note: what an adapter buys you
The adapter is the single "seam" through which legacy or third-party SDK code works with our code. Clients depend on an interface, not on a concrete implementation: swapping a gateway or adding a new one is easy. The main rule — do not patch foreign code by hand, wrap it.
Example output
Running php demo.php in the terminal produces:
paid 99.5!
declined!
Advantages
- Lets you reuse incompatible code without editing it.
- Isolates the client from a third-party library or legacy API.
- Keeps all conversion in one place — the adapter.
- Lets you swap one implementation for another behind a shared interface.
When to use
- When integrating a third-party or legacy library with a foreign interface.
- When the interfaces of the client and the subsystem do not match.
- When an external dependency should be isolated in one class.
- When several implementations must share one interface.
Usage in Laravel
In Laravel the adapter is Illuminate\Filesystem\FilesystemAdapter: it maps different Flysystem drivers (local, s3, ftp) onto the single Illuminate\Contracts\Filesystem\Filesystem interface with put(), get() and delete(). Cache adapters (Illuminate\Cache\RedisStore), mail transports and queue connectors work the same way: the third-party library is hidden behind a Laravel contract.