mollie / mollie-api-php

Mollie API client for PHP
http://www.mollie.com
BSD 2-Clause "Simplified" License
550 stars 190 forks source link

Allow PSR-18 Http Clients #703

Open KDederichs opened 10 months ago

KDederichs commented 10 months ago

Specifications

Describe the issue

Hi,

I was wondering if you guys could add PSR18 (https://github.com/php-fig/http-client) as supported option for Http Clients.

The reason I'm asking is I'd like to use something like https://docs.php-http.org/en/latest/clients/mock-client.html in integration tests to mock mollie responses.

Sadly it currently only seems to support Guzzle's ClientInterface and your own one.

Naoray commented 10 months ago

Hi @KDederichs,

thanks for your suggestion. I will discuss this internally.

Nevertheless, this would be a breaking change and therefore won’t be included in any release before v3, which is scheduled for early next year.

sandervanhooft commented 3 months ago

How about adding a Mollie\Api\HttpAdapter\PSR18MollieHttpAdapter here that implements the Mollie\Api\HttpAdapter\ MollieHttpAdapterInterface, which in turn accepts a PSR18 enabled client on the constructor? I think that way it's possible to introduce PSR18 support without a breaking change.

@KDederichs would you need PSR17 support as well (RequestFactoryInterface, StreamFactoryInterface)?

sandervanhooft commented 3 months ago

For example (not tested):

<?php

namespace Mollie\Api\HttpAdapter;

use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Mollie\Api\Exceptions\ApiException;

class PSR18MollieHttpAdapter implements MollieHttpAdapterInterface
{
    /**
     * @var ClientInterface
     */
    private $httpClient;

    /**
     * @var RequestFactoryInterface
     */
    private $requestFactory;

    /**
     * @var StreamFactoryInterface
     */
    private $streamFactory;

    /**
     * PSR18MollieHttpAdapter constructor.
     *
     * @param ClientInterface $httpClient
     * @param RequestFactoryInterface $requestFactory
     * @param StreamFactoryInterface $streamFactory
     */
    public function __construct(ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory)
    {
        $this->httpClient = $httpClient;
        $this->requestFactory = $requestFactory;
        $this->streamFactory = $streamFactory;
    }

    /**
     * {@inheritdoc}
     */
    public function send($httpMethod, $url, $headers, $httpBody)
    {
        try {
            $request = $this->createRequest($httpMethod, $url, $headers, $httpBody);
            $response = $this->httpClient->sendRequest($request);

            $body = (string) $response->getBody();
            return json_decode($body);
        } catch (\Exception $e) {
            throw new ApiException("Error while sending request to Mollie API: " . $e->getMessage(), 0, $e);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function versionString()
    {
        return 'PSR18MollieHttpAdapter';
    }

    /**
     * Create a PSR-7 request.
     *
     * @param string $httpMethod
     * @param string $url
     * @param string|array $headers
     * @param string $httpBody
     * @return RequestInterface
     */
    private function createRequest($httpMethod, $url, $headers, $httpBody)
    {
        $request = $this->requestFactory->createRequest($httpMethod, $url);

        if (is_array($headers)) {
            foreach ($headers as $name => $value) {
                $request = $request->withHeader($name, $value);
            }
        } else {
            // Assuming headers is a string in the form of 'Header-Name: Header-Value'
            $headerLines = explode("\r\n", $headers);
            foreach ($headerLines as $line) {
                list($name, $value) = explode(': ', $line, 2);
                $request = $request->withHeader($name, $value);
            }
        }

        $stream = $this->streamFactory->createStream($httpBody);
        $request = $request->withBody($stream);

        return $request;
    }
}
KDederichs commented 3 months ago

That looks like it would work yeah.

sandervanhooft commented 2 months ago

@Naoray what do you think?