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 StripeThe 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
EasyLet'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 aTemperatureSensorinterface that your weather application expects. It should have a methodgetTemperature(): floatthat returns the temperature in Celsius.LegacySensor.php— Create aLegacySensorclass 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(): floatthat returns the stored Fahrenheit value
LegacySensorAdapter.php— Include both the TemperatureSensor interface and LegacySensor class. Create aLegacySensorAdapterclass that implementsTemperatureSensorand wraps aLegacySensor. Your adapter should:- Accept a
LegacySensorin 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
- Accept a
main.php— Include the LegacySensorAdapter file. You'll receive one input: a temperature in Fahrenheit.Create a
LegacySensorwith the input temperature, then wrap it in aLegacySensorAdapter. CallgetTemperature()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 StripeThe 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
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System10Advanced OOP Concepts
Composition vs InheritanceDependency InjectionAnonymous ClassesEnums (PHP 8.1)Fibers (PHP 8.1)Object Cloning Deep DiveGenerators & Iterators13Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite PatternRepository Pattern2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern