<?php

$exampleData = [
    "name" => "Johhny",
    "surname" => "Marrony",
    "company" => "Company",
    "position" => "Senior Doorkeeper"
];

$jsonContext = new Context('json');
echo $jsonContext->formatData($exampleData) . PHP_EOL;

$stringContext = new Context('string');
echo $stringContext->formatData($exampleData) . PHP_EOL;

$xmlContext = new Context('xml');
echo $xmlContext->formatData($exampleData) . PHP_EOL;


// 
interface OutputFormatter {

    public function format (array $data): string;

}


class Context {

    private $formatter;

    public function __construct (string $outputType) {
        switch ($outputType) {
            case "json":
                $this->formatter = new JSONFormatter();
                break;
            case "xml":
                $this->formatter = new XMLFormatter();
                break;
            case "string":
                $this->formatter = new StringFormatter();
                break;
            default:
                throw new \InvalidArgumentException("{$outputType} is not supported");
        }
    }

    public function formatData (array $data): string {
        return $this->formatter->format($data);
    }
}



use SimpleXMLElement;
class XMLFormatter implements OutputFormatter {

    public function format (array $data): string {
        $xml = $this->addData($data, new SimpleXMLElement('<root/>'));

        return $xml->asXML();
    }

    protected function addData (array $data, SimpleXMLElement $xml) {
        foreach ($data as $k => $v) {
            is_array($v)
                ? $this->addData($v, $xml->addChild($k))
                : $xml->addChild($k, $v);
        }
        return $xml;
    }
}


class StringFormatter implements OutputFormatter {

    const DELIMITER = ",";

    public function format (array $data): string {
        return implode(self::DELIMITER, $data);
    }
}


class JSONFormatter implements OutputFormatter {

    public function format (array $data): string {
        return json_encode($data);
    }
}