/ Gists / 5. Adapter
On gists

5. Adapter

PHP Patterns

pic.md Raw #

adapter.php Raw #

<?php

class Facebook {    
    public function getUserToken($userId) {
        // code to get user token
    }

    public function postUpdate($message) {
        // code to post status update
    }
}

class Twitter {    
    public function checkUserToken($userId) {
        // code to get user token
    }

    public function setStatusUpdate($message) {
        // code to post status update
    }
}

interface iStatusUpdate {
    function getUserToken($userId);
    function postUpdate($message);
}

class TwitterAdapter implements iStatusUpdate {

    protected $twitter;

    public function __construct(Twitter $twitter){
        $this->twitter = $twitter;
    }

    public function getUserToken($userId) {
        $this->twitter->checkUserToken($userId);
    }

    public function postUpdate($message) {
        $this->twitter->setStatusUpdate($message);
    }
}


class SomeOtherServiceAdapter implements iStatusUpdate {

    protected $otherService;

    public function __construct(SomeOtherService $otherService){
        $this->otherService = $otherService;
    }

    public function getUserToken($userId) {
        $this->otherService->authenticate($userId);
    }

    public function postUpdate($message) {
        $this->otherService->postMessage($message);
    }
}


// USAGE

$statusUpdate = new TwitterAdapter(new Twitter);
$statusUpdate->getUserToken($someUserId);
$statusUpdate->postUpdate('some message');

// or
$statusUpdate = new SomeOtherServiceAdapter(new SomeOtherService);
$statusUpdate->getUserToken($someUserId);
$statusUpdate->postUpdate('some message');

Adapter2.php Raw #

<?php

interface Notification
{
    public function send(string $title, string $message);
}


class EmailNotification implements Notification
{
    private $adminEmail;

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

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


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): void
    {
        $slackMessage = "#" . $title . "# " . strip_tags($message);
        $this->slack->logIn();
        $this->slack->sendMessage($this->chatId, $slackMessage);
    }
}


class SlackApi
{
    private $login;
    private $apiKey;

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

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

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


// USAGE

function clientCode(Notification $notification)
{
    // ...

    echo $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!");

    // ...
}

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


echo "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);

Adapter3.php Raw #

<?php

interface Book
{
    public function turnPage();

    public function open();

    public function getPage(): int;
}


interface EBook
{
    public function unlock();

    public function pressNext();

    /**
     * returns current page and total number of pages, like [10, 100] is page 10 of 100
     *
     * @return int[]
     */
    public function getPage(): array;
}


class PaperBook implements Book
{
    private int $page;

    public function open()
    {
        $this->page = 1;
    }

    public function turnPage()
    {
        $this->page++;
    }

    public function getPage(): int
    {
        return $this->page;
    }
}


class Kindle implements EBook
{
    private int $page = 1;
    private int $totalPages = 100;

    public function pressNext()
    {
        $this->page++;
    }

    public function unlock()
    {
    }

    /**
     * returns current page and total number of pages, like [10, 100] is page 10 of 100
     *
     * @return int[]
     */
    public function getPage(): array
    {
        return [$this->page, $this->totalPages];
    }
}


class EBookAdapter implements Book
{
    protected EBook $eBook;

    public function __construct(EBook $eBook)
    {
        $this->eBook = $eBook;
    }

    /**
     * This class makes the proper translation from one interface to another.
     */
    public function open()
    {
        $this->eBook->unlock();
    }

    public function turnPage()
    {
        $this->eBook->pressNext();
    }

    /**
     * notice the adapted behavior here: EBook::getPage() will return two integers, but Book
     * supports only a current page getter, so we adapt the behavior here
     */
    public function getPage(): int
    {
        return $this->eBook->getPage()[0];
    }
}


// USAGE
$book = new PaperBook();
$book->open();
$book->turnPage();

$kindle = new Kindle();
$book = new EBookAdapter($kindle);
$book->open();
$book->turnPage();

Adapter4.php Raw #

<?php

/**
 * interface for creating render classes
 */
interface RenderTemplateInterface 
{
    public function renderHeader();
    public function renderBody();
    public function renderFooter();
}
 
/**
 * Used for rendering HTML templates
 */
class RenderHTMLTemplate implements RenderTemplateInterface 
{
 
    public function renderHeader() 
    {
        return "<html><body>";
    }
 
    public function renderBody() 
    {
        return "Hello World";
    }
 
    public function renderFooter() 
    {
        return "</body></html>";
    }
 
}
 
/**
 * Separate interface just for rendering PDF's
 * Having a separate interface from RenderTemplateInterface could be a requirement from a third party
 */
interface PDFTemplateInterface 
{
    public function renderTop();
    public function renderMiddle();
    public function renderBottom();
}
 
/**
 * Used for rendering PDF templates
 */
class RenderPDFTemplate implements PDFTemplateInterface 
{
 
    public function renderTop() 
    {
        return "This is the top of the PDF";
    }
 
    public function renderMiddle() 
    {
        return "Hello World";
    }
 
    public function renderBottom() 
    {
        return "This is the bottom of the PDF";
    }
 
}
 
/**
 * The adapter - this will encapsulate an instance of the RenderPDFTemplate class
 * to work polymorphically with the RenderTemplateInterface interface
 */
class PDFTemplateAdapter implements RenderTemplateInterface
{
    private $pdfTemplate;
 
    public function __construct(PDFTemplateInterface $pdfTemplate)
    {
        $this->pdfTemplate = $pdfTemplate;
    }
 
    public function renderHeader() 
    {
        return $this->pdfTemplate->renderTop();
    }
 
    public function renderBody() 
    {
        return $this->pdfTemplate->renderMiddle();
    }
 
    public function renderFooter() 
    {
        return $this->pdfTemplate->renderBottom();
    }
}
 
$pdfTemplate = new RenderPDFTemplate();
 
// $pdfTemplateAdapter will implement RenderTemplateInterface, just like RenderHTMLTemplate does
$pdfTemplateAdapter = new PDFTemplateAdapter($pdfTemplate);
 
// This is the top of the PDF
echo $pdfTemplateAdapter->renderHeader();