diff --git a/composer.json b/composer.json index 4ca4a24..920903c 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "php-http/httplug": "1.0.0-beta", "php-http/message-factory": "^1.0", "php-http/promise": "^0.1.1", - "php-http/client-common": "^0.1", + "php-http/client-common": "^0.2@dev", "symfony/options-resolver": "^2.6|^3.0" }, "require-dev": { @@ -32,6 +32,11 @@ "Http\\Client\\Plugin\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "spec\\Http\\Client\\Plugin\\": "spec/" + } + }, "suggest": { "php-http/message": "Allow to use Authentication and Encoding plugins", "php-http/cookie": "Allow to use CookiePlugin", diff --git a/spec/LoopPlugin.php b/spec/LoopPlugin.php new file mode 100644 index 0000000..2905f73 --- /dev/null +++ b/spec/LoopPlugin.php @@ -0,0 +1,14 @@ +sendRequest($request)->willReturn($response); - $this->sendRequest($request)->shouldReturnAnInstanceOf('Psr\Http\Message\ResponseInterface'); + $this->sendRequest($request)->shouldReturn($response); } function it_sends_async_request_with_underlying_client(HttpAsyncClient $asyncClient, RequestInterface $request, Promise $promise) @@ -55,9 +47,25 @@ function it_sends_async_request_with_underlying_client(HttpAsyncClient $asyncCli $this->sendAsyncRequest($request)->shouldReturn($promise); } + function it_sends_async_request_if_no_send_request(HttpAsyncClient $asyncClient, RequestInterface $request, ResponseInterface $response, Promise $promise) + { + $this->beConstructedWith($asyncClient->getWrappedObject()); + $asyncClient->sendAsyncRequest($request)->willReturn($promise); + $promise->wait()->willReturn($response); + + $this->sendRequest($request)->shouldReturn($response); + } + + function it_prefers_send_request(StubClient $client, RequestInterface $request, ResponseInterface $response) + { + $client->sendRequest($request)->willReturn($response); + + $this->sendRequest($request)->shouldReturn($response); + } + function it_throws_loop_exception(HttpClient $client, RequestInterface $request) { - $this->beConstructedWith($client, [new DefectuousPlugin()]); + $this->beConstructedWith($client, [new LoopPlugin()]); $this->shouldThrow('Http\Client\Plugin\Exception\LoopException')->duringSendRequest($request); } diff --git a/spec/StubClient.php b/spec/StubClient.php new file mode 100644 index 0000000..aecea99 --- /dev/null +++ b/spec/StubClient.php @@ -0,0 +1,18 @@ +httpClient = $httpClient; - } -} diff --git a/src/PluginClient.php b/src/PluginClient.php index a808865..ba9242e 100644 --- a/src/PluginClient.php +++ b/src/PluginClient.php @@ -2,9 +2,13 @@ namespace Http\Client\Plugin; +use Http\Client\Common\EmulatedHttpAsyncClient; +use Http\Client\Exception; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use Http\Client\Plugin\Exception\LoopException; +use Http\Promise\FulfilledPromise; +use Http\Promise\RejectedPromise; use Psr\Http\Message\RequestInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -48,7 +52,7 @@ public function __construct($client, array $plugins = [], array $options = []) if ($client instanceof HttpAsyncClient) { $this->client = $client; } elseif ($client instanceof HttpClient) { - $this->client = new EmulateAsyncClient($client); + $this->client = new EmulatedHttpAsyncClient($client); } else { throw new \RuntimeException('Client must be an instance of Http\\Client\\HttpClient or Http\\Client\\HttpAsyncClient'); } @@ -62,9 +66,23 @@ public function __construct($client, array $plugins = [], array $options = []) */ public function sendRequest(RequestInterface $request) { - $promise = $this->sendAsyncRequest($request); + // If we don't have an http client, use the async call + if (!($this->client instanceof HttpClient)) { + return $this->sendAsyncRequest($request)->wait(); + } + + // Else we want to use the synchronous call of the underlying client, and not the async one in the case + // we have both an async and sync call + $client = $this->client; + $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) use ($client) { + try { + return new FulfilledPromise($client->sendRequest($request)); + } catch (Exception $exception) { + return new RejectedPromise($exception); + } + }); - return $promise->wait(); + return $pluginChain($request)->wait(); } /** @@ -72,7 +90,10 @@ public function sendRequest(RequestInterface $request) */ public function sendAsyncRequest(RequestInterface $request) { - $pluginChain = $this->createPluginChain($this->plugins); + $client = $this->client; + $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) use ($client) { + return $client->sendAsyncRequest($request); + }); return $pluginChain($request); } @@ -95,20 +116,18 @@ protected function configure(array $options = []) } /** - * @param Plugin[] $pluginList + * Create the plugin chain. + * + * @param Plugin[] $pluginList A list of plugins + * @param callable $clientCallable Callable making the HTTP call * * @return callable */ - private function createPluginChain($pluginList) + private function createPluginChain($pluginList, callable $clientCallable) { - $client = $this->client; $options = $this->options; + $firstCallable = $lastCallable = $clientCallable; - $lastCallable = function (RequestInterface $request) use ($client) { - return $client->sendAsyncRequest($request); - }; - - $firstCallable = $lastCallable; while ($plugin = array_pop($pluginList)) { $lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable, &$firstCallable) { return $plugin->handleRequest($request, $lastCallable, $firstCallable);