From 6954b4dd2a7576af5557b1aebbfb36e92c551187 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Sat, 2 Feb 2019 08:30:04 +0100 Subject: [PATCH] Use PSR-18 (#522) * Use PSR-18 * minor fixes * phpstan fixes * cs --- composer.json | 6 +- phpstan.neon | 8 ++- src/Api/HttpApi.php | 16 ++--- src/Api/Suppression.php | 6 +- src/Api/Webhook.php | 4 +- src/Exception/HttpServerException.php | 2 +- src/HttpClient/HttpClientConfigurator.php | 12 ++-- src/HttpClient/Plugin/History.php | 6 +- src/HttpClient/Plugin/HistoryTrait.php | 41 ++++++++++++ src/HttpClient/Plugin/ReplaceUriPlugin.php | 7 +- src/HttpClient/RequestBuilder.php | 76 +++++++++++++--------- src/Mailgun.php | 5 +- tests/Api/TestCase.php | 5 +- tests/HttpClient/RequestBuilderTest.php | 70 ++++++++++++++------ 14 files changed, 175 insertions(+), 89 deletions(-) create mode 100644 src/HttpClient/Plugin/HistoryTrait.php diff --git a/composer.json b/composer.json index 3550492..fdc20dc 100644 --- a/composer.json +++ b/composer.json @@ -3,10 +3,10 @@ "description": "The Mailgun SDK provides methods for all API functions.", "require": { "php": "^7.1", - "php-http/httplug": "^1.0 || ^2.0", + "psr/http-client": "^1.0", "php-http/multipart-stream-builder": "^1.0", - "php-http/client-common": "^1.9", - "php-http/discovery": "^1.0", + "php-http/client-common": "^1.9 || ^2.0", + "php-http/discovery": "^1.6", "webmozart/assert": "^1.2" }, "require-dev": { diff --git a/phpstan.neon b/phpstan.neon index 20b8f0d..6fce17f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,7 @@ parameters: - level: 5 - paths: - - src + level: 5 + paths: + - src + excludes_analyse: + - %currentWorkingDirectory%/src/HttpClient/Plugin/HistoryTrait.php diff --git a/src/Api/HttpApi.php b/src/Api/HttpApi.php index d63a90d..ea42c60 100644 --- a/src/Api/HttpApi.php +++ b/src/Api/HttpApi.php @@ -11,14 +11,14 @@ declare(strict_types=1); namespace Mailgun\Api; -use Http\Client\Exception as HttplugException; -use Http\Client\HttpClient; use Mailgun\Exception\UnknownErrorException; use Mailgun\Hydrator\Hydrator; use Mailgun\Hydrator\NoopHydrator; use Mailgun\Exception\HttpClientException; use Mailgun\Exception\HttpServerException; use Mailgun\HttpClient\RequestBuilder; +use Psr\Http\Client as Psr18; +use Psr\Http\Client\ClientInterface; use Psr\Http\Message\ResponseInterface; /** @@ -29,7 +29,7 @@ abstract class HttpApi /** * The HTTP client. * - * @var HttpClient + * @var ClientInterface */ protected $httpClient; @@ -43,7 +43,7 @@ abstract class HttpApi */ protected $requestBuilder; - public function __construct(HttpClient $httpClient, RequestBuilder $requestBuilder, Hydrator $hydrator) + public function __construct(ClientInterface $httpClient, RequestBuilder $requestBuilder, Hydrator $hydrator) { $this->httpClient = $httpClient; $this->requestBuilder = $requestBuilder; @@ -113,7 +113,7 @@ abstract class HttpApi $response = $this->httpClient->sendRequest( $this->requestBuilder->create('GET', $path, $requestHeaders) ); - } catch (HttplugException\NetworkException $e) { + } catch (Psr18\NetworkExceptionInterface $e) { throw HttpServerException::networkError($e); } @@ -145,7 +145,7 @@ abstract class HttpApi $response = $this->httpClient->sendRequest( $this->requestBuilder->create('POST', $path, $requestHeaders, $body) ); - } catch (HttplugException\NetworkException $e) { + } catch (Psr18\NetworkExceptionInterface $e) { throw HttpServerException::networkError($e); } @@ -165,7 +165,7 @@ abstract class HttpApi $response = $this->httpClient->sendRequest( $this->requestBuilder->create('PUT', $path, $requestHeaders, $this->createRequestBody($parameters)) ); - } catch (HttplugException\NetworkException $e) { + } catch (Psr18\NetworkExceptionInterface $e) { throw HttpServerException::networkError($e); } @@ -185,7 +185,7 @@ abstract class HttpApi $response = $this->httpClient->sendRequest( $this->requestBuilder->create('DELETE', $path, $requestHeaders, $this->createRequestBody($parameters)) ); - } catch (HttplugException\NetworkException $e) { + } catch (Psr18\NetworkExceptionInterface $e) { throw HttpServerException::networkError($e); } diff --git a/src/Api/Suppression.php b/src/Api/Suppression.php index 4bfc0f4..a8ff9c0 100644 --- a/src/Api/Suppression.php +++ b/src/Api/Suppression.php @@ -11,11 +11,11 @@ declare(strict_types=1); namespace Mailgun\Api; -use Http\Client\HttpClient; use Mailgun\Api\Suppression\Bounce; use Mailgun\Api\Suppression\Complaint; use Mailgun\Api\Suppression\Unsubscribe; use Mailgun\Hydrator\Hydrator; +use Psr\Http\Client\ClientInterface; use Mailgun\HttpClient\RequestBuilder; /** @@ -26,7 +26,7 @@ use Mailgun\HttpClient\RequestBuilder; class Suppression { /** - * @var HttpClient + * @var ClientInterface */ private $httpClient; @@ -40,7 +40,7 @@ class Suppression */ private $hydrator; - public function __construct(HttpClient $httpClient, RequestBuilder $requestBuilder, Hydrator $hydrator) + public function __construct(ClientInterface $httpClient, RequestBuilder $requestBuilder, Hydrator $hydrator) { $this->httpClient = $httpClient; $this->requestBuilder = $requestBuilder; diff --git a/src/Api/Webhook.php b/src/Api/Webhook.php index a599fdd..acecfdb 100644 --- a/src/Api/Webhook.php +++ b/src/Api/Webhook.php @@ -11,7 +11,6 @@ declare(strict_types=1); namespace Mailgun\Api; -use Http\Client\HttpClient; use Mailgun\Assert; use Mailgun\Hydrator\Hydrator; use Mailgun\Model\Webhook\CreateResponse; @@ -21,6 +20,7 @@ use Mailgun\Model\Webhook\ShowResponse; use Mailgun\Model\Webhook\UpdateResponse; use Mailgun\HttpClient\RequestBuilder; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Client\ClientInterface; /** * @author Tobias Nyholm @@ -32,7 +32,7 @@ class Webhook extends HttpApi */ private $apiKey; - public function __construct(HttpClient $httpClient, RequestBuilder $requestBuilder, Hydrator $hydrator, string $apiKey) + public function __construct(ClientInterface $httpClient, RequestBuilder $requestBuilder, Hydrator $hydrator, string $apiKey) { parent::__construct($httpClient, $requestBuilder, $hydrator); $this->apiKey = $apiKey; diff --git a/src/Exception/HttpServerException.php b/src/Exception/HttpServerException.php index 214b3a7..b0d7c22 100644 --- a/src/Exception/HttpServerException.php +++ b/src/Exception/HttpServerException.php @@ -23,7 +23,7 @@ final class HttpServerException extends \RuntimeException implements Exception return new self('An unexpected error occurred at Mailgun\'s servers. Try again later and contact support if the error still exists.', $httpStatus); } - public static function networkError(\Exception $previous) + public static function networkError(\Throwable $previous) { return new self('Mailgun\'s servers are currently unreachable.', 0, $previous); } diff --git a/src/HttpClient/HttpClientConfigurator.php b/src/HttpClient/HttpClientConfigurator.php index 60f6f51..4e436c6 100644 --- a/src/HttpClient/HttpClientConfigurator.php +++ b/src/HttpClient/HttpClientConfigurator.php @@ -11,14 +11,14 @@ declare(strict_types=1); namespace Mailgun\HttpClient; -use Http\Client\HttpClient; use Http\Client\Common\PluginClient; -use Http\Discovery\HttpClientDiscovery; +use Http\Discovery\Psr18ClientDiscovery; use Http\Discovery\UriFactoryDiscovery; use Http\Message\UriFactory; use Http\Client\Common\Plugin; use Mailgun\HttpClient\Plugin\History; use Mailgun\HttpClient\Plugin\ReplaceUriPlugin; +use Psr\Http\Client\ClientInterface; /** * Configure a HTTP client. @@ -50,7 +50,7 @@ final class HttpClientConfigurator private $uriFactory; /** - * @var HttpClient + * @var ClientInterface */ private $httpClient; @@ -124,16 +124,16 @@ final class HttpClientConfigurator return $this; } - private function getHttpClient(): HttpClient + private function getHttpClient(): ClientInterface { if (null === $this->httpClient) { - $this->httpClient = HttpClientDiscovery::find(); + $this->httpClient = Psr18ClientDiscovery::find(); } return $this->httpClient; } - public function setHttpClient(HttpClient $httpClient): self + public function setHttpClient(ClientInterface $httpClient): self { $this->httpClient = $httpClient; diff --git a/src/HttpClient/Plugin/History.php b/src/HttpClient/Plugin/History.php index 2934071..4ef27dd 100644 --- a/src/HttpClient/Plugin/History.php +++ b/src/HttpClient/Plugin/History.php @@ -12,7 +12,6 @@ declare(strict_types=1); namespace Mailgun\HttpClient\Plugin; use Http\Client\Common\Plugin\Journal; -use Http\Client\Exception; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -23,6 +22,7 @@ use Psr\Http\Message\ResponseInterface; */ final class History implements Journal { + use HistoryTrait; /** * @var ResponseInterface */ @@ -40,8 +40,4 @@ final class History implements Journal { $this->lastResponse = $response; } - - public function addFailure(RequestInterface $request, Exception $exception) - { - } } diff --git a/src/HttpClient/Plugin/HistoryTrait.php b/src/HttpClient/Plugin/HistoryTrait.php new file mode 100644 index 0000000..cc99f42 --- /dev/null +++ b/src/HttpClient/Plugin/HistoryTrait.php @@ -0,0 +1,41 @@ +uri = $uri; } - /** - * {@inheritdoc} - */ - public function handleRequest(RequestInterface $request, callable $next, callable $first) + public function doHandleRequest(RequestInterface $request, callable $next, callable $first) { $request = $request->withUri($this->uri); diff --git a/src/HttpClient/RequestBuilder.php b/src/HttpClient/RequestBuilder.php index 7885479..ed0c1d4 100644 --- a/src/HttpClient/RequestBuilder.php +++ b/src/HttpClient/RequestBuilder.php @@ -11,10 +11,12 @@ declare(strict_types=1); namespace Mailgun\HttpClient; -use Http\Discovery\MessageFactoryDiscovery; +use Http\Discovery\Psr17FactoryDiscovery; use Http\Message\MultipartStream\MultipartStreamBuilder; -use Http\Message\RequestFactory; +use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamFactoryInterface; +use Psr\Http\Message\StreamInterface; /** * @author Tobias Nyholm @@ -22,10 +24,15 @@ use Psr\Http\Message\RequestInterface; class RequestBuilder { /** - * @var RequestFactory + * @var RequestFactoryInterface|null */ private $requestFactory; + /** + * @var StreamFactoryInterface|null + */ + private $streamFactory; + /** * @var MultipartStreamBuilder */ @@ -42,13 +49,13 @@ class RequestBuilder * 'filename'=> string (optional) * 'headers' => array (optinal) ['header-name' => 'header-value'] * ) - * - * @return RequestInterface */ - public function create(string $method, string $uri, array $headers = [], $body = null) + public function create(string $method, string $uri, array $headers = [], $body = null): RequestInterface { if (!is_array($body)) { - return $this->getRequestFactory()->createRequest($method, $uri, $headers, $body); + $stream = $this->getStreamFactory()->createStream($body); + + return $this->createRequest($method, $uri, $headers, $stream); } $builder = $this->getMultipartStreamBuilder(); @@ -67,37 +74,42 @@ class RequestBuilder $headers['Content-Type'] = 'multipart/form-data; boundary="'.$boundary.'"'; - return $this->getRequestFactory()->createRequest($method, $uri, $headers, $multipartStream); + return $this->createRequest($method, $uri, $headers, $multipartStream); } - /** - * @return RequestFactory - */ - private function getRequestFactory() + private function getRequestFactory(): RequestFactoryInterface { if (null === $this->requestFactory) { - $this->requestFactory = MessageFactoryDiscovery::find(); + $this->requestFactory = Psr17FactoryDiscovery::findRequestFactory(); } return $this->requestFactory; } - /** - * @param RequestFactory $requestFactory - * - * @return RequestBuilder - */ - public function setRequestFactory($requestFactory) + public function setRequestFactory(RequestFactoryInterface $requestFactory): self { $this->requestFactory = $requestFactory; return $this; } - /** - * @return MultipartStreamBuilder - */ - private function getMultipartStreamBuilder() + private function getStreamFactory(): StreamFactoryInterface + { + if (null === $this->streamFactory) { + $this->streamFactory = Psr17FactoryDiscovery::findStreamFactory(); + } + + return $this->streamFactory; + } + + public function setStreamFactory(StreamFactoryInterface $streamFactory): self + { + $this->streamFactory = $streamFactory; + + return $this; + } + + private function getMultipartStreamBuilder(): MultipartStreamBuilder { if (null === $this->multipartStreamBuilder) { $this->multipartStreamBuilder = new MultipartStreamBuilder(); @@ -106,15 +118,21 @@ class RequestBuilder return $this->multipartStreamBuilder; } - /** - * @param MultipartStreamBuilder $multipartStreamBuilder - * - * @return RequestBuilder - */ - public function setMultipartStreamBuilder($multipartStreamBuilder) + public function setMultipartStreamBuilder(MultipartStreamBuilder $multipartStreamBuilder): self { $this->multipartStreamBuilder = $multipartStreamBuilder; return $this; } + + private function createRequest(string $method, string $uri, array $headers, StreamInterface $stream) + { + $request = $this->getRequestFactory()->createRequest($method, $uri); + $request = $request->withBody($stream); + foreach ($headers as $name => $value) { + $request = $request->withAddedHeader($name, $value); + } + + return $request; + } } diff --git a/src/Mailgun.php b/src/Mailgun.php index b8eb656..75a2362 100644 --- a/src/Mailgun.php +++ b/src/Mailgun.php @@ -11,13 +11,14 @@ declare(strict_types=1); namespace Mailgun; -use Http\Client\HttpClient; +use Http\Client\Common\PluginClient; use Mailgun\HttpClient\HttpClientConfigurator; use Mailgun\HttpClient\Plugin\History; use Mailgun\HttpClient\RequestBuilder; use Mailgun\Hydrator\ModelHydrator; use Mailgun\Hydrator\Hydrator; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Client\ClientInterface; /** * This class is the base class for the Mailgun SDK. @@ -30,7 +31,7 @@ final class Mailgun private $apiKey; /** - * @var HttpClient + * @var ClientInterface|PluginClient */ private $httpClient; diff --git a/tests/Api/TestCase.php b/tests/Api/TestCase.php index 1fdea38..0122317 100644 --- a/tests/Api/TestCase.php +++ b/tests/Api/TestCase.php @@ -14,6 +14,7 @@ namespace Mailgun\Tests\Api; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use Mailgun\Hydrator\ModelHydrator; +use Psr\Http\Client\ClientInterface; use Psr\Http\Message\ResponseInterface; /** @@ -49,7 +50,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase protected function getApiMock($httpClient = null, $requestClient = null, $hydrator = null) { if (null === $httpClient) { - $httpClient = $this->getMockBuilder('Http\Client\HttpClient') + $httpClient = $this->getMockBuilder(ClientInterface::class) ->setMethods(['sendRequest']) ->getMock(); $httpClient @@ -79,7 +80,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase */ protected function getApiInstance($apiKey = null) { - $httpClient = $this->getMockBuilder('Http\Client\HttpClient') + $httpClient = $this->getMockBuilder(ClientInterface::class) ->setMethods(['sendRequest']) ->getMock(); $httpClient diff --git a/tests/HttpClient/RequestBuilderTest.php b/tests/HttpClient/RequestBuilderTest.php index bafe9bc..4e17164 100644 --- a/tests/HttpClient/RequestBuilderTest.php +++ b/tests/HttpClient/RequestBuilderTest.php @@ -12,19 +12,25 @@ declare(strict_types=1); namespace Mailgun\Tests\HttpClient; use Http\Message\MultipartStream\MultipartStreamBuilder; -use Http\Message\RequestFactory; use Mailgun\HttpClient\RequestBuilder; use Mailgun\Tests\MailgunTestCase; +use Nyholm\Psr7\Stream; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\StreamInterface; class RequestBuilderTest extends MailgunTestCase { /** - * @var MockObject|RequestFactory + * @var MockObject|RequestFactoryInterface */ private $requestFactory; + /** + * @var MockObject|StreamFactoryInterface + */ + private $streamFactory; /** * @var RequestBuilder @@ -38,13 +44,18 @@ class RequestBuilderTest extends MailgunTestCase { parent::setUp(); - $this->requestFactory = $this->getMockBuilder(RequestFactory::class) + $this->requestFactory = $this->getMockBuilder(RequestFactoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->streamFactory = $this->getMockBuilder(StreamFactoryInterface::class) ->disableOriginalConstructor() ->getMock(); $this->requestBuilder = new RequestBuilder(); //Everything but testing class is mock. Otherwise it wouldn't be unit testing $this->requestBuilder->setRequestFactory($this->requestFactory); + $this->requestBuilder->setStreamFactory($this->streamFactory); } /** @@ -60,21 +71,34 @@ class RequestBuilderTest extends MailgunTestCase public function testCreateSimpleStream() { + $streamContent = 'content'; + $stream = Stream::create($streamContent); + + $this->streamFactory + ->expects($this->once()) + ->method('createStream') + ->with($streamContent) + ->willReturn($stream); + + $request = $this->getMockBuilder(RequestInterface::class)->getMock(); + $request->expects($this->once()) + ->method('withBody') + ->with($this->equalTo($stream)) + ->willReturn($request); + + $request->expects($this->once()) + ->method('withAddedHeader') + ->with($this->equalTo('Content-Type'), $this->equalTo('application/json')) + ->willReturn($request); + $this->requestFactory ->expects($this->once()) ->method('createRequest') ->with( $this->equalTo('GET'), - $this->equalTo('http://foo.bar'), - $this->callback(function (array $headers) { - $this->assertArrayHasKey('Content-Type', $headers); - $this->assertEquals('application/json', $headers['Content-Type']); - - return true; - }), - $this->equalTo('content') + $this->equalTo('http://foo.bar') ) - ->willReturn($request = $this->getMockBuilder(RequestInterface::class)->getMock()); + ->willReturn($request); $result = $this->requestBuilder ->create('GET', 'http://foo.bar', ['Content-Type' => 'application/json'], 'content'); @@ -122,21 +146,25 @@ class RequestBuilderTest extends MailgunTestCase ->method('reset') ->willReturn($multipartStreamBuilder); + $request = $this->getMockBuilder(RequestInterface::class)->getMock(); + $request->expects($this->once()) + ->method('withBody') + ->with($this->equalTo($stream)) + ->willReturn($request); + + $request->expects($this->once()) + ->method('withAddedHeader') + ->with($this->equalTo('Content-Type'), $this->equalTo('multipart/form-data; boundary="some boundary"')) + ->willReturn($request); + $this->requestFactory ->expects($this->once()) ->method('createRequest') ->with( $this->equalTo('GET'), - $this->equalTo('http://foo.bar'), - $this->callback(function (array $headers) { - $this->assertArrayHasKey('Content-Type', $headers); - $this->assertEquals('multipart/form-data; boundary="some boundary"', $headers['Content-Type']); - - return true; - }), - $this->equalTo($stream) + $this->equalTo('http://foo.bar') ) - ->willReturn($request = $this->getMockBuilder(RequestInterface::class)->getMock()); + ->willReturn($request); $this->requestBuilder->setMultipartStreamBuilder($multipartStreamBuilder); $result = $this->requestBuilder