Design Pattern: Adapter Pattern in PHP

Adapter pattern is used to convert the interface of a class to another interface, so that the existing class can be adapted to the desired new behavior. This way an existing class can adapt to a new functionality with any direct change to it.

Sometimes we want to use some existing class(es), for some functionality, but the interface of the classes does not match with the new requirement. Adapter pattern is used in that case.

This article is about Adapter pattern implementations in PHP. Check the implementation details and examples below.

Implementation

Follow the steps below to implement the Adapter pattern in PHP-

Prerequisite-

  • An existing interface, which we want to adapt to some new behavior of a new interface.
  • A new interface, to which the existing interface will adapt to.

To implement an adapter we need to follow the steps below-

  • Create an adapter class.
  • Implement the new interface for the adapter class.
  • In the adapter declare a filed of type of the existing(old) interface. And in the constructor accept and set the object of existing(old) class(implementing old interface).
  • In the implementations of the adapter, use/call methods from the old interface object, and use the functionality whenever required.

Here is a simple Adapter pattern implementation in PHP-

<?php
// Adapter pattern implementation in PHP

// Old interface
interface OldInterface {
    function operation1(): void;
    function operation2(): void;
}

// Old interface implementation
class OldExample implements OldInterface {
    public function operation1(): void {
        echo "Operation 1 of Old Example\n";
    }

    public function operation2(): void {
        echo "Operation 2 of Old Example\n";
    }
}

// New interface
interface NewInterface {
    function newOperation1(): void;
    function newOperation2(): void;
    function newOperation3(): void;
}

// New interface implementation
class NewExample implements NewInterface {
    public function newOperation1(): void {
        echo "New Operation 1 of New Example\n";
    }

    public function newOperation2(): void {
        echo "New Operation 2 of New Example\n";
    }

    public function newOperation3(): void {
        echo "New Operation 3 of New Example\n";
    }
}

// Adapter
class Adapter implements NewInterface {
    private OldInterface $oldInterface;

    public function __construct(OldInterface $oldInterface) {
        $this->oldInterface = $oldInterface;
    }

    public function newOperation1(): void {
        echo "Inside New Operation 1 of Adapter\n";

        $this->oldInterface->operation1();
    }

    public function newOperation2(): void {
        echo "Inside New Operation 2 of Adapter\n";

        $this->oldInterface->operation2();
    }

    public function newOperation3(): void {
        throw new Exception("Operation not available in Adapter");
    }
}


// Demo
$oldExample = new OldExample();
$adapter = new Adapter($oldExample);

$adapter->newOperation1();
$adapter->newOperation2();

This will generate following output.

Inside New Operation 1 of Adapter
Operation 1 of Old Example

Inside New Operation 2 of Adapter
Operation 2 of Old Example

Examples

Here are a few examples of Adapter pattern in PHP.

Example #1: API with File Adapter

Pre-existing Interface

We have an interface and its implementation that performs file operations (read/write etc.). The interface name is “FileOp” and the implementation is “FileOperation“.

New Requirement/Changes

Later we created an interface for API, named “Api“. There are implementations of the “Api” interface – “NativeApi“, and “ThirdPartyApi“.

Adapter Requirement

We want the file operations to behave like the API interface, for ease of use. So that the client/user does not realize any difference while performing a native or 3rd party API call, and file operations.

Let’s start the implementation-

File Interface

  • Create a file “FileOp.php”.
  • Create interface “FileOp”.
  • Declare methods for file operations, here we have methods – “readFile”, “writeFile”.
<?php
// FileOp.php

namespace BigBoxCode\DesignPattern\Adapter\FileAdapter;

interface FileOp {
    function readFile(): string;
    function writeFile(string $input): void;
}

FileOperation Class [implements File Interface]

  • Create a file “FileOperation.php”.
  • Create class “FileOperation”.
  • Implement “FileOp” interface for the class. Define method “readFile” and “writeFile” as part of the interface implementation.
<?php
// FileOperation

namespace BigBoxCode\DesignPattern\Adapter\FileAdapter;


class FileOperation implements FileOp {
    public function readFile(): string {
        echo "Reading from file\n";

        return "some dummy response read from file";
    }

    public function writeFile(string $input): void {
        echo "Writing to file: " . $input . "\n";
    }
}

API Interface

  • Create a file “Api.php”.
  • Define interface “Api”.
  • Declare methods – “fetchData”, “sendData”.
<?php
// Api.php

namespace BigBoxCode\DesignPattern\Adapter\FileAdapter;


interface Api {
    function fetchData(): string;
    function sendData(string $data): void;
}

Native API Class [implements Api interface]

  • Create a file “NativeApi.php”.
  • Create class “NativeApi”.
  • Implement “Api” interface. Define methods “fetchData” and “sendData”.
