-
Notifications
You must be signed in to change notification settings - Fork 6
Prefer the sync call if possible #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
namespace spec\Http\Client\Plugin; | ||
|
||
use Http\Client\Plugin\Plugin; | ||
use Psr\Http\Message\RequestInterface; | ||
|
||
class LoopPlugin implements Plugin | ||
{ | ||
public function handleRequest(RequestInterface $request, callable $next, callable $first) | ||
{ | ||
return $first($request); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
namespace spec\Http\Client\Plugin; | ||
|
||
use Http\Client\HttpAsyncClient; | ||
use Http\Client\HttpClient; | ||
use Psr\Http\Message\RequestInterface; | ||
|
||
class StubClient implements HttpAsyncClient, HttpClient | ||
{ | ||
public function sendAsyncRequest(RequestInterface $request) | ||
{ | ||
} | ||
|
||
public function sendRequest(RequestInterface $request) | ||
{ | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,17 +66,34 @@ 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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically, this doesn't make any difference IMO. When a non-async client is passed, it is emulated to be an async client, which means you are free to always rely on sendAsyncRequest. If it is an async client, the upper condition would just return with an async client call. If it is not an async client, then the emulated async request send method will just do the same as this callback does. What's the difference then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difference is if you have an async client and a sync client, it will use the correct underlying method for sendRequest or sendAsyncRequest, before it was always using the sendAsyncRequest one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, but I still can't see the difference. Here is what happens now: If the client is async (not nonasync) then the async version is called. If it is sync, we emulate an async one. In the plugin clien's sync call we wrap the sync client call with the same code as we did in case of the emulated client. Same code, whats the difference apart from the method name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And if it is both async and sync ? |
||
try { | ||
return new FulfilledPromise($client->sendRequest($request)); | ||
} catch (Exception $exception) { | ||
return new RejectedPromise($exception); | ||
} | ||
}); | ||
|
||
return $promise->wait(); | ||
return $pluginChain($request)->wait(); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function sendAsyncRequest(RequestInterface $request) | ||
{ | ||
$pluginChain = $this->createPluginChain($this->plugins); | ||
$client = $this->client; | ||
$pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) use ($client) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In PHP 5.4, you can access the |
||
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); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we just emulate a sync client if an async client is passed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we can, but by doing this we save many calls, from sync to async and async to sync.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't matter IMO. If we emulate a sync client as well, this condition and call becomes unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The emulation add a non necessary layer of code in this case.
As we will have a emulated sync call, which transform promise into a response / exception which is then transformed into promise, and then retransformed into response / exception.
Here we only do the conversion from promise to response / exception once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant async emulation here. We don't need sync emulation IMO. There you also have only one conversion (like now).