PHP: Adapter

Adapter

Adapter is a structural design pattern, which allows incompatible objects to collaborate.

Adapter acts as a wrapper between two objects, It catches calls for one object and transforms them to format and interface recognizable by the second object.

More about Adapter

Application of the pattern in PHP

Complexity:

Popularity:

Usage examples: The Adapter pattern is pretty common in PHP code. It's very often used in systems based on some legacy code. In such cases, Adapters make legacy code with modern classes.

Example: Structure of the Pattern

This example illustrates the structure of the Adapter design pattern and focuses on following questions:

  • What classes does it consists of?
  • What roles do these classes play?
  • In what way the elements of the pattern are related?

After learning about the pattern's structure it will be easier for you to grasp the following example, based on a real world PHP use case.

AdapterStructural.php: Structural Example

<?php

namespace RefactoringGuru\Adapter\Structural;

/**
 * The Target defines the domain-specific interface used by the client code.
 */
class Target
{
    public function request()
    {
        return "Target: The default target's behavior.";
    }
}

/**
 * The Adaptee contains some useful behavior, but its interface is incompatible
 * with the existing client code. The Adaptee needs some adaptation before the
 * client code can use it.
 */
class Adaptee
{
    public function specificRequest()
    {
        return ".eetpadA eht fo roivaheb laicepS";
    }
}

/**
 * The Adapter makes the Adaptee's interface compatible with the Target's
 * interface.
 */
class Adapter extends Target
{
    private $adaptee;

    public function __construct(Adaptee $adaptee)
    {
        $this->adaptee = $adaptee;
    }

    public function request()
    {
        return "Adapter: (TRANSLATED) ".strrev($this->adaptee->specificRequest());
    }
}

/**
 * The client code supports all classes that follow the Target interface.
 */
function clientCode(Target $target)
{
    print($target->request());
}

print("Client: I can work just fine with the Target objects:\n");
$target = new Target();
clientCode($target);
print("\n\n");

$adaptee = new Adaptee();
print("Client: The Adaptee class has a weird interface. See, I don't understand it:\n");
print($adaptee->specificRequest());
print("\n\n");

print("Client: But I can work with it via the Adapter:\n");
$adapter = new Adapter($adaptee);
clientCode($adapter);

Output.txt: Output

Client: I can work just fine with the Target objects:
Target: The default target's behavior.

Client: The Adaptee class has a weird interface. See, I don't understand it:
.eetpadA eht fo roivaheb laicepS

Client: But I can work with it via the Adapter:
Adapter: (TRANSLATED) Special behavior of the Adaptee.

Example: Real World Example

The Adapter pattern allows you to use 3rd-party or legacy classes even if they are incompatible with the bulk of your code. For example, instead of rewriting the notification interface of your app to support each 3rd-party service such as Slack, Facebook, SMS or (you-name-it), you can create a set of special wrappers that adapt calls from your app to an interface and format required by each 3rd-party class.

AdapterRealWorld.php: Real world example

<?php

namespace RefactoringGuru\Adapter\RealWorld;

/**
 * Adapter Design Pattern
 *
 * Intent: Convert the interface of a class into the interface clients expect.
 * Adapter lets classes work together where they otherwise couldn't, due to
 * incompatible interfaces.
 *
 * Example: The Adapter pattern allows you to use 3rd-party or legacy classes
 * even if they are incompatible with the bulk of your code. For example,
 * instead of rewriting the notification interface of your app to support each
 * 3rd-party service such as Slack, Facebook, SMS or (you-name-it), you can
 * create a set of special wrappers that adapt calls from your app to an
 * interface and format required by each 3rd-party class.
 */

/**
 * The Target interface represents the interface that your application's classes
 * already follow.
 */
interface Notification
{
    public function send(string $title, string $message);
}

/**
 * Here's an example of the existing class that follows the Target interface.
 *
 * The truth is that many real apps may not have this interface clearly defined.
 * If you're in that boat, your best bet would be to extend the Adapter from one
 * of your application's existing classes. If that's awkward (for instance,
 * SlackNotification doesn't feel like a subclass of EmailNotification), then
 * extracting an interface should be your first step.
 */
class EmailNotification implements Notification
{
    private $adminEmail;

    public function __construct(string $adminEmail)
    {
        $this->adminEmail = $adminEmail;
    }

    public function send(string $title, string $message)
    {
        mail($this->adminEmail, $title, $message);
        print("Sent email with title '$title' to '{$this->adminEmail}' that says '$message'.");
    }
}

/**
 * The Adaptee is some useful class, incompatible with the Target interface. You
 * can't just go in and change the code of the class to follow the Target
 * interface, since the code might be provided by a 3rd-party library.
 */
class SlackApi
{
    private $login;
    private $apiKey;

    public function __construct(string $login, string $apiKey)
    {
        $this->login = $login;
        $this->apiKey = $apiKey;
    }

    public function logIn()
    {
        // Send authentication request to Slack web service.
        print("Logged in to a slack account '{$this->login}'.\n");
    }

    public function sendMessage($chatId, $message)
    {
        // Send message post request to Slack web service.
        print("Posted following message into the '$chatId' chat: '$message'.\n");
    }
}

/**
 * The Adapter is a class that links the Target interface and the Adaptee class.
 * In this case, it allows the application to send notifications using Slack
 * API.
 */
class SlackNotification implements Notification
{
    private $slack;
    private $chatId;

    public function __construct(SlackApi $slack, string $chatId)
    {
        $this->slack = $slack;
        $this->chatId = $chatId;
    }

    /**
     * An Adapter is not only capable of adapting interfaces, but it can also
     * convert incoming data to the format required by the Adaptee.
     */
    public function send(string $title, string $message)
    {
        $slackMessage = "#" . $title . "# " . strip_tags($message);
        $this->slack->logIn();
        $this->slack->sendMessage($this->chatId, $slackMessage);
    }
}

/**
 * The client code can work with any class that follows the Target interface.
 */
function clientCode(Notification $notification)
{
    // ...

    print($notification->send("Website is down!",
        "<strong style='color:red;font-size: 50px;'>Alert!</strong> " .
        "Our website is not responding. Call admins and bring it up!"));

    // ...
}

print("Client code is designed correctly and works with email notifications:\n");
$notification = new EmailNotification("developers@example.com");
clientCode($notification);
print("\n\n");


print("The same client code can work with other classes via adapter:\n");
$slackApi = new SlackApi("example.com", "XXXXXXXX");
$notification = new SlackNotification($slackApi, "Example.com Developers");
clientCode($notification);

Output.txt: Output

Client code is designed correctly and works with email notifications:
Sent email with title 'Website is down!' to 'developers@example.com' that says '<strong style='color:red;font-size: 50px;'>Alert!</strong> Our website is not responding. Call admins and bring it up!'.

The same client code can work with other classes via adapter:
Logged in to a slack account 'example.com'.
Posted following message into the 'Example.com Developers' chat: '#Website is down!# Alert! Our website is not responding. Call admins and bring it up!'.