diff --git a/composer.json b/composer.json index 2a13f059..78494217 100644 --- a/composer.json +++ b/composer.json @@ -41,11 +41,11 @@ "php": "^7.1", "moneyphp/money": "^3.1", "php-http/discovery": "^1.6", + "php-http/message": "^1.5", "psr/http-client": "^1", "psr/http-client-implementation": "^1", "psr/http-factory": "^1", - "symfony/http-foundation": "^2.1||^3||^4", - "zendframework/zend-diactoros": "^2.1" + "symfony/http-foundation": "^2.1||^3||^4" }, "require-dev": { "omnipay/tests": "^3", diff --git a/src/Common/Http/Client.php b/src/Common/Http/Client.php index e6f12bfc..cb1a392c 100644 --- a/src/Common/Http/Client.php +++ b/src/Common/Http/Client.php @@ -2,37 +2,130 @@ namespace Omnipay\Common\Http; -use Http\Discovery\Psr17FactoryDiscovery; -use Http\Discovery\Psr18ClientDiscovery; use Omnipay\Common\Http\Exception\NetworkException; use Omnipay\Common\Http\Exception\RequestException; +use Http\Client\HttpClient; +use Http\Discovery\HttpClientDiscovery; +use Http\Discovery\Exception\NotFoundException as DiscoveryNotFoundException; +use Http\Discovery\MessageFactoryDiscovery; +use Http\Discovery\Psr17FactoryDiscovery; +use Http\Discovery\Psr18ClientDiscovery; +use Http\Message\RequestFactory; +use Psr\Http\Message\RequestFactoryInterface as Psr17RequestFactoryInterface; +use Psr\Http\Message\StreamFactoryInterface; +use Psr\Http\Message\UriFactoryInterface; use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; use Psr\Http\Client\ClientInterface as Psr18ClientInterface; -use Psr\Http\Message\RequestFactoryInterface as Psr17RequestFactoryInterface; -final class Client implements ClientInterface +class Client implements ClientInterface { /** * The Http Client which implements `public function sendRequest(RequestInterface $request)` * - * @var Psr18ClientInterface + * @var Psr18ClientInterface|HttpClient */ private $httpClient; /** - * @var Psr17RequestFactoryInterface + * @var Psr17RequestFactoryInterface|RequestFactory */ private $requestFactory; - public function __construct($httpClient = null, $requestFactory = null) + /** + * @var StreamFactoryInterface + */ + private $streamFactory; + + /** + * @var UriFactoryInterface + */ + private $uriFactory; + + public function __construct( + $httpClient = null, + $requestFactory = null, + $streamFactory = null, + $uriFactory = null + ) { + $this->httpClient = $httpClient; + $this->requestFactory = $requestFactory; + $this->streamFactory = $streamFactory; + $this->uriFactory = $uriFactory; + } + + /** + * DRY factory getter + * Loads a factory into memory only if requested + * + * @param $propertyName camelCased property name + * @return factory set in constructor, otherwise the discovered by default + */ + private function getFactory(string $propertyName) { - $this->httpClient = $httpClient ?: Psr18ClientDiscovery::find(); - $this->requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory(); + if (empty($this->{$propertyName})) { + $this->{$propertyName} = Psr17FactoryDiscovery::{'find' . ucfirst($propertyName)}(); + } + + return $this->{$propertyName}; + } + + protected function getHttpClient() + { + if (empty($this->httpClient)) { + try { + $this->httpClient = Psr18ClientDiscovery::find(); + } catch (DiscoveryNotFoundException $e) { + $this->httpClient = HttpClientDiscovery::find(); + } + } + return $this->httpClient; + } + + protected function getRequestFactory() + { + // TODO: After dropping MessageFactoryDiscovery support + // replace contents of this function with the following line: + // return $this->getFactory('requestFactory'); + + if (empty($this->requestFactory)) { + try { + $this->requestFactory = Psr17FactoryDiscovery::findRequestFactory(); + } catch (DiscoveryNotFoundException $e) { + $this->requestFactory = MessageFactoryDiscovery::find(); + } + } + return $this->requestFactory; + } + + protected function getStreamFactory(): StreamFactoryInterface + { + return $this->getFactory('streamFactory'); + } + + protected function getUriFactory(): UriFactoryInterface + { + return $this->getFactory('uriFactory'); + } + + public function createRequest(string $method, $uri): RequestInterface + { + return $this->getRequestFactory()->createRequest($method, $uri); + } + + public function createStream(string $content): StreamInterface + { + return $this->getStreamFactory()->createStream($content); + } + + public function createUri(string $uri): UriInterface + { + return $this->getUriFactory()->createUri($method, $uri); } /** + * @deprecated 4.0.0 use createRequest() directly instead * @param $method * @param $uri * @param array $headers @@ -48,30 +141,34 @@ public function request( $body = null, $protocolVersion = '1.1' ) { - $request = $this->requestFactory - ->createRequest($method, $uri) - ->withProtocolVersion($protocolVersion); - - if ($body) { - $request = $request->withBody($body); - } - - foreach ($headers as $name => $value) { - $request = $request->withHeader($name, $value); + if ($this->getRequestFactory() instanceof Psr18ClientInterface) { + $request = $this->requestFactory + ->createRequest($method, $uri) + ->withProtocolVersion($protocolVersion); + if ($body) { + $request = $request->withBody($body); + } + foreach ($headers as $name => $value) { + $request = $request->withHeader($name, $value); + } + } else { + $request = $this->getRequestFactory() + ->createRequest($method, $uri, $headers, $body, $protocolVersion); } return $this->sendRequest($request); } /** + * @deprecated 4.0.0 future versions will not use current exceptions * @param RequestInterface $request * @return ResponseInterface * @throws NetworkException|RequestException */ - private function sendRequest(RequestInterface $request) + public function sendRequest(RequestInterface $request) { try { - return $this->httpClient->sendRequest($request); + return $this->getHttpClient()->sendRequest($request); } catch (\Http\Client\Exception\NetworkException $networkException) { throw new NetworkException($networkException->getMessage(), $request, $networkException); } catch (\Throwable $exception) { diff --git a/tests/Omnipay/Common/Http/ClientTest.php b/tests/Omnipay/Common/Http/ClientTest.php index 4e369f2f..2e5198b6 100644 --- a/tests/Omnipay/Common/Http/ClientTest.php +++ b/tests/Omnipay/Common/Http/ClientTest.php @@ -13,14 +13,6 @@ class ClientTest extends TestCase { - public function testEmptyConstruct() - { - $client = new Client(); - - $this->assertAttributeInstanceOf(ClientInterface::class, 'httpClient', $client); - $this->assertAttributeInstanceOf(RequestFactoryInterface::class, 'requestFactory', $client); - } - public function testSend() { $mockClient = m::mock(ClientInterface::class); @@ -33,6 +25,9 @@ public function testSend() $mockFactory->shouldReceive('createRequest')->withArgs([ 'GET', '/path', + [], + null, + '1.1', ])->andReturn($request); $mockClient->shouldReceive('sendRequest') @@ -56,6 +51,9 @@ public function testSendException() $mockFactory->shouldReceive('createRequest')->withArgs([ 'GET', '/path', + [], + null, + '1.1', ])->andReturn($request); $mockClient->shouldReceive('sendRequest') @@ -80,6 +78,9 @@ public function testSendNetworkException() $mockFactory->shouldReceive('createRequest')->withArgs([ 'GET', '/path', + [], + null, + '1.1', ])->andReturn($request); $mockClient->shouldReceive('sendRequest') @@ -104,6 +105,9 @@ public function testSendExceptionGetRequest() $mockFactory->shouldReceive('createRequest')->withArgs([ 'GET', '/path', + [], + null, + '1.1', ])->andReturn($request); $exception = new \Exception('Something went wrong'); @@ -125,4 +129,4 @@ public function testSendExceptionGetRequest() throw $e; } } -} \ No newline at end of file +}