diff --git a/CHANGELOG.md b/CHANGELOG.md index c6a01d9..183b001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Changed - Abstract method `HttpClientPool::chooseHttpClient()` has now an explicit return type (`Http\Client\Common\HttpClientPoolItem`) - Interface method `Plugin::handleRequest(...)` has now an explicit return type (`Http\Promise\Promise`) +- Made all classes final as they are not intended to be extended. ### Removed - Deprecated option `debug_plugins` has been removed from `PluginClient` diff --git a/spec/HttpClientPoolItemSpec.php b/spec/HttpClientPool/HttpClientPoolItemImplSpec.php similarity index 98% rename from spec/HttpClientPoolItemSpec.php rename to spec/HttpClientPool/HttpClientPoolItemImplSpec.php index f44351b..b853e7f 100644 --- a/spec/HttpClientPoolItemSpec.php +++ b/spec/HttpClientPool/HttpClientPoolItemImplSpec.php @@ -1,6 +1,6 @@ addHttpClient(new HttpClientPoolItem($client->getWrappedObject(), 0)); + $this->addHttpClient(new HttpClientPoolItemImpl($client->getWrappedObject(), 0)); $client->sendRequest($request)->willThrow(HttpException::class); $this->shouldThrow(HttpException::class)->duringSendRequest($request); diff --git a/spec/HttpClientPool/RandomClientPoolSpec.php b/spec/HttpClientPool/RandomClientPoolSpec.php index fb0e5ad..a7e6c62 100644 --- a/spec/HttpClientPool/RandomClientPoolSpec.php +++ b/spec/HttpClientPool/RandomClientPoolSpec.php @@ -2,7 +2,7 @@ namespace spec\Http\Client\Common\HttpClientPool; -use Http\Client\Common\HttpClientPoolItem; +use Http\Client\Common\HttpClientPool\HttpClientPoolItemImpl; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use Http\Promise\Promise; @@ -65,7 +65,7 @@ public function it_throw_exception_if_no_more_enable_client(HttpClient $client, public function it_reenable_client(HttpClient $client, RequestInterface $request) { - $this->addHttpClient(new HttpClientPoolItem($client->getWrappedObject(), 0)); + $this->addHttpClient(new HttpClientPoolItemImpl($client->getWrappedObject(), 0)); $client->sendRequest($request)->willThrow(HttpException::class); $this->shouldThrow(HttpException::class)->duringSendRequest($request); diff --git a/spec/HttpClientPool/RoundRobinClientPoolSpec.php b/spec/HttpClientPool/RoundRobinClientPoolSpec.php index 925f548..e64239b 100644 --- a/spec/HttpClientPool/RoundRobinClientPoolSpec.php +++ b/spec/HttpClientPool/RoundRobinClientPoolSpec.php @@ -2,7 +2,7 @@ namespace spec\Http\Client\Common\HttpClientPool; -use Http\Client\Common\HttpClientPoolItem; +use Http\Client\Common\HttpClientPool\HttpClientPoolItemImpl; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use Http\Promise\Promise; @@ -65,7 +65,7 @@ public function it_throw_exception_if_no_more_enable_client(HttpClient $client, public function it_reenable_client(HttpClient $client, RequestInterface $request) { - $this->addHttpClient(new HttpClientPoolItem($client->getWrappedObject(), 0)); + $this->addHttpClient(new HttpClientPoolItemImpl($client->getWrappedObject(), 0)); $client->sendRequest($request)->willThrow(HttpException::class); $this->shouldThrow(HttpException::class)->duringSendRequest($request); diff --git a/spec/HttpMethodsClientSpec.php b/spec/HttpMethodsClientSpec.php index 74e0810..30df11c 100644 --- a/spec/HttpMethodsClientSpec.php +++ b/spec/HttpMethodsClientSpec.php @@ -5,132 +5,88 @@ use GuzzleHttp\Psr7\Response; use Http\Client\Common\HttpMethodsClient; use Http\Client\HttpClient; -use Http\Message\MessageFactory; +use Http\Message\RequestFactory; use PhpSpec\ObjectBehavior; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; class HttpMethodsClientSpec extends ObjectBehavior { - public function let(HttpClient $client, MessageFactory $messageFactory) + private static $requestData = [ + 'uri' => '/uri', + 'headers' => [ + 'Content-Type' => 'text/plain', + ], + 'body' => 'body', + ]; + + public function let(HttpClient $client, RequestFactory $requestFactory) { $this->beAnInstanceOf( - HttpMethodsClientStub::class, [ + HttpMethodsClient::class, [ $client, - $messageFactory, + $requestFactory, ] ); } - public function it_sends_a_get_request() + public function it_sends_a_get_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response) { - $data = HttpMethodsClientStub::$requestData; - - $this->get($data['uri'], $data['headers'])->shouldReturnAnInstanceOf(ResponseInterface::class); + $this->assert($client, $requestFactory, $request, $response, 'get'); } - public function it_sends_a_head_request() - { - $data = HttpMethodsClientStub::$requestData; - $this->head($data['uri'], $data['headers'])->shouldReturnAnInstanceOf(ResponseInterface::class); + public function it_sends_a_head_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response) + { + $this->assert($client, $requestFactory, $request, $response, 'head'); } - public function it_sends_a_trace_request() + public function it_sends_a_trace_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response) { - $data = HttpMethodsClientStub::$requestData; - - $this->trace($data['uri'], $data['headers'])->shouldReturnAnInstanceOf(ResponseInterface::class); + $this->assert($client, $requestFactory, $request, $response, 'trace'); } - public function it_sends_a_post_request() + public function it_sends_a_post_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response) { - $data = HttpMethodsClientStub::$requestData; - - $this->post($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class); + $this->assert($client, $requestFactory, $request, $response, 'post', self::$requestData['body']); } - public function it_sends_a_put_request() + public function it_sends_a_put_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response) { - $data = HttpMethodsClientStub::$requestData; - - $this->put($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class); + $this->assert($client, $requestFactory, $request, $response, 'put', self::$requestData['body']); } - public function it_sends_a_patch_request() + public function it_sends_a_patch_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response) { - $data = HttpMethodsClientStub::$requestData; - - $this->patch($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class); + $this->assert($client, $requestFactory, $request, $response, 'patch', self::$requestData['body']); } - public function it_sends_a_delete_request() + public function it_sends_a_delete_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response) { - $data = HttpMethodsClientStub::$requestData; - - $this->delete($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class); + $this->assert($client, $requestFactory, $request, $response, 'delete', self::$requestData['body']); } - public function it_sends_a_options_request() + public function it_sends_an_options_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response) { - $data = HttpMethodsClientStub::$requestData; - - $this->options($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class); + $this->assert($client, $requestFactory, $request, $response, 'options', self::$requestData['body']); } - public function it_sends_request_with_underlying_client(HttpClient $client, MessageFactory $messageFactory, RequestInterface $request, ResponseInterface $response) + /** + * Run the actual test. + * + * As there is no data provider in phpspec, we keep separate methods to get new mocks for each test. + */ + private function assert(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response, string $method, string $body = null) { $client->sendRequest($request)->shouldBeCalled()->willReturn($response); + $this->mockFactory($requestFactory, $request, strtoupper($method), $body); - $this->beConstructedWith($client, $messageFactory); - $this->sendRequest($request)->shouldReturn($response); - } -} + $this->$method(self::$requestData['uri'], self::$requestData['headers'], self::$requestData['body'])->shouldReturnAnInstanceOf(ResponseInterface::class); -class HttpMethodsClientStub extends HttpMethodsClient -{ - public static $requestData = [ - 'uri' => '/uri', - 'headers' => [ - 'Content-Type' => 'text/plain', - ], - 'body' => 'body', - ]; + } - /** - * {@inheritdoc} - */ - public function send($method, $uri, array $headers = [], $body = null): ResponseInterface + private function mockFactory(RequestFactory $requestFactory, RequestInterface $request, string $method, string $body = null) { - if ($uri !== self::$requestData['uri']) { - throw new \InvalidArgumentException('Invalid URI: '.$uri); - } - - if ($headers !== self::$requestData['headers']) { - throw new \InvalidArgumentException('Invalid headers: '.print_r($headers, true)); - } - - switch ($method) { - case 'GET': - case 'HEAD': - case 'TRACE': - if (null !== $body) { - throw new \InvalidArgumentException('Non-empty body'); - } - - return new Response(); - case 'POST': - case 'PUT': - case 'PATCH': - case 'DELETE': - case 'OPTIONS': - if ($body !== self::$requestData['body']) { - throw new \InvalidArgumentException('Invalid body: '.print_r($body, true)); - } - - return new Response(); - default: - throw new \InvalidArgumentException('Invalid method: '.$method); - } + $requestFactory->createRequest($method, self::$requestData['uri'], self::$requestData['headers'], $body)->willReturn($request); } } diff --git a/src/BatchClient.php b/src/BatchClient.php index 8dfeb81..38532ed 100644 --- a/src/BatchClient.php +++ b/src/BatchClient.php @@ -15,7 +15,7 @@ * * @author Joel Wurtz */ -class BatchClient implements HttpClient +final class BatchClient implements HttpClient { /** * @var HttpClient diff --git a/src/Deferred.php b/src/Deferred.php index 7413451..c294a8d 100644 --- a/src/Deferred.php +++ b/src/Deferred.php @@ -9,7 +9,7 @@ /** * A deferred allow to return a promise which has not been resolved yet. */ -class Deferred implements Promise +final class Deferred implements Promise { private $value; diff --git a/src/HttpClientPool.php b/src/HttpClientPool.php index 90a8464..4307ec7 100644 --- a/src/HttpClientPool.php +++ b/src/HttpClientPool.php @@ -3,6 +3,8 @@ namespace Http\Client\Common; use Http\Client\Common\Exception\HttpClientNotFoundException; +use Http\Client\Common\HttpClientPool\HttpClientPoolItem; +use Http\Client\Common\HttpClientPool\HttpClientPoolItemImpl; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use Psr\Http\Message\RequestInterface; @@ -27,7 +29,7 @@ abstract class HttpClientPool implements HttpAsyncClient, HttpClient public function addHttpClient($client) { if (!$client instanceof HttpClientPoolItem) { - $client = new HttpClientPoolItem($client); + $client = new HttpClientPoolItemImpl($client); } $this->clientPool[] = $client; diff --git a/src/HttpClientPool/HttpClientPoolItem.php b/src/HttpClientPool/HttpClientPoolItem.php new file mode 100644 index 0000000..ef1b7c1 --- /dev/null +++ b/src/HttpClientPool/HttpClientPoolItem.php @@ -0,0 +1,38 @@ + + */ +interface HttpClientPoolItem extends HttpClient, HttpAsyncClient +{ + /** + * Whether this client is disabled or not. + * + * Will also reactivate this client if possible + * + * @internal + * + * @return bool + */ + public function isDisabled(); + + /** + * Get current number of request that are currently being sent by the underlying HTTP client. + * + * @internal + * + * @return int + */ + public function getSendingRequestCount(); +} diff --git a/src/HttpClientPoolItem.php b/src/HttpClientPool/HttpClientPoolItemImpl.php similarity index 71% rename from src/HttpClientPoolItem.php rename to src/HttpClientPool/HttpClientPoolItemImpl.php index a46ee2f..31b9bbe 100644 --- a/src/HttpClientPoolItem.php +++ b/src/HttpClientPool/HttpClientPoolItemImpl.php @@ -1,7 +1,8 @@ */ -class HttpClientPoolItem implements HttpClient, HttpAsyncClient +final class HttpClientPoolItemImpl implements HttpClientPoolItem { /** * @var int Number of request this client is currently sending @@ -29,7 +27,11 @@ class HttpClientPoolItem implements HttpClient, HttpAsyncClient private $disabledAt; /** - * @var int|null Number of seconds after this client is reenable, by default null: never reenable this client + * Number of seconds until this client is enabled again after an error. + * + * null: never reenable this client. + * + * @var int|null */ private $reenableAfter; @@ -40,7 +42,7 @@ class HttpClientPoolItem implements HttpClient, HttpAsyncClient /** * @param HttpClient|HttpAsyncClient $client - * @param null|int $reenableAfter Number of seconds after this client is reenable + * @param null|int $reenableAfter Number of seconds until this client is enabled again after an error */ public function __construct($client, $reenableAfter = null) { @@ -95,23 +97,15 @@ public function sendAsyncRequest(RequestInterface $request) } /** - * Whether this client is disabled or not. - * - * Will also reactivate this client if possible - * - * @internal - * - * @return bool + * {@inheritdoc} */ public function isDisabled() { - $disabledAt = $this->getDisabledAt(); - - if (null !== $this->reenableAfter && null !== $disabledAt) { + if (null !== $this->reenableAfter && null !== $this->disabledAt) { // Reenable after a certain time $now = new \DateTime(); - if (($now->getTimestamp() - $disabledAt->getTimestamp()) >= $this->reenableAfter) { + if (($now->getTimestamp() - $this->disabledAt->getTimestamp()) >= $this->reenableAfter) { $this->enable(); return false; @@ -120,31 +114,17 @@ public function isDisabled() return true; } - return null !== $disabledAt; + return null !== $this->disabledAt; } /** - * Get current number of request that is send by the underlying http client. - * - * @internal - * - * @return int + * {@inheritdoc} */ public function getSendingRequestCount() { return $this->sendingRequestCount; } - /** - * Return when this client has been disabled or null if it's enabled. - * - * @return \DateTime|null - */ - private function getDisabledAt() - { - return $this->disabledAt; - } - /** * Increment the request count. */ diff --git a/src/HttpClientPool/LeastUsedClientPool.php b/src/HttpClientPool/LeastUsedClientPool.php index 61eb670..e737f5a 100644 --- a/src/HttpClientPool/LeastUsedClientPool.php +++ b/src/HttpClientPool/LeastUsedClientPool.php @@ -4,7 +4,6 @@ use Http\Client\Common\Exception\HttpClientNotFoundException; use Http\Client\Common\HttpClientPool; -use Http\Client\Common\HttpClientPoolItem; /** * LeastUsedClientPool will choose the client with the less current request in the pool. diff --git a/src/HttpClientPool/RandomClientPool.php b/src/HttpClientPool/RandomClientPool.php index 2889ed7..1aaf739 100644 --- a/src/HttpClientPool/RandomClientPool.php +++ b/src/HttpClientPool/RandomClientPool.php @@ -4,7 +4,6 @@ use Http\Client\Common\Exception\HttpClientNotFoundException; use Http\Client\Common\HttpClientPool; -use Http\Client\Common\HttpClientPoolItem; /** * RoundRobinClientPool will choose the next client in the pool. @@ -18,7 +17,7 @@ final class RandomClientPool extends HttpClientPool */ protected function chooseHttpClient(): HttpClientPoolItem { - $clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) { + $clientPool = array_filter($this->clientPool, function (HttpClientPoolItemImpl $clientPoolItem) { return !$clientPoolItem->isDisabled(); }); diff --git a/src/HttpClientPool/RoundRobinClientPool.php b/src/HttpClientPool/RoundRobinClientPool.php index 3315b51..fe33c37 100644 --- a/src/HttpClientPool/RoundRobinClientPool.php +++ b/src/HttpClientPool/RoundRobinClientPool.php @@ -4,7 +4,6 @@ use Http\Client\Common\Exception\HttpClientNotFoundException; use Http\Client\Common\HttpClientPool; -use Http\Client\Common\HttpClientPoolItem; /** * RoundRobinClientPool will choose the next client in the pool. diff --git a/src/HttpClientRouter.php b/src/HttpClientRouter.php index 00ca15f..d4e00df 100644 --- a/src/HttpClientRouter.php +++ b/src/HttpClientRouter.php @@ -12,6 +12,8 @@ /** * Route a request to a specific client in the stack based using a RequestMatcher. * + * This is not a HttpClientPool client because it uses a matcher to select the client. + * * @author Joel Wurtz */ final class HttpClientRouter implements HttpClient, HttpAsyncClient @@ -26,9 +28,7 @@ final class HttpClientRouter implements HttpClient, HttpAsyncClient */ public function sendRequest(RequestInterface $request): ResponseInterface { - $client = $this->chooseHttpClient($request); - - return $client->sendRequest($request); + return $this->chooseHttpClient($request)->sendRequest($request); } /** @@ -36,9 +36,7 @@ public function sendRequest(RequestInterface $request): ResponseInterface */ public function sendAsyncRequest(RequestInterface $request) { - $client = $this->chooseHttpClient($request); - - return $client->sendAsyncRequest($request); + return $this->chooseHttpClient($request)->sendAsyncRequest($request); } /** diff --git a/src/HttpMethodsClient.php b/src/HttpMethodsClient.php index bc02ef8..082575a 100644 --- a/src/HttpMethodsClient.php +++ b/src/HttpMethodsClient.php @@ -24,7 +24,7 @@ * @author Márk Sági-Kazár * @author David Buchmann */ -class HttpMethodsClient implements HttpClient +final class HttpMethodsClient implements HttpClient { /** * @var HttpClient