Menu
Coddy logo textTech

Adapter Pattern

Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 76 of 91.

The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. It acts as a bridge between two interfaces, translating calls from one format to another.

Imagine you're building an application that processes payments, and you need to integrate a third-party payment library. The library works perfectly, but its interface doesn't match what your code expects. Instead of rewriting your entire application, you create an adapter that translates between the two.

<?php
// The interface your application expects
interface PaymentProcessor {
    public function pay(float $amount): string;
}

// Third-party library with a different interface
class StripeAPI {
    public function createCharge(int $cents, string $currency): string {
        return "Charged {$cents} {$currency} via Stripe";
    }
}

The adapter implements your expected interface while internally using the third-party class:

<?php
class StripeAdapter implements PaymentProcessor {
    public function __construct(private StripeAPI $stripe) {}
    
    public function pay(float $amount): string {
        $cents = (int)($amount * 100);
        return $this->stripe->createCharge($cents, 'USD');
    }
}

$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);

Output:

Charged 2999 USD via Stripe

The adapter converts dollars to cents and calls the appropriate method. Your application code only knows about PaymentProcessor, remaining blissfully unaware of Stripe's specific requirements. If you later switch to a different payment provider, you simply create a new adapter without changing your existing code.

challenge icon

Challenge

Easy

Let's build a temperature conversion system using the Adapter Pattern. You have a legacy temperature sensor that outputs readings in Fahrenheit, but your modern weather application expects all temperatures in Celsius. Instead of modifying either system, you'll create an adapter that bridges the gap.

You'll organize your code across four files:

  • TemperatureSensor.php — Define a TemperatureSensor interface that your weather application expects. It should have a method getTemperature(): float that returns the temperature in Celsius.
  • LegacySensor.php — Create a LegacySensor class that simulates the old hardware. This class has a different interface than what your application needs:
    • It takes a Fahrenheit reading in its constructor (use constructor promotion)
    • It has a method readFahrenheit(): float that returns the stored Fahrenheit value
  • LegacySensorAdapter.php — Include both the TemperatureSensor interface and LegacySensor class. Create a LegacySensorAdapter class that implements TemperatureSensor and wraps a LegacySensor. Your adapter should:
    • Accept a LegacySensor in its constructor
    • Implement getTemperature() by reading the Fahrenheit value from the legacy sensor and converting it to Celsius using the formula: (fahrenheit - 32) * 5 / 9
  • main.php — Include the LegacySensorAdapter file. You'll receive one input: a temperature in Fahrenheit.

    Create a LegacySensor with the input temperature, then wrap it in a LegacySensorAdapter. Call getTemperature() on the adapter and print the result rounded to two decimal places.

    The output format should be: Temperature: [celsius]C

This challenge demonstrates how the Adapter Pattern lets you integrate incompatible interfaces without modifying existing code. Your weather application works exclusively with the TemperatureSensor interface, completely unaware that the actual readings come from a legacy Fahrenheit sensor.

Cheat sheet

The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together by acting as a bridge between them.

Basic Structure

Define the interface your application expects:

<?php
interface PaymentProcessor {
    public function pay(float $amount): string;
}

A third-party class with a different interface:

<?php
class StripeAPI {
    public function createCharge(int $cents, string $currency): string {
        return "Charged {$cents} {$currency} via Stripe";
    }
}

Creating the Adapter

The adapter implements your expected interface while internally using the third-party class:

<?php
class StripeAdapter implements PaymentProcessor {
    public function __construct(private StripeAPI $stripe) {}
    
    public function pay(float $amount): string {
        $cents = (int)($amount * 100);
        return $this->stripe->createCharge($cents, 'USD');
    }
}

Usage

$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
// Output: Charged 2999 USD via Stripe

The adapter translates between interfaces, allowing your application code to remain unchanged when integrating third-party libraries or switching implementations.

Try it yourself

<?php
require_once 'LegacySensorAdapter.php';

// Read input
$fahrenheit = floatval(trim(fgets(STDIN)));

// TODO: Create a LegacySensor with the input temperature
// TODO: Wrap it in a LegacySensorAdapter
// TODO: Call getTemperature() on the adapter
// TODO: Print the result rounded to two decimal places
// Output format: Temperature: [celsius]C

?>
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming