PHP Adapter Design Pattern Structural Pattern

PHP Adapter Pattern

A short screencast: the Adapter pattern in PHP — fitting an alien API into a single contract without touching legacy code.

Adapter Pattern — open the lesson page

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

classDiagram class PaymentInterface { <<interface>> +process(amount: float) string } class LegacyPaymentGateway { +charge(amount: float) bool } class PaymentAdapter { -gateway LegacyPaymentGateway +process(amount: float) string } PaymentAdapter ..|> PaymentInterface : implements PaymentAdapter o-- LegacyPaymentGateway : wraps

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.