Skip to content

Commit fadb51a

Browse files
committed
mark classes as final and methods and properties as private
We add interfaces for final classes so that they can be mocked in testing. We chose to use the Interface suffix here to avoid name clashes, rather than renaming the existing PHP classes. The interfaces are only relevant for unit testing, we do not expect people to provide their own implementations. HttpClientPoolItem is purely internal and we therefore don't want an interface for it. It is marked with @Final and explained to not be meant to extend.
1 parent 5541efd commit fadb51a

23 files changed

+326
-306
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
- RetryPlugin will no longer retry requests when the response failed with a HTTP code < 500.
77
- Abstract method `HttpClientPool::chooseHttpClient()` has now an explicit return type (`Http\Client\Common\HttpClientPoolItem`)
88
- Interface method `Plugin::handleRequest(...)` has now an explicit return type (`Http\Promise\Promise`)
9+
- Made classes final that are not intended to be extended.
10+
Added interfaces for BatchClient, HttpClientRouter and HttpMethodsClient.
11+
(These interfaces use the `Interface` suffix to avoid name collisions.)
12+
- Added an interface for HttpClientPool and moved the abstract class to the HttpClientPool sub namespace.
913

1014
### Removed
1115
- Deprecated option `debug_plugins` has been removed from `PluginClient`

spec/HttpClientPoolItemSpec.php renamed to spec/HttpClientPool/HttpClientPoolItemSpec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace spec\Http\Client\Common;
3+
namespace spec\Http\Client\Common\HttpClientPool;
44

55
use Http\Client\Exception;
66
use Http\Client\Exception\TransferException;

spec/HttpClientPool/LeastUsedClientPoolSpec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace spec\Http\Client\Common\HttpClientPool;
44

5-
use Http\Client\Common\HttpClientPoolItem;
5+
use Http\Client\Common\HttpClientPool\HttpClientPoolItem;
66
use Http\Client\HttpAsyncClient;
77
use Http\Client\HttpClient;
88
use Http\Promise\Promise;

spec/HttpClientPool/RandomClientPoolSpec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace spec\Http\Client\Common\HttpClientPool;
44

5-
use Http\Client\Common\HttpClientPoolItem;
5+
use Http\Client\Common\HttpClientPool\HttpClientPoolItem;
66
use Http\Client\HttpAsyncClient;
77
use Http\Client\HttpClient;
88
use Http\Promise\Promise;

spec/HttpClientPool/RoundRobinClientPoolSpec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace spec\Http\Client\Common\HttpClientPool;
44

5-
use Http\Client\Common\HttpClientPoolItem;
5+
use Http\Client\Common\HttpClientPool\HttpClientPoolItem;
66
use Http\Client\HttpAsyncClient;
77
use Http\Client\HttpClient;
88
use Http\Promise\Promise;

spec/HttpClientRouterSpec.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
namespace spec\Http\Client\Common;
44

5+
use Http\Client\Common\HttpClientRouter;
56
use Http\Message\RequestMatcher;
67
use Http\Client\HttpAsyncClient;
78
use Http\Client\HttpClient;
89
use Http\Promise\Promise;
910
use Psr\Http\Message\RequestInterface;
1011
use Psr\Http\Message\ResponseInterface;
1112
use PhpSpec\ObjectBehavior;
12-
use Http\Client\Common\HttpClientRouter;
13+
use Http\Client\Common\HttpClientRouterInterface;
1314
use Http\Client\Exception\RequestException;
1415

1516
class HttpClientRouterSpec extends ObjectBehavior
@@ -19,6 +20,11 @@ public function it_is_initializable()
1920
$this->shouldHaveType(HttpClientRouter::class);
2021
}
2122

23+
public function it_is_an_http_client_router()
24+
{
25+
$this->shouldImplement(HttpClientRouterInterface::class);
26+
}
27+
2228
public function it_is_an_http_client()
2329
{
2430
$this->shouldImplement(HttpClient::class);

spec/HttpMethodsClientSpec.php

Lines changed: 38 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2,135 +2,88 @@
22

33
namespace spec\Http\Client\Common;
44

5-
use GuzzleHttp\Psr7\Response;
65
use Http\Client\Common\HttpMethodsClient;
76
use Http\Client\HttpClient;
8-
use Http\Message\MessageFactory;
7+
use Http\Message\RequestFactory;
98
use PhpSpec\ObjectBehavior;
109
use Psr\Http\Message\RequestInterface;
1110
use Psr\Http\Message\ResponseInterface;
1211

1312
class HttpMethodsClientSpec extends ObjectBehavior
1413
{
15-
public function let(HttpClient $client, MessageFactory $messageFactory)
14+
private static $requestData = [
15+
'uri' => '/uri',
16+
'headers' => [
17+
'Content-Type' => 'text/plain',
18+
],
19+
'body' => 'body',
20+
];
21+
22+
public function let(HttpClient $client, RequestFactory $requestFactory)
1623
{
1724
$this->beAnInstanceOf(
18-
HttpMethodsClientStub::class, [
25+
HttpMethodsClient::class, [
1926
$client,
20-
$messageFactory,
27+
$requestFactory,
2128
]
2229
);
2330
}
2431

25-
public function it_sends_a_get_request()
32+
public function it_sends_a_get_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response)
2633
{
27-
$data = HttpMethodsClientStub::$requestData;
28-
29-
$this->get($data['uri'], $data['headers'])->shouldReturnAnInstanceOf(ResponseInterface::class);
34+
$this->assert($client, $requestFactory, $request, $response, 'get');
3035
}
3136

32-
public function it_sends_a_head_request()
37+
public function it_sends_a_head_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response)
3338
{
34-
$data = HttpMethodsClientStub::$requestData;
35-
36-
$this->head($data['uri'], $data['headers'])->shouldReturnAnInstanceOf(ResponseInterface::class);
39+
$this->assert($client, $requestFactory, $request, $response, 'head');
3740
}
3841

39-
public function it_sends_a_trace_request()
42+
public function it_sends_a_trace_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response)
4043
{
41-
$data = HttpMethodsClientStub::$requestData;
42-
43-
$this->trace($data['uri'], $data['headers'])->shouldReturnAnInstanceOf(ResponseInterface::class);
44+
$this->assert($client, $requestFactory, $request, $response, 'trace');
4445
}
4546