<?php
// NativeApi.php

namespace BigBoxCode\DesignPattern\Adapter\FileAdapter;


class NativeApi implements Api {
    public function fetchData(): string {
        echo "Fetching data from Native API\n";

        return "Data read from Native Api";
    }

    public function sendData(string $data): void {
        echo "Sending data to Native API: " . $data . "\n";
    }
}

Third-Party API Class [implements Api interface]

  • Create a file “ThirdPartyApi.php”.
  • Create class “ThirdPartyApi”.
  • Implement interface “Api” for the class.
<?php
// ThirdPartyApi.php

namespace BigBoxCode\DesignPattern\Adapter\FileAdapter;


class ThirdPartyApi implements Api {
    public function fetchData(): string {
        echo "Fetching data from Third Party API\n";

        return "Data read from Third Party Api";
    }

    public function sendData(string $data): void {
        echo "Sending data to Third Party API: " . $data . "\n";
    }
}

To adapt to the API interface, let’s create an adapter.

File Adapter

  • Create file “FileAdapter.php”.
  • Define class “FileAdapter”.
  • Define a private property “$fileOp” of the interface “FileOp”. Accept a param in the constructor and set the value of “$fileOp”.
  • Implement interface “Api” for “FileAdapter”. Define methods “fetchData” and “sendData” as part of the interface implementation.
  • In the method implementations call/use methods of “$fileOp”, whenever required and/or possible.
<?php
// FileAdapter.php

namespace BigBoxCode\DesignPattern\Adapter\FileAdapter;


class FileAdapter implements Api {
    public function __construct(private FileOp $fileOp) {
    }

    public function fetchData(): string {
        return $this->fileOp->readFile();
    }

    public function sendData(string $data): void {
        $this->fileOp->writeFile($data);
    }
}

Demo

In the client we have to create an object of the “FileOperation” class first, then we have to pass the “FileOperation” object to the “FileAdapter”. Then we can use the “FileAdpater” object just like other API classes, as it implements the “Api” interface.

<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';

use BigBoxCode\DesignPattern\Adapter\FileAdapter\FileAdapter;
use BigBoxCode\DesignPattern\Adapter\FileAdapter\FileOperation;
use BigBoxCode\DesignPattern\Adapter\FileAdapter\ThirdPartyApi;

// make a call to third part API for testing
$thirdPartyApi = new ThirdPartyApi();
$thirdPartyApi->fetchData();
$thirdPartyApi->sendData("1234");


// Make a call to the file via FileAdapter
$file = new FileOperation();
$fileAdapter = new FileAdapter($file);
$fileAdapter->fetchData();
$fileAdapter->sendData("ABCDEF");

Output

Output will be as below

Fetching data from Third Party API
Sending data to Third Party API: 1234

Reading from file
Writing to file: ABCDEF

Example #2: Transport

Pre-existing Interface

We have “Plane” and “Helicopter” classes that implement the “AirTransport” adapter.

New Requirement/Changes

Later we created a general “Transport” interface. And a few classes that implement the general “Transport” interface.

Adapter Requirement

We want the “AirTransport” interface to adapt to the new general “Transport” interface.

Let’s check the implementation-

AirTransport Interface

  • Create file “AirTransport.php”.
  • Define interface “AirTrasport”.
  • Declare methods – “getNumberOfWheels”, “getNumberOfEngines”, “getWeight”, “getDistanceTravelled”, “getTravelCosTotal”.
<?php
// AirTransport.php

namespace BigBoxCode\DesignPattern\Adapter\TransportAdapter;

interface AirTransport {
    public function getNumberOfWheels(): int;
    public function getNumberOfEngines(): int;
    public function getWeight(): float;
    public function getDistanceTravelled(): float;
    public function getTravelCostTotal(): float;
}

Plane Class [implements AirTransport]

  • Create file “Plane.php”.
  • Define the class “Plane”.
  • Implement “AirTransport” interface for the class.
<?php
// Plane.php

namespace BigBoxCode\DesignPattern\Adapter\TransportAdapter;


class Plane implements AirTransport {
    public function getNumberOfWheels(): int {
        return 3;
    }

    public function getNumberOfEngines(): int {
        return 2;
    }

    public function getWeight(): float {
        return 127_000;
    }

    public function getDistanceTravelled(): float {
        return 500;
    }

    public function getTravelCostTotal(): float {
        return 3_000;
    }
}

Helicopter Class [implements AirTransport]

  • Create file “Helicopter.php”.
  • Define class “Helicopter”.
  • Implement “AirTransport” interface.
<?php
// Helicopter.php

namespace BigBoxCode\DesignPattern\Adapter\TransportAdapter;

class Helicopter implements AirTransport {
    public function getNumberOfWheels(): int {
        return 0;
    }

