diff --git a/README.md b/README.md index 12063f7..9503bb9 100644 --- a/README.md +++ b/README.md @@ -87,19 +87,15 @@ $satoshi = \Denpa\Bitcoin\to_satoshi($bitcoin); ``` To send asynchronous request, add Async to method name: ```php -use Denpa\Bitcoin\BitcoindResponse; - -$promise = $bitcoind->getBlockAsync( +$bitcoind->getBlockAsync( '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', - function (BitcoindResponse $success) { - // + function ($response) { + // success }, - function (\Exception $exception) { - // + function ($exception) { + // error } ); - -$promise->wait(); ``` You can also send requests using request method: @@ -131,20 +127,16 @@ $txid = $result->get(); ``` or requestAsync method for asynchronous calls: ```php -use Denpa\Bitcoin\BitcoindResponse; - -$promise = $bitcoind->requestAsync( +$bitcoind->requestAsync( 'getBlock', '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', - function (BitcoindResponse $success) { - // + function ($response) { + // success }, - function (\Exception $exception) { - // + function ($exception) { + // error } ); - -$promise->wait(); ``` ## Multi-Wallet RPC diff --git a/src/Client.php b/src/Client.php index d5931d4..782e161 100644 --- a/src/Client.php +++ b/src/Client.php @@ -7,6 +7,7 @@ use GuzzleHttp\Exception\RequestException; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; +use GuzzleHttp\Promise; use Psr\Http\Message\ResponseInterface; class Client @@ -25,6 +26,13 @@ class Client */ protected $config; + /** + * Array of GuzzleHttp promises. + * + * @var array + */ + protected $promises = []; + /** * URL path. * @@ -60,6 +68,18 @@ public function __construct($config = []) ]); } + /** + * Wait for all promises on object destruction. + * + * @return void + */ + public function __destruct() + { + if (!empty($this->promises)) { + Promise\settle($this->promises)->wait(); + } + } + /** * Gets http client config. * @@ -124,10 +144,8 @@ public function wallet($name) public function request($method, ...$params) { try { - $response = $this->client->request( - 'POST', - $this->path, - ['json' => $this->makeJson($method, $params)]); + $response = $this->client + ->post($this->path, $this->makeJson($method, $params)); if ($response->hasError()) { // throw exception on error @@ -136,17 +154,7 @@ public function request($method, ...$params) return $response; } catch (RequestException $exception) { - if ( - $exception->hasResponse() && - $exception->getResponse()->hasError() - ) { - throw new Exceptions\BitcoindException($exception->getResponse()->error()); - } - - throw new Exceptions\ClientException( - $exception->getMessage(), - $exception->getCode() - ); + throw $this->handleException($exception); } } @@ -155,31 +163,29 @@ public function request($method, ...$params) * * @param string $method * @param mixed $params - * @param callable|null $onFullfiled - * @param callable|null $onRejected + * @param callable|null $fulfilled + * @param callable|null $rejected * * @return \GuzzleHttp\Promise\Promise */ public function requestAsync( $method, $params = [], - callable $onFullfiled = null, - callable $onRejected = null) + callable $fulfilled = null, + callable $rejected = null) { - $promise = $this->client->requestAsync( - 'POST', - $this->path, - ['json' => $this->makeJson($method, $params)] - ); + $promise = $this->client + ->postAsync($this->path, $this->makeJson($method, $params)); - $promise->then( - function (ResponseInterface $response) use ($onFullfiled) { - $this->asyncFulfilled($response, $onFullfiled); - }, - function (RequestException $exception) use ($onRejected) { - $this->asyncRejected($exception, $onRejected); - } - ); + $promise->then(function ($response) use ($fulfilled) { + $this->onSuccess($response, $fulfilled); + }); + + $promise->otherwise(function ($exception) use ($rejected) { + $this->onError($exception, $rejected); + }); + + $this->promises[] = $promise; return $promise; } @@ -194,9 +200,8 @@ function (RequestException $exception) use ($onRejected) { */ public function __call($method, array $params = []) { - $method = str_ireplace('async', '', $method, $count); - if ($count > 0) { - return $this->requestAsync($method, ...$params); + if (strtolower(substr($method, -5)) == 'async') { + return $this->requestAsync(substr($method, 0, -5), ...$params); } return $this->request($method, ...$params); @@ -326,9 +331,11 @@ protected function parseUrl($config) protected function makeJson($method, $params = []) { return [ - 'method' => strtolower($method), - 'params' => (array) $params, - 'id' => $this->rpcId++, + 'json' => [ + 'method' => strtolower($method), + 'params' => (array) $params, + 'id' => $this->rpcId++, + ], ]; } @@ -340,15 +347,14 @@ protected function makeJson($method, $params = []) * * @return void */ - protected function asyncFulfilled(ResponseInterface $response, callable $callback = null) + protected function onSuccess(ResponseInterface $response, callable $callback = null) { - $error = null; - if ($response->hasError()) { - $error = new Exceptions\BitcoindException($response->error()); - } + if (!is_null($callback)) { + if ($response->hasError()) { + $response = new Exceptions\BitcoindException($response->error()); + } - if (is_callable($callback)) { - $callback($error ?: $response); + $callback($response); } } @@ -360,26 +366,33 @@ protected function asyncFulfilled(ResponseInterface $response, callable $callbac * * @return void */ - protected function asyncRejected(RequestException $exception, callable $callback = null) + protected function onError(RequestException $exception, callable $callback = null) { - if ( - $exception->hasResponse() && - $exception->getResponse()->hasError() - ) { - $exception = new Exceptions\BitcoindException( - $exception->getResponse()->error() - ); + if (!is_null($callback)) { + $callback($this->handleException($exception)); } + } - if ($exception instanceof RequestException) { - $exception = new Exceptions\ClientException( - $exception->getMessage(), - $exception->getCode() - ); - } + /** + * Handles exceptions. + * + * @param \Exception $exception + * + * @return \Exception + */ + protected function handleException($exception) + { + if ($exception->hasResponse()) { + $response = $exception->getResponse(); - if (is_callable($callback)) { - $callback($exception); + if ($response->hasError()) { + return new Exceptions\BitcoindException($response->error()); + } } + + return new Exceptions\ClientException( + $exception->getMessage(), + $exception->getCode() + ); } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 014ed21..f0e41ef 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -142,6 +142,7 @@ public function testRequest() ); $request = $this->getHistoryRequestBody(); + $this->assertEquals($this->makeRequestBody( 'getblockheader', $request['id'], @@ -191,8 +192,9 @@ public function testMultiWalletAsyncRequest() $this->bitcoind ->setClient($guzzle) ->wallet($wallet) - ->requestAsync('getbalance', []) - ->wait(); + ->requestAsync('getbalance', []); + + $this->bitcoind->__destruct(); $this->assertEquals( $this->getHistoryRequestUri()->getPath(), @@ -217,7 +219,7 @@ public function testAsyncRequest() }), ]); - $promise = $this->bitcoind + $this->bitcoind ->setClient($guzzle) ->requestAsync( 'getblockheader', @@ -225,8 +227,9 @@ public function testAsyncRequest() function ($response) use ($onFulfilled) { $onFulfilled($response); } - ) - ->wait(); + ); + + $this->bitcoind->__destruct(); $request = $this->getHistoryRequestBody(); $this->assertEquals($this->makeRequestBody( @@ -278,15 +281,16 @@ public function testAsyncMagic() }), ]); - $promise = $this->bitcoind + $this->bitcoind ->setClient($guzzle) ->getBlockHeaderAsync( '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', function ($response) use ($onFulfilled) { $onFulfilled($response); } - ) - ->wait(); + ); + + $this->bitcoind->__destruct(); $request = $this->getHistoryRequestBody(); $this->assertEquals($this->makeRequestBody( @@ -336,7 +340,7 @@ public function testAsyncBitcoindException() }), ]); - $promise = $this->bitcoind + $this->bitcoind ->setClient($guzzle) ->requestAsync( 'getrawtransaction', @@ -344,8 +348,9 @@ public function testAsyncBitcoindException() function ($response) use ($onFulfilled) { $onFulfilled($response); } - ) - ->wait(); + ); + + $this->bitcoind->__destruct(); } /** @@ -370,38 +375,6 @@ public function testRequestExceptionWithServerErrorCode() ); } - /** - * Test async request exception with error code. - * - * @return void - */ - public function testAsyncRequestExceptionWithServerErrorCode() - { - $guzzle = $this->mockGuzzle([ - $this->rawTransactionError(500), - ]); - - $onRejected = $this->mockCallable([ - $this->callback(function (Exceptions\BitcoindException $exception) { - return $exception->getMessage() == self::$rawTransactionError['message'] && - $exception->getCode() == self::$rawTransactionError['code']; - }), - ]); - - $promise = $this->bitcoind - ->setClient($guzzle) - ->requestAsync( - 'getrawtransaction', - '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', - null, - function ($exception) use ($onRejected) { - $onRejected($exception); - } - ); - - $promise->wait(false); - } - /** * Test request exception with empty response body. * @@ -435,25 +408,25 @@ public function testAsyncRequestExceptionWithEmptyResponseBody() new Response(500), ]); - $onRejected = $this->mockCallable([ + $rejected = $this->mockCallable([ $this->callback(function (Exceptions\ClientException $exception) { return $exception->getMessage() == $this->error500() && $exception->getCode() == 500; }), ]); - $promise = $this->bitcoind + $this->bitcoind ->setClient($guzzle) ->requestAsync( 'getrawtransaction', '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', null, - function ($exception) use ($onRejected) { - $onRejected($exception); + function ($exception) use ($rejected) { + $rejected($exception); } ); - $promise->wait(false); + $this->bitcoind->__destruct(); } /** @@ -481,8 +454,6 @@ public function testRequestExceptionWithResponseBody() /** * Test async request exception with response. * - * @expectedException GuzzleHttp\Exception\RequestException - * * @return void */ public function testAsyncRequestExceptionWithResponseBody() @@ -498,7 +469,7 @@ public function testAsyncRequestExceptionWithResponseBody() }), ]); - $promise = $this->bitcoind + $this->bitcoind ->setClient($guzzle) ->requestAsync( 'getrawtransaction', @@ -507,8 +478,9 @@ public function testAsyncRequestExceptionWithResponseBody() function ($exception) use ($onRejected) { $onRejected($exception); } - ) - ->wait(); + ); + + $this->bitcoind->__destruct(); } /** @@ -536,8 +508,6 @@ public function testRequestExceptionWithNoResponseBody() /** * Test async request exception with no response. * - * @expectedException GuzzleHttp\Exception\RequestException - * * @return void */ public function testAsyncRequestExceptionWithNoResponseBody() @@ -546,23 +516,24 @@ public function testAsyncRequestExceptionWithNoResponseBody() $this->requestExceptionWithoutResponse(), ]); - $onRejected = $this->mockCallable([ + $rejected = $this->mockCallable([ $this->callback(function (Exceptions\ClientException $exception) { return $exception->getMessage() == 'test' && $exception->getCode() == 0; }), ]); - $promise = $this->bitcoind + $this->bitcoind ->setClient($guzzle) ->requestAsync( 'getrawtransaction', '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', null, - function ($exception) use ($onRejected) { - $onRejected($exception); + function ($exception) use ($rejected) { + $rejected($exception); } - ) - ->wait(); + ); + + $this->bitcoind->__destruct(); } } diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index 8c1dfae..f62a236 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -7,6 +7,9 @@ class FunctionsTest extends TestCase /** * Test satoshi to btc converter. * + * @param int $satoshi + * @param string $bitcoin + * * @return void * * @dataProvider valueProvider @@ -19,6 +22,9 @@ public function testToBtc($satoshi, $bitcoin) /** * Test btc to satoshi converter. * + * @param int $satoshi + * @param string $bitcoin + * * @return void * * @dataProvider valueProvider @@ -31,6 +37,10 @@ public function testToSatoshi($satoshi, $bitcoin) /** * Test float to fixed converter. * + * @param float $float + * @param int $precision + * @param string $expected + * * @return void * * @dataProvider floatProvider