soliantconsulting/SimpleFM

View on GitHub
src/Connection/Connection.php

Summary

Maintainability
A
35 mins
Test Coverage
A
100%
<?php
declare(strict_types = 1);

namespace Soliant\SimpleFM\Connection;

use Assert\Assertion;
use Http\Client\HttpClient;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use SimpleXMLElement;
use Soliant\SimpleFM\Authentication\IdentityHandlerInterface;
use Soliant\SimpleFM\Connection\Exception\InvalidResponseException;
use Zend\Diactoros\Request;
use Zend\Diactoros\Stream;

final class Connection implements ConnectionInterface
{
    /**
     * @var HttpClient
     */
    private $httpClient;

    /**
     * @var UriInterface
     */
    private $uri;

    /**
     * @var string
     */
    private $database;

    /**
     * @var IdentityHandlerInterface|null
     */
    private $identityHandler;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(
        HttpClient $httpClient,
        UriInterface $uri,
        string $database,
        IdentityHandlerInterface $identityHandler = null,
        LoggerInterface $logger = null
    ) {
        $this->httpClient = $httpClient;
        $this->uri = $uri;
        $this->database = $database;
        $this->identityHandler = $identityHandler;
        $this->logger = $logger ?: new NullLogger();
    }

    public function execute(Command $command, string $grammarPath) : SimpleXMLElement
    {
        $uri = $this->uri->withPath($grammarPath);
        $response = $this->httpClient->sendRequest($this->buildRequest($command, $uri));

        if (200 !== (int) $response->getStatusCode()) {
            throw InvalidResponseException::fromUnsuccessfulResponse($response);
        }

        $previousValue = libxml_use_internal_errors(true);
        $xml = simplexml_load_string((string) $response->getBody());
        libxml_use_internal_errors($previousValue);

        if (false === $xml) {
            throw InvalidResponseException::fromXmlError(libxml_get_last_error());
        }

        return $xml;
    }

    public function getAsset(string $assetUri) : StreamInterface
    {
        $assetUriParts = parse_url($assetUri);
        $uri = $this->uri->withUserInfo('');

        if (array_key_exists('path', $assetUriParts)) {
            $uri = $uri->withPath($assetUriParts['path']);
        }

        if (array_key_exists('query', $assetUriParts)) {
            $uri = $uri->withQuery($assetUriParts['query']);
        }

        $request = (new Request($uri, 'GET'))
            ->withAddedHeader('User-agent', 'SimpleFM');

        $credentials = urldecode($this->uri->getUserInfo());

        if ('' !== $credentials) {
            $request = $request->withAddedHeader('Authorization', sprintf('Basic %s', base64_encode($credentials)));
        }

        $response = $this->httpClient->sendRequest($request);

        if (200 !== (int) $response->getStatusCode()) {
            throw InvalidResponseException::fromUnsuccessfulResponse($response);
        }

        return $response->getBody();
    }

    private function buildRequest(Command $command, UriInterface $uri) : RequestInterface
    {
        $parameters = sprintf('-db=%s&%s', urlencode($this->database), $command);

        $body = new Stream('php://temp', 'wb+');
        $body->write($parameters);
        $body->rewind();

        $request = (new Request($uri->withUserInfo(''), 'POST'))
            ->withAddedHeader('User-agent', 'SimpleFM')
            ->withAddedHeader('Content-type', 'application/x-www-form-urlencoded')
            ->withAddedHeader('Content-length', (string) strlen($parameters))
            ->withBody($body);

        $credentials = urldecode($uri->getUserInfo());

        if ($command->hasIdentity()) {
            Assertion::notNull($this->identityHandler, 'An identity handler must be set to use identities on commands');
            $identity = $command->getIdentity();

            $credentials = sprintf(
                '%s:%s',
                $identity->getUsername(),
                $this->identityHandler->decryptPassword($identity)
            );
        }

        $this->logger->info(sprintf('%s?%s', (string) $uri->withUserInfo(''), $parameters));

        if ('' === $credentials) {
            return $request;
        }

        return $request->withAddedHeader('Authorization', sprintf('Basic %s', base64_encode($credentials)));
    }
}