    public function getNumberOfEngines(): int {
        return 1;
    }

    public function getWeight(): int {
        return 12000;
    }

    public function getDistanceTravelled(): int {
        return 180;
    }

    public function getTravelCostTotal(): int {
        return 20000;
    }
}

Here is the new interface, an interface for transports in general-

Transport Interface

  • Create file “Transport.php”.
  • Define interface “Transport”.
  • Declare methods – “getNumerOfWheels”, “getWeight”, “getDistanceTravelled”, “getTravelCosPerMile”.
<?php
// Transport.php

namespace BigBoxCode\DesignPattern\Adapter\TransportAdapter;


interface Transport {
    public function getNumberOfWheels(): int;
    public function getWeight(): float;
    public function getDistanceTravelled(): float;
    public function getTravelCostPerMile(): float;
}

Bus Class [implements Transport]

  • Create file “Bus.php”.
  • Create “Bus” class.
  • Implement “Transport” interface for the “Bus” class.
<?php
// Bus.php

namespace BigBoxCode\DesignPattern\Adapter\TransportAdapter;


class Bus implements Transport {
    public function getNumberOfWheels(): int {
        return 4;
    }

    public function getWeight(): float {
        return 10_000;
    }

    public function getDistanceTravelled(): float {
        return 1_000;
    }
    
    public function getTravelCostPerMile(): float {
        return 5;
    }
}

Bike Class [implements Transport]

  • Create file “Bike.php”.
  • Create class “Bike” and implement the “Transport” interface for the class.
<?php
// Bike.php

namespace BigBoxCode\DesignPattern\Adapter\TransportAdapter;


class Bike implements Transport {
    public function getNumberOfWheels(): int {
        return 2;
    }

    public function getWeight(): float {
        return 700;
    }

    public function getDistanceTravelled(): float {p
        return 80;
    }

    public function getTravelCostPerMile(): float {
        return 4;
    }
}

Let’s create the adapter now.

AirTransportAdapter Class [implements Transport]

  • Create file “AirTransportAdapter.php”.
  • Create class “AirTransportAdapter”.
  • Declare a private property “$airTransport” of type of “AirTransport”.
  • In the constructor accept and set the value of “$airTransport”.
  • Implement “Transport” interface for the class.  
  • In the method implementations, use the methods from the “$airTransport” object.
<?php
// AirTransportAdapter.php

namespace BigBoxCode\DesignPattern\Adapter\TransportAdapter;


class AirTransportAdapter implements Transport {
    public function __construct(private AirTransport $airTransport) {
        $this->airTransport = $airTransport;
    }

    public function getNumberOfWheels(): int {
        return $this->airTransport->getNumberOfWheels();
    }

    public function getWeight(): float {
        return $this->airTransport->getWeight();
    }

    public function getDistanceTravelled(): float {
        $distanceInNauticalMile = $this->airTransport->getDistanceTravelled();
        return $distanceInNauticalMile * 1.151;
    }

    public function getTravelCostPerMile(): float {
        $totalCost = $this->airTransport->getTravelCostTotal();
        return $totalCost / $this->getDistanceTravelled();
    }
}

Demo

While creating an “AirTransportAdapter” object, we need to pass an object of the “Plane” or “Helicopter” (classes that implement the “AirTransport” interface). Then we can use the adapter object.

<?php
// demo.php

require __DIR__ . '/../../vendor/autoload.php';

use BigBoxCode\DesignPattern\Adapter\TransportAdapter\AirTransportAdapter;
use BigBoxCode\DesignPattern\Adapter\TransportAdapter\Bus;
use BigBoxCode\DesignPattern\Adapter\TransportAdapter\Plane;

echo "Get information of Bus travel...";

$bus = new Bus();
echo "\nNumber of wheels: " . $bus->getNumberOfWheels();
echo "\nWeight(kg): " . $bus->getWeight();
echo "\nDistance(miles): " . $bus->getDistanceTravelled();
echo "\nCost per mile: " . $bus->getTravelCostPerMile();


echo "\n\nGet information of Plane travel...";

$planeTransport = new AirTransportAdapter(new Plane());
echo "\nNumber of wheels: " . $planeTransport->getNumberOfWheels();
echo "\nWeight(kg): " . $planeTransport->getWeight();
echo "\nDistance(miles): " . $planeTransport->getDistanceTravelled();
echo "\nCost per mile: " . $planeTransport->getTravelCostPerMile();

Output

Output will be as below-

Get information of Bus travel...
Number of wheels: 4
Weight(kg): 10000
Distance(miles): 1000
Cost per mile: 5

Get information of Plane travel...
Number of wheels: 3
Weight(kg): 127000
Distance(miles): 575.5
Cost per mile: 5.2128583840139

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Adapter pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.