fapi-cz/http-client

View on GitHub
src/Fapi/HttpClient/CapturingHttpClient.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php declare(strict_types = 1);

namespace Fapi\HttpClient;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Tester\Dumper;
use function class_exists;
use function file_put_contents;
use function is_file;
use function preg_match;
use function spl_autoload;
use function str_replace;

class CapturingHttpClient implements IHttpClient
{

    /** @var array<RequestInterface> */
    private array $httpRequests = [];

    /** @var array<ResponseInterface> */
    private array $httpResponses = [];

    public function __construct(private IHttpClient $httpClient, private string $file, private string $className)
    {
        if (!class_exists('Tester\Dumper')) {
            throw new InvalidStateException('Capturing HTTP client requires Nette Tester.');
        }

        if (is_file($file)) {
            require_once $file;
            spl_autoload($className);

            if (class_exists($className)) {
                /** @var IHttpClient $httpClient */
                $httpClient = new $className();

                $this->httpClient = $httpClient;
            }
        }
    }

    public function sendRequest(RequestInterface $request): ResponseInterface
    {
        $response = $this->httpClient->sendRequest($request);
        $this->capture($request, $response);

        return $response;
    }

    private function capture(RequestInterface $httpRequest, ResponseInterface $httpResponse): void
    {
        $this->httpRequests[] = $httpRequest;
        $this->httpResponses[] = $httpResponse;
    }

    public function close(): void
    {
        if ($this->httpClient instanceof $this->className) {
            return;
        }

        $this->writeToPhpFile($this->file, $this->className);
    }

    private function writeToPhpFile(string $fileName, string $className): void
    {
        preg_match('#^(?:(.*)\\\\)?([^\\\\]+)\z#', $className, $match);
        [, $namespace, $className] = $match;

        $code = '<?php declare(strict_types = 1);' . "\n";
        $code .= "\n";

        if ((bool) $namespace) {
            $code .= 'namespace ' . $namespace . ';' . "\n";
            $code .= "\n";
        }

        $code .= 'use Fapi\HttpClient\HttpRequest;' . "\n";
        $code .= 'use Fapi\HttpClient\HttpResponse;' . "\n";
        $code .= 'use Fapi\HttpClient\MockHttpClient;' . "\n";
        $code .= "\n";
        $code .= 'final class ' . $className . ' extends MockHttpClient' . "\n";
        $code .= '{' . "\n";
        $code .= "\n";
        $code .= "\t" . 'public function __construct()' . "\n";
        $code .= "\t" . '{' . "\n";

        foreach ($this->httpRequests as $index => $httpRequest) {
            $httpResponse = $this->httpResponses[$index];

            $code .= "\t\t" . '$this->add(' . "\n";
            $code .= "\t\t\t" . 'new HttpRequest(' . "\n";
            $code .= "\t\t\t\t" . $this->exportValue($httpRequest->getMethod(), "\t\t\t\t") . ",\n";
            $code .= "\t\t\t\t" . $this->exportValue((string) $httpRequest->getUri(), "\t\t\t\t") . ",\n";
            $code .= "\t\t\t\t" . $this->exportValue($httpRequest->getHeaders(), "\t\t\t\t") . ",\n";
            $code .= "\t\t\t\t" . $this->exportValue((string) $httpRequest->getBody(), "\t\t\t\t") . ",\n";
            $code .= "\t\t\t\t" . $this->exportValue($httpRequest->getProtocolVersion(), "\t\t\t\t") . ",\n";
            $code .= "\t\t\t" . '),' . "\n";
            $code .= "\t\t\t" . 'new HttpResponse(' . "\n";
            $code .= "\t\t\t\t" . $this->exportValue($httpResponse->getStatusCode(), "\t\t\t\t") . ",\n";
            $code .= "\t\t\t\t" . $this->exportValue($httpResponse->getHeaders(), "\t\t\t\t") . ",\n";
            $code .= "\t\t\t\t" . $this->exportValue((string) $httpResponse->getBody(), "\t\t\t\t") . ",\n";
            $code .= "\t\t\t" . ')' . ",\n";
            $code .= "\t\t" . ');' . "\n";
        }

        $code .= "\t" . '}' . "\n";
        $code .= "\n";
        $code .= '}' . "\n";

        file_put_contents($fileName, $code);
    }

    private function exportValue(mixed $value, string $indent = ''): string
    {
        $s = Dumper::toPhp($value);
        $s = str_replace("\n", "\n" . $indent, $s);

        return $s;
    }

}