46-
public function it_sends_a_post_request()
47+
public function it_sends_a_post_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response)
4748
{
48-
$data = HttpMethodsClientStub::$requestData;
49-
50-
$this->post($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class);
49+
$this->assert($client, $requestFactory, $request, $response, 'post', self::$requestData['body']);
5150
}
5251

53-
public function it_sends_a_put_request()
52+
public function it_sends_a_put_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response)
5453
{
55-
$data = HttpMethodsClientStub::$requestData;
56-
57-
$this->put($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class);
54+
$this->assert($client, $requestFactory, $request, $response, 'put', self::$requestData['body']);
5855
}
5956

60-
public function it_sends_a_patch_request()
57+
public function it_sends_a_patch_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response)
6158
{
62-
$data = HttpMethodsClientStub::$requestData;
63-
64-
$this->patch($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class);
59+
$this->assert($client, $requestFactory, $request, $response, 'patch', self::$requestData['body']);
6560
}
6661

67-
public function it_sends_a_delete_request()
62+
public function it_sends_a_delete_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response)
6863
{
69-
$data = HttpMethodsClientStub::$requestData;
70-
71-
$this->delete($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class);
64+
$this->assert($client, $requestFactory, $request, $response, 'delete', self::$requestData['body']);
7265
}
7366

74-
public function it_sends_a_options_request()
67+
public function it_sends_an_options_request(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response)
7568
{
76-
$data = HttpMethodsClientStub::$requestData;
77-
78-
$this->options($data['uri'], $data['headers'], $data['body'])->shouldReturnAnInstanceOf(ResponseInterface::class);
69+
$this->assert($client, $requestFactory, $request, $response, 'options', self::$requestData['body']);
7970
}
8071

81-
public function it_sends_request_with_underlying_client(HttpClient $client, MessageFactory $messageFactory, RequestInterface $request, ResponseInterface $response)
72+
/**
73+
* Run the actual test.
74+
*
75+
* As there is no data provider in phpspec, we keep separate methods to get new mocks for each test.
76+
*/
77+
private function assert(HttpClient $client, RequestFactory $requestFactory, RequestInterface $request, ResponseInterface $response, string $method, string $body = null)
8278
{
8379
$client->sendRequest($request)->shouldBeCalled()->willReturn($response);
80+
$this->mockFactory($requestFactory, $request, strtoupper($method), $body);
8481

85-
$this->beConstructedWith($client, $messageFactory);
86-
$this->sendRequest($request)->shouldReturn($response);
82+
$this->$method(self::$requestData['uri'], self::$requestData['headers'], self::$requestData['body'])->shouldReturnAnInstanceOf(ResponseInterface::class);
8783
}
88-
}
89-
90-
class HttpMethodsClientStub extends HttpMethodsClient
91-
{
92-
public static $requestData = [
93-
'uri' => '/uri',
94-
'headers' => [
95-
'Content-Type' => 'text/plain',
96-
],
97-
'body' => 'body',
98-
];
9984

100-
/**
101-
* {@inheritdoc}
102-
*/
103-
public function send($method, $uri, array $headers = [], $body = null): ResponseInterface
85+
private function mockFactory(RequestFactory $requestFactory, RequestInterface $request, string $method, string $body = null)
10486
{
105-
if ($uri !== self::$requestData['uri']) {
106-
throw new \InvalidArgumentException('Invalid URI: '.$uri);
107-
}
108-
109-
if ($headers !== self::$requestData['headers']) {
110-
throw new \InvalidArgumentException('Invalid headers: '.print_r($headers, true));
111-
}
112-
113-
switch ($method) {
114-
case 'GET':
115-
case 'HEAD':
116-
case 'TRACE':
117-
if (null !== $body) {
118-
throw new \InvalidArgumentException('Non-empty body');
119-
}
120-
121-
return new Response();
122-
case 'POST':
123-
case 'PUT':
124-
case 'PATCH':
125-
case 'DELETE':
126-
case 'OPTIONS':
127-
if ($body !== self::$requestData['body']) {
128-
throw new \InvalidArgumentException('Invalid body: '.print_r($body, true));
129-
}
130-
131-
return new Response();
132-
default:
133-
throw new \InvalidArgumentException('Invalid method: '.$method);
134-
}
87+
$requestFactory->createRequest($method, self::$requestData['uri'], self::$requestData['headers'], $body)->willReturn($request);
13588
}
13689
}

spec/Plugin/AddPathPluginSpec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function it_adds_path(
4646
$this->handleRequest($request, PluginStub::next(), function () {});
4747
}
4848

49-
function it_removes_ending_slashes(
49+
public function it_removes_ending_slashes(
5050
RequestInterface $request,
5151
UriInterface $host,
5252
UriInterface $host2,

src/BatchClient.php

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,7 @@
88
use Psr\Http\Message\RequestInterface;
99
use Psr\Http\Message\ResponseInterface;
1010

11-
/**
12-
* BatchClient allow to sends multiple request and retrieve a Batch Result.
13-
*
14-
* This implementation simply loops over the requests and uses sendRequest with each of them.
15-
*
16-
* @author Joel Wurtz <[email protected]>
17-
*/
18-
class BatchClient implements HttpClient
11+
final class BatchClient implements BatchClientInterface
1912
{
2013
/**
2114
* @var HttpClient
@@ -27,28 +20,11 @@ public function __construct(HttpClient $client)
2720
$this->client = $client;
2821
}
2922

30-
/**
31-
* {@inheritdoc}
32-
*/
3323
public function sendRequest(RequestInterface $request): ResponseInterface
3424
{
3525
return $this->client->sendRequest($request);
3626
}
3727

38-
/**
39-
* Send several requests.
40-
*
41-
* You may not assume that the requests are executed in a particular order. If the order matters
42-
* for your application, use sendRequest sequentially.
43-
*
44-
* @param RequestInterface[] The requests to send
45-
*
46-
* @return BatchResult Containing one result per request
47-
*
48-
* @throws BatchException If one or more requests fails. The exception gives access to the
49-
* BatchResult with a map of request to result for success, request to
50-
* exception for failures
51-
*/
5228
public function sendRequests(array $requests): BatchResult
5329
{
5430
$batchResult = new BatchResult();

src/BatchClientInterface.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Http\Client\Common;
4+
5+
use Http\Client\Exception;
6+
use Http\Client\HttpClient;
7+
use Http\Client\Common\Exception\BatchException;
8+
use Psr\Http\Message\RequestInterface;
9+
10+
/**
11+
* BatchClient allow to sends multiple request and retrieve a Batch Result.
12+
*
13+
* This implementation simply loops over the requests and uses sendRequest with each of them.
14+
*
15+
* @author Joel Wurtz <[email protected]>
16+
*/
17+
interface BatchClientInterface extends HttpClient
18+
{
19+
/**
20+
* Send several requests.
21+
*
22+
* You may not assume that the requests are executed in a particular order. If the order matters
23+
* for your application, use sendRequest sequentially.
24+
*
25+
* @param RequestInterface[] The requests to send
26+
*
27+
* @return BatchResult Containing one result per request
28+
*
29+
* @throws BatchException If one or more requests fails. The exception gives access to the
30+
* BatchResult with a map of request to result for success, request to
31+
* exception for failures
32+
*/
33+
public function sendRequests(array $requests): BatchResult;
34+
}

src/Deferred.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
/**
1010
* A deferred allow to return a promise which has not been resolved yet.
1111
*/
12-
class Deferred implements Promise
12+
final class Deferred implements Promise
1313
{
1414
private $value;
1515

src/EmulatedHttpAsyncClient.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@
66
use Http\Client\HttpClient;
77

88
/**
9-
* Emulates an async HTTP client.
10-
*
11-
* This should be replaced by an anonymous class in PHP 7.
9+
* Emulates an async HTTP client with the help of a synchronous client.
1210
*
1311
* @author Márk Sági-Kazár <[email protected]>
1412
*/
15-
class EmulatedHttpAsyncClient implements HttpClient, HttpAsyncClient
13+
final class EmulatedHttpAsyncClient implements HttpClient, HttpAsyncClient
1614
{
1715
use HttpAsyncClientEmulator;
1816
use HttpClientDecorator;

src/EmulatedHttpClient.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@
66
use Http\Client\HttpClient;
77

88
/**
9-
* Emulates an HTTP client.
10-
*
11-
* This should be replaced by an anonymous class in PHP 7.
9+
* Emulates a synchronous HTTP client with the help of an asynchronous client.
1210
*
1311
* @author Márk Sági-Kazár <[email protected]>
1412
*/
15-
class EmulatedHttpClient implements HttpClient, HttpAsyncClient
13+
final class EmulatedHttpClient implements HttpClient, HttpAsyncClient
1614
{
1715
use HttpAsyncClientDecorator;
1816
use HttpClientEmulator;

0 commit comments

Comments
 (0)