diff --git a/.gitignore b/.gitignore index f57def41..00b83854 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ composer.lock # nodejs node_modules/ package-lock.json -package.json \ No newline at end of file +package.json diff --git a/README.md b/README.md index 9ba283d3..24745305 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,11 @@ $web3 = new Web3('http://localhost:8545'); ```php use Web3\Web3; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\HttpRequestManager; -$web3 = new Web3(new HttpProvider(new HttpRequestManager('http://localhost:8545'))); +$web3 = new Web3(new HttpProvider('http://localhost:8545')); // timeout -$web3 = new Web3(new HttpProvider(new HttpRequestManager('http://localhost:8545', 0.1))); +$web3 = new Web3(new HttpProvider('http://localhost:8545', 0.1)); ``` ### You can use callback to each rpc call: @@ -62,6 +61,43 @@ $web3->clientVersion(function ($err, $version) { }); ``` +### Async +```php +use Web3\Web3; +use Web3\Providers\HttpAsyncProvider; + +$web3 = new Web3(new HttpAsyncProvider('http://localhost:8545')); + +// timeout +$web3 = new Web3(new HttpAsyncProvider('http://localhost:8545', 0.1)); + +// await +$promise = $web3->clientVersion(function ($err, $version) { + // do somthing +}); +Async\await($promise); +``` + +### Websocket +```php +use Web3\Web3; +use Web3\Providers\WsProvider; + +$web3 = new Web3(new WsProvider('ws://localhost:8545')); + +// timeout +$web3 = new Web3(new WsProvider('ws://localhost:8545', 0.1)); + +// await +$promise = $web3->clientVersion(function ($err, $version) { + // do somthing +}); +Async\await($promise); + +// close connection +$web3->provider->close(); +``` + ### Eth ```php use Web3\Web3; diff --git a/composer.json b/composer.json index b58bfda9..61d30dc2 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,9 @@ "react/async": "^4.0.0|^3.1.0", "react/promise": "^2.9.0", "react/event-loop": "^1.2", - "react/socket": "^1.13" + "react/socket": "^1.13", + "ratchet/pawl": "^0.4.1", + "react/promise-timer": "^1.10" }, "require-dev": { "phpunit/phpunit": "~8.0|~9.0" diff --git a/examples/accounts2.php b/examples/accounts2.php new file mode 100644 index 00000000..671abab9 --- /dev/null +++ b/examples/accounts2.php @@ -0,0 +1,36 @@ +eth; + +echo 'Eth Get Account and Balance' . PHP_EOL; +$promises = []; +$promises[] = $eth->accounts(function ($err, $accounts) use ($eth) { + if ($err !== null) { + echo 'Error: ' . $err->getMessage(); + return; + } + + foreach ($accounts as $account) { + echo 'Account: ' . $account . PHP_EOL; + + $promises[] = $eth->getBalance($account, function ($err, $balance) { + if ($err !== null) { + echo 'Error: ' . $err->getMessage(); + return; + } + echo 'Balance: ' . $balance . PHP_EOL; + }); + } + + // wait all promises + Async\await(Promise\all($promises)); + echo 'close connection...' . PHP_EOL; + $eth->provider->close(); +}); diff --git a/src/Contract.php b/src/Contract.php index ba5c51ea..b75e2402 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -14,8 +14,7 @@ use InvalidArgumentException; use Web3\Providers\Provider; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; +use Web3\Providers\WsProvider; use Web3\Utils; use Web3\Eth; use Web3\Contracts\Ethabi; @@ -118,9 +117,9 @@ public function __construct($provider, $abi, $defaultBlock = 'latest') if (is_string($provider) && (filter_var($provider, FILTER_VALIDATE_URL) !== false)) { // check the uri schema if (preg_match('/^https?:\/\//', $provider) === 1) { - $requestManager = new HttpRequestManager($provider); - - $this->provider = new HttpProvider($requestManager); + $this->provider = new HttpProvider($provider); + } else if (preg_match('/^wss?:\/\//', $provider) === 1) { + $this->provider = new WsProvider($provider); } } else if ($provider instanceof Provider) { $this->provider = $provider; @@ -177,7 +176,7 @@ public function __construct($provider, $abi, $defaultBlock = 'latest') // if (empty($this->provider)) { // throw new \RuntimeException('Please set provider first.'); // } - // $class = explode('\\', get_class()); + // $class = explode('\\', get_class($this)); // if (preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { // } // } diff --git a/src/Eth.php b/src/Eth.php index 95ff9a48..c29012d1 100644 --- a/src/Eth.php +++ b/src/Eth.php @@ -13,8 +13,7 @@ use Web3\Providers\Provider; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; +use Web3\Providers\WsProvider; class Eth { @@ -45,16 +44,17 @@ class Eth * construct * * @param string|\Web3\Providers\Provider $provider + * @param float $timeout * @return void */ - public function __construct($provider) + public function __construct($provider, $timeout = 1) { if (is_string($provider) && (filter_var($provider, FILTER_VALIDATE_URL) !== false)) { // check the uri schema if (preg_match('/^https?:\/\//', $provider) === 1) { - $requestManager = new HttpRequestManager($provider); - - $this->provider = new HttpProvider($requestManager); + $this->provider = new HttpProvider($provider, $timeout); + } else if (preg_match('/^wss?:\/\//', $provider) === 1) { + $this->provider = new WsProvider($provider, $timeout); } } else if ($provider instanceof Provider) { $this->provider = $provider; @@ -74,7 +74,7 @@ public function __call($name, $arguments) throw new \RuntimeException('Please set provider first.'); } - $class = explode('\\', get_class()); + $class = explode('\\', get_class($this)); if (preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { $method = strtolower($class[1]) . '_' . $name; diff --git a/src/Net.php b/src/Net.php index adae1047..8fd16514 100644 --- a/src/Net.php +++ b/src/Net.php @@ -13,8 +13,7 @@ use Web3\Providers\Provider; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; +use Web3\Providers\WsProvider; class Net { @@ -45,16 +44,17 @@ class Net * construct * * @param string|\Web3\Providers\Provider $provider + * @param float $timeout * @return void */ - public function __construct($provider) + public function __construct($provider, $timeout = 1) { if (is_string($provider) && (filter_var($provider, FILTER_VALIDATE_URL) !== false)) { // check the uri schema if (preg_match('/^https?:\/\//', $provider) === 1) { - $requestManager = new HttpRequestManager($provider); - - $this->provider = new HttpProvider($requestManager); + $this->provider = new HttpProvider($provider, $timeout); + } else if (preg_match('/^wss?:\/\//', $provider) === 1) { + $this->provider = new WsProvider($provider, $timeout); } } else if ($provider instanceof Provider) { $this->provider = $provider; @@ -74,7 +74,7 @@ public function __call($name, $arguments) throw new \RuntimeException('Please set provider first.'); } - $class = explode('\\', get_class()); + $class = explode('\\', get_class($this)); if (preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { $method = strtolower($class[1]) . '_' . $name; diff --git a/src/Personal.php b/src/Personal.php index 8b479776..087ed51a 100644 --- a/src/Personal.php +++ b/src/Personal.php @@ -13,8 +13,7 @@ use Web3\Providers\Provider; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; +use Web3\Providers\WsProvider; class Personal { @@ -45,16 +44,17 @@ class Personal * construct * * @param string|\Web3\Providers\Provider $provider + * @param float $timeout * @return void */ - public function __construct($provider) + public function __construct($provider, $timeout = 1) { if (is_string($provider) && (filter_var($provider, FILTER_VALIDATE_URL) !== false)) { // check the uri schema if (preg_match('/^https?:\/\//', $provider) === 1) { - $requestManager = new HttpRequestManager($provider); - - $this->provider = new HttpProvider($requestManager); + $this->provider = new HttpProvider($provider, $timeout); + } else if (preg_match('/^wss?:\/\//', $provider) === 1) { + $this->provider = new WsProvider($provider, $timeout); } } else if ($provider instanceof Provider) { $this->provider = $provider; @@ -74,7 +74,7 @@ public function __call($name, $arguments) throw new \RuntimeException('Please set provider first.'); } - $class = explode('\\', get_class()); + $class = explode('\\', get_class($this)); if (preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { $method = strtolower($class[1]) . '_' . $name; diff --git a/src/RequestManagers/HttpAsyncRequestManager.php b/src/Providers/HttpAsyncProvider.php similarity index 57% rename from src/RequestManagers/HttpAsyncRequestManager.php rename to src/Providers/HttpAsyncProvider.php index 0c655999..693fa679 100644 --- a/src/RequestManagers/HttpAsyncRequestManager.php +++ b/src/Providers/HttpAsyncProvider.php @@ -2,29 +2,37 @@ /** * This file is part of web3.php package. - * + * * (c) Kuan-Cheng,Lai - * + * * @author Peter Lai * @license MIT */ -namespace Web3\RequestManagers; +namespace Web3\Providers; use InvalidArgumentException; use Psr\Http\Message\StreamInterface; use RuntimeException as RPCException; use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\Exception\RequestException; use React; use React\Async; use React\EventLoop\Loop; use React\Http\Browser; use React\Socket\Connector; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\IRequestManager; +use Web3\Providers\Provider; +use Web3\Providers\IProvider; -class HttpAsyncRequestManager extends RequestManager implements IRequestManager +class HttpAsyncProvider extends Provider implements IProvider { + /** + * methods + * + * @var array + */ + protected $methods = []; + /** * client * @@ -36,7 +44,7 @@ class HttpAsyncRequestManager extends RequestManager implements IRequestManager * construct * * @param string $host - * @param int $timeout + * @param float $timeout * @return void */ public function __construct($host, $timeout = 1) @@ -48,6 +56,92 @@ public function __construct($host, $timeout = 1) $this->client = (new Browser($connector, Loop::get()))->withRejectErrorResponse(false); } + /** + * close + * + * @return void + */ + public function close() {} + + /** + * send + * + * @param \Web3\Methods\Method $method + * @param callable $callback + * @return void + */ + public function send($method, $callback) + { + $payload = $method->toPayloadString(); + + if (!$this->isBatch) { + $proxy = function ($err, $res) use ($method, $callback) { + if ($err !== null) { + return call_user_func($callback, $err, null); + } + if (!is_array($res)) { + $res = $method->transform([$res], $method->outputFormatters); + return call_user_func($callback, null, $res[0]); + } + $res = $method->transform($res, $method->outputFormatters); + + return call_user_func($callback, null, $res); + }; + return $this->sendPayload($payload, $proxy); + } else { + $this->methods[] = $method; + $this->batch[] = $payload; + } + } + + /** + * batch + * + * @param bool $status + * @return void + */ + public function batch($status) + { + $status = is_bool($status); + + $this->isBatch = $status; + } + + /** + * execute + * + * @param callable $callback + * @return void + */ + public function execute($callback) + { + if (!$this->isBatch) { + throw new \RuntimeException('Please batch json rpc first.'); + } + $methods = $this->methods; + $proxy = function ($err, $res) use ($methods, $callback) { + if ($err !== null) { + return call_user_func($callback, $err, null); + } + foreach ($methods as $key => $method) { + if (isset($res[$key])) { + if (!is_array($res[$key])) { + $transformed = $method->transform([$res[$key]], $method->outputFormatters); + $res[$key] = $transformed[0]; + } else { + $transformed = $method->transform($res[$key], $method->outputFormatters); + $res[$key] = $transformed; + } + } + } + return call_user_func($callback, null, $res); + }; + $r = $this->sendPayload('[' . implode(',', $this->batch) . ']', $proxy); + $this->methods = []; + $this->batch = []; + return $r; + } + /** * sendPayload * @@ -122,4 +216,4 @@ public function sendPayload($payload, $callback) return Async\coroutine($request); } -} +} \ No newline at end of file diff --git a/src/Providers/HttpProvider.php b/src/Providers/HttpProvider.php index 05e4f366..d68aafa3 100644 --- a/src/Providers/HttpProvider.php +++ b/src/Providers/HttpProvider.php @@ -11,9 +11,14 @@ namespace Web3\Providers; +use InvalidArgumentException; +use Psr\Http\Message\StreamInterface; +use RuntimeException as RPCException; +use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Client; use Web3\Providers\Provider; use Web3\Providers\IProvider; -use Web3\RequestManagers\RequestManager; class HttpProvider extends Provider implements IProvider { @@ -24,17 +29,33 @@ class HttpProvider extends Provider implements IProvider */ protected $methods = []; + /** + * client + * + * @var \GuzzleHttp + */ + protected $client; + /** * construct - * - * @param \Web3\RequestManagers\RequestManager $requestManager + * + * @param string $host + * @param float $timeout * @return void */ - public function __construct(RequestManager $requestManager) + public function __construct($host, $timeout = 1) { - parent::__construct($requestManager); + parent::__construct($host, $timeout); + $this->client = new Client; } + /** + * close + * + * @return void + */ + public function close() {} + /** * send * @@ -59,7 +80,7 @@ public function send($method, $callback) return call_user_func($callback, null, $res); }; - return $this->requestManager->sendPayload($payload, $proxy); + return $this->sendPayload($payload, $proxy); } else { $this->methods[] = $method; $this->batch[] = $payload; @@ -108,9 +129,79 @@ public function execute($callback) } return call_user_func($callback, null, $res); }; - $r = $this->requestManager->sendPayload('[' . implode(',', $this->batch) . ']', $proxy); + $r = $this->sendPayload('[' . implode(',', $this->batch) . ']', $proxy); $this->methods = []; $this->batch = []; return $r; } + + /** + * sendPayload + * + * @param string $payload + * @param callable $callback + * @return void + */ + public function sendPayload($payload, $callback) + { + if (!is_string($payload)) { + throw new InvalidArgumentException('Payload must be string.'); + } + + try { + $res = $this->client->post($this->host, [ + 'headers' => [ + 'content-type' => 'application/json' + ], + 'body' => $payload, + 'timeout' => $this->timeout, + 'connect_timeout' => $this->timeout + ]); + /** + * @var StreamInterface $stream ; + */ + $stream = $res->getBody(); + $json = json_decode($stream); + $stream->close(); + + if (JSON_ERROR_NONE !== json_last_error()) { + call_user_func($callback, new InvalidArgumentException('json_decode error: ' . json_last_error_msg()), null); + } + if (is_array($json)) { + // batch results + $results = []; + $errors = []; + + foreach ($json as $result) { + if (property_exists($result,'result')) { + $results[] = $result->result; + } else { + if (isset($json->error)) { + $error = $json->error; + $errors[] = new RPCException(mb_ereg_replace('Error: ', '', $error->message), $error->code); + } else { + $results[] = null; + } + } + } + if (count($errors) > 0) { + call_user_func($callback, $errors, $results); + } else { + call_user_func($callback, null, $results); + } + } elseif (property_exists($json,'result')) { + call_user_func($callback, null, $json->result); + } else { + if (isset($json->error)) { + $error = $json->error; + + call_user_func($callback, new RPCException(mb_ereg_replace('Error: ', '', $error->message), $error->code), null); + } else { + call_user_func($callback, new RPCException('Something wrong happened.'), null); + } + } + } catch (RequestException $err) { + call_user_func($callback, $err, null); + } + } } \ No newline at end of file diff --git a/src/Providers/IProvider.php b/src/Providers/IProvider.php index bbc8a7e6..d0f15419 100644 --- a/src/Providers/IProvider.php +++ b/src/Providers/IProvider.php @@ -13,6 +13,13 @@ interface IProvider { + /** + * close + * + * @return void + */ + public function close(); + /** * send * @@ -37,4 +44,13 @@ public function batch($status); * @return void */ public function execute($callback); + + /** + * sendPayload + * + * @param string $payload + * @param callable $callback + * @return void + */ + public function sendPayload($payload, $callback); } \ No newline at end of file diff --git a/src/Providers/Provider.php b/src/Providers/Provider.php index 00ee9152..b4ad5157 100644 --- a/src/Providers/Provider.php +++ b/src/Providers/Provider.php @@ -11,16 +11,21 @@ namespace Web3\Providers; -use Web3\RequestManagers\RequestManager; - class Provider { /** - * requestManager + * host + * + * @var string + */ + protected $host; + + /** + * timeout * - * @var \Web3\RequestManagers\RequestManager + * @var float */ - protected $requestManager; + protected $timeout; /** * isBatch @@ -53,12 +58,14 @@ class Provider /** * construct * - * @param \Web3\RequestManagers\RequestManager $requestManager + * @param string $host + * @param float $timeout * @return void */ - public function __construct(RequestManager $requestManager) + public function __construct($host, $timeout=1) { - $this->requestManager = $requestManager; + $this->host = $host; + $this->timeout = (float) $timeout; } /** @@ -95,13 +102,23 @@ public function __set($name, $value) } /** - * getRequestManager + * getHost + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * getTimeout * - * @return \Web3\RequestManagers\RequestManager + * @return float */ - public function getRequestManager() + public function getTimeout() { - return $this->requestManager; + return $this->timeout; } /** diff --git a/src/Providers/WsClient.php b/src/Providers/WsClient.php new file mode 100644 index 00000000..08537011 --- /dev/null +++ b/src/Providers/WsClient.php @@ -0,0 +1,234 @@ + + * + * @author Peter Lai + * @license MIT + */ + + namespace Web3\Providers; + +use Ratchet\Client\Connector; +use React; +use React\Async; +use React\EventLoop\Loop; +use React\Promise\Deferred; +use React\Promise\Timer; +use React\Promise\Timer\TimeoutException; + +use Ratchet\RFC6455\Messaging\Frame; +use Ratchet\RFC6455\Messaging\Message; + +use Exception; +use RuntimeException; + +class NoOriginHeaderConnector extends Connector { + public function generateRequest($url, array $subProtocols, array $headers) { + return parent::generateRequest($url, $subProtocols, $headers)->withoutHeader('Origin'); + } +} + +class WsClient { + + public $url; + + public $error; + public $connectionEstablished; + public $timeout = 1; + // is server with feature ping/pong and keep alive? + // can remove this if not? + public $pingInterval; + public $keepAlive = 30; + public $maxPingPongMisses = 2.0; + public $lastPong = null; + public $ping = null; + public $connection = null; + public $connected; + public $isConnected = false; + public $noOriginHeader = true; + public $headers = []; + + // ratchet/pawl/reactphp stuff + public $connector = null; + + protected $deferredConnected; + protected $deferredMessages = []; + + public function resolve($result) { + $deferred = array_shift($this->deferredMessages); + if ($deferred) { + $deferred->resolve($result); + } + } + + public function reject($result) { + foreach ($this->deferredMessages as $deferred) { + $deferred->reject($result); + } + } + + public function __construct( + $url, + $timeout = 1, + $keepAlive = 30, + $maxPingPongMisses = 2.0, + $noOriginHeader = true, + $headers = [] + ) { + + $this->url = $url; + + $this->timeout = $timeout; + $this->keepAlive = $keepAlive; + $this->maxPingPongMisses = $maxPingPongMisses; + $this->noOriginHeader = $noOriginHeader; + $this->headers = $headers; + + $deferred = new Deferred(); + $this->connected = $deferred->promise(); + $this->deferredConnected = $deferred; + } + + public function set_ws_connector() { + $react_default_connector = new React\Socket\Connector(); + if ($this->noOriginHeader) { + $this->connector = new NoOriginHeaderConnector(Loop::get(), $react_default_connector); + } else { + $this->connector = new Connector(Loop::get(), $react_default_connector); + } + } + + public function create_connection() { + $connect = function () { + $timeout = $this->timeout; + $headers = $this->headers; + $promise = call_user_func($this->connector, $this->url, [], $headers); + Timer\timeout($promise, $timeout, Loop::get())->then( + function($connection) { + $this->connection = $connection; + $this->connection->on('message', array($this, 'on_message')); + $this->connection->on('close', array($this, 'on_close')); + $this->connection->on('error', array($this, 'on_error')); + $this->connection->on('pong', array($this, 'on_pong')); + $this->isConnected = true; + $this->connectionEstablished = $this->milliseconds(); + $this->deferredConnected->resolve($this->url); + $this->set_ping_interval(); + }, + function(\Exception $error) { + // the ordering of these exceptions is important + // since one inherits another + if ($error instanceof TimeoutException) { + } else if ($error instanceof RuntimeException) { + // connection failed or rejected + } + $this->on_error($error); + } + ); + }; + if (function_exists('React\\Async\\async')) { + $connect = Async\async($connect); + } + return Async\coroutine($connect); + } + + public function connect($backoff_delay = 0) { + if (!$this->connection) { + $this->connection = true; + if ($backoff_delay) { + $callback = array($this, 'create_connection'); + Loop::addTimer(((float)$backoff_delay), $callback); + } else { + $this->create_connection(); + } + } + return $this->connected; + } + + public function send($data) { + $send = function () use ($data) { + $this->connection->send($data); + $deferred = new Deferred(); + $this->deferredMessages[] = $deferred; + return Async\await($deferred->promise()); + }; + if (function_exists('React\\Async\\async')) { + $send = Async\async($send); + } + return Async\coroutine($send); + } + + public function close() { + $this->connection->close(); + } + + public function on_pong($message) { + $this->lastPong = $this->milliseconds(); + } + + public function on_error($error) { + $this->error = $error; + $this->reset($error); + } + + public function on_close($message) { + if (!$this->error) { + // todo: exception types for server-side disconnects + $this->reset(new RuntimeException($message)); + } + } + + public function on_message(Message $message) { + + try { + $message = (string) $message; + } catch (Exception $e) { + // reset with a json encoding error? + } + + try { + $this->resolve($message); + } catch (Exception $error) { + $this->reject($error); + } + } + + public function reset($error) { + $this->clear_ping_interval(); + $this->reject($error); + } + + public function set_ping_interval() { + if ($this->keepAlive) { + $delay = $this->keepAlive; + $this->pingInterval = Loop::addPeriodicTimer($delay, array($this, 'on_ping_interval')); + } + } + + public function clear_ping_interval() { + if ($this->pingInterval) { + Loop::cancelTimer($this->pingInterval); + } + } + + public function milliseconds() { + list($msec, $sec) = explode(' ', microtime()); + return (int)($sec . substr($msec, 2, 3)); + } + + public function on_ping_interval() { + if ($this->keepAlive && $this->isConnected) { + $now = $this->milliseconds(); + $this->lastPong = isset ($this->lastPong) ? $this->lastPong : $now; + if (($this->lastPong + $this->keepAlive * $this->maxPingPongMisses) < $now) { + $this->on_error(new RuntimeException('Connection to ' . $this->url . ' timed out due to a ping-pong keepalive missing on time')); + } else { + $this->connection->send(new Frame('', true, Frame::OP_PING)); + } + } + } +}; diff --git a/src/Providers/WsProvider.php b/src/Providers/WsProvider.php new file mode 100644 index 00000000..da702174 --- /dev/null +++ b/src/Providers/WsProvider.php @@ -0,0 +1,210 @@ + + * + * @author Peter Lai + * @license MIT + */ + +namespace Web3\Providers; + +use React\Async; +use Web3\Providers\Provider; +use Web3\Providers\IProvider; + +class WsProvider extends Provider implements IProvider +{ + /** + * methods + * + * @var array + */ + protected $methods = []; + + /** + * client + * + * @var \Web3\Providers\WsClient + */ + protected $client; + + /** + * construct + * + * @param string $host + * @param float $timeout + * @return void + */ + public function __construct($host, $timeout = 1) + { + parent::__construct($host, $timeout); + $this->client = new WsClient( + $host, + $timeout + ); + $this->client->set_ws_connector(); + } + + /** + * close + * + * @return void + */ + public function close() + { + $this->client->close(); + } + + /** + * send + * + * @param \Web3\Methods\Method $method + * @param callable $callback + * @return void + */ + public function send($method, $callback) + { + $payload = $method->toPayloadString(); + + if (!$this->isBatch) { + $proxy = function ($err, $res) use ($method, $callback) { + if ($err !== null) { + return call_user_func($callback, $err, null); + } + if (!is_array($res)) { + $res = $method->transform([$res], $method->outputFormatters); + return call_user_func($callback, null, $res[0]); + } + $res = $method->transform($res, $method->outputFormatters); + + return call_user_func($callback, null, $res); + }; + return $this->sendPayload($payload, $proxy); + } else { + $this->methods[] = $method; + $this->batch[] = $payload; + } + } + + /** + * batch + * + * @param bool $status + * @return void + */ + public function batch($status) + { + $status = is_bool($status); + + $this->isBatch = $status; + } + + /** + * execute + * + * @param callable $callback + * @return void + */ + public function execute($callback) + { + if (!$this->isBatch) { + throw new \RuntimeException('Please batch json rpc first.'); + } + $methods = $this->methods; + $proxy = function ($err, $res) use ($methods, $callback) { + if ($err !== null) { + return call_user_func($callback, $err, null); + } + foreach ($methods as $key => $method) { + if (isset($res[$key])) { + if (!is_array($res[$key])) { + $transformed = $method->transform([$res[$key]], $method->outputFormatters); + $res[$key] = $transformed[0]; + } else { + $transformed = $method->transform($res[$key], $method->outputFormatters); + $res[$key] = $transformed; + } + } + } + return call_user_func($callback, null, $res); + }; + $r = $this->sendPayload('[' . implode(',', $this->batch) . ']', $proxy); + $this->methods = []; + $this->batch = []; + return $r; + } + + /** + * sendPayload + * + * @param string $payload + * @param callable $callback + * @return void + */ + public function sendPayload($payload, $callback) + { + if (!is_string($payload)) { + throw new \InvalidArgumentException('Payload must be string.'); + } + + if (!$this->client->isConnected) { + Async\await($this->client->connect(0)); + } + + $host = $this->host; + $request = function () use ($host, $payload, $callback) { + try { + $res = Async\await($this->client->send($payload)); + $json = json_decode($res); + + if (JSON_ERROR_NONE !== json_last_error()) { + call_user_func($callback, new InvalidArgumentException('json_decode error: ' . json_last_error_msg()), null); + } + if (is_array($json)) { + // batch results + $results = []; + $errors = []; + + foreach ($json as $result) { + if (property_exists($result,'result')) { + $results[] = $result->result; + } else { + if (isset($json->error)) { + $error = $json->error; + $errors[] = new RPCException(mb_ereg_replace('Error: ', '', $error->message), $error->code); + } else { + $results[] = null; + } + } + } + if (count($errors) > 0) { + call_user_func($callback, $errors, $results); + } else { + call_user_func($callback, null, $results); + } + } elseif (property_exists($json,'result')) { + call_user_func($callback, null, $json->result); + } else { + if (isset($json->error)) { + $error = $json->error; + + call_user_func($callback, new RPCException(mb_ereg_replace('Error: ', '', $error->message), $error->code), null); + } else { + call_user_func($callback, new RPCException('Something wrong happened.'), null); + } + } + } catch (Exception $err) { + call_user_func($callback, $err, null); + } + }; + + if (function_exists('React\\Async\\async')) { + $request = Async\async($request); + } + + return Async\coroutine($request); + } +} \ No newline at end of file diff --git a/src/RequestManagers/HttpRequestManager.php b/src/RequestManagers/HttpRequestManager.php deleted file mode 100644 index b7e4e75e..00000000 --- a/src/RequestManagers/HttpRequestManager.php +++ /dev/null @@ -1,114 +0,0 @@ - - * - * @author Peter Lai - * @license MIT - */ - -namespace Web3\RequestManagers; - -use InvalidArgumentException; -use Psr\Http\Message\StreamInterface; -use RuntimeException as RPCException; -use Psr\Http\Message\ResponseInterface; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Client; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\IRequestManager; - -class HttpRequestManager extends RequestManager implements IRequestManager -{ - /** - * client - * - * @var \GuzzleHttp - */ - protected $client; - - /** - * construct - * - * @param string $host - * @param int $timeout - * @return void - */ - public function __construct($host, $timeout = 1) - { - parent::__construct($host, $timeout); - $this->client = new Client; - } - - /** - * sendPayload - * - * @param string $payload - * @param callable $callback - * @return void - */ - public function sendPayload($payload, $callback) - { - if (!is_string($payload)) { - throw new \InvalidArgumentException('Payload must be string.'); - } - - try { - $res = $this->client->post($this->host, [ - 'headers' => [ - 'content-type' => 'application/json' - ], - 'body' => $payload, - 'timeout' => $this->timeout, - 'connect_timeout' => $this->timeout - ]); - /** - * @var StreamInterface $stream ; - */ - $stream = $res->getBody(); - $json = json_decode($stream); - $stream->close(); - - if (JSON_ERROR_NONE !== json_last_error()) { - call_user_func($callback, new InvalidArgumentException('json_decode error: ' . json_last_error_msg()), null); - } - if (is_array($json)) { - // batch results - $results = []; - $errors = []; - - foreach ($json as $result) { - if (property_exists($result,'result')) { - $results[] = $result->result; - } else { - if (isset($json->error)) { - $error = $json->error; - $errors[] = new RPCException(mb_ereg_replace('Error: ', '', $error->message), $error->code); - } else { - $results[] = null; - } - } - } - if (count($errors) > 0) { - call_user_func($callback, $errors, $results); - } else { - call_user_func($callback, null, $results); - } - } elseif (property_exists($json,'result')) { - call_user_func($callback, null, $json->result); - } else { - if (isset($json->error)) { - $error = $json->error; - - call_user_func($callback, new RPCException(mb_ereg_replace('Error: ', '', $error->message), $error->code), null); - } else { - call_user_func($callback, new RPCException('Something wrong happened.'), null); - } - } - } catch (RequestException $err) { - call_user_func($callback, $err, null); - } - } -} diff --git a/src/RequestManagers/IRequestManager.php b/src/RequestManagers/IRequestManager.php deleted file mode 100644 index 8056051d..00000000 --- a/src/RequestManagers/IRequestManager.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * @author Peter Lai - * @license MIT - */ - -namespace Web3\RequestManagers; - -interface IRequestManager -{ - /** - * sendPayload - * - * @param string $payload - * @param callable $callback - * @return void - */ - public function sendPayload($payload, $callback); -} \ No newline at end of file diff --git a/src/RequestManagers/RequestManager.php b/src/RequestManagers/RequestManager.php deleted file mode 100644 index 585dae03..00000000 --- a/src/RequestManagers/RequestManager.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * @author Peter Lai - * @license MIT - */ - -namespace Web3\RequestManagers; - -class RequestManager -{ - /** - * host - * - * @var string - */ - protected $host; - - /** - * timeout - * - * @var float - */ - protected $timeout; - - /** - * construct - * - * @param string $host - * @param float $timeout - * @return void - */ - public function __construct($host, $timeout=1) - { - $this->host = $host; - $this->timeout = (float) $timeout; - } - - /** - * get - * - * @param string $name - * @return mixed - */ - public function __get($name) - { - $method = 'get' . ucfirst($name); - - if (method_exists($this, $method)) { - return call_user_func_array([$this, $method], []); - } - return false; - } - - /** - * set - * - * @param string $name - * @param mixed $value - * @return bool - */ - public function __set($name, $value) - { - $method = 'set' . ucfirst($name); - - if (method_exists($this, $method)) { - return call_user_func_array([$this, $method], [$value]); - } - return false; - } - - /** - * getHost - * - * @return string - */ - public function getHost() - { - return $this->host; - } - - /** - * getTimeout - * - * @return float - */ - public function getTimeout() - { - return $this->timeout; - } -} \ No newline at end of file diff --git a/src/Shh.php b/src/Shh.php index ed38738b..bea6e238 100644 --- a/src/Shh.php +++ b/src/Shh.php @@ -13,8 +13,7 @@ use Web3\Providers\Provider; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; +use Web3\Providers\WsProvider; class Shh { @@ -46,16 +45,17 @@ class Shh * construct * * @param string|\Web3\Providers\Provider $provider + * @param float $timeout * @return void */ - public function __construct($provider) + public function __construct($provider, $timeout = 1) { if (is_string($provider) && (filter_var($provider, FILTER_VALIDATE_URL) !== false)) { // check the uri schema if (preg_match('/^https?:\/\//', $provider) === 1) { - $requestManager = new HttpRequestManager($provider); - - $this->provider = new HttpProvider($requestManager); + $this->provider = new HttpProvider($provider, $timeout); + } else if (preg_match('/^wss?:\/\//', $provider) === 1) { + $this->provider = new WsProvider($provider, $timeout); } } else if ($provider instanceof Provider) { $this->provider = $provider; @@ -75,7 +75,7 @@ public function __call($name, $arguments) throw new \RuntimeException('Please set provider first.'); } - $class = explode('\\', get_class()); + $class = explode('\\', get_class($this)); if (preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { $method = strtolower($class[1]) . '_' . $name; diff --git a/src/Web3.php b/src/Web3.php index 4e489f77..90ed2649 100644 --- a/src/Web3.php +++ b/src/Web3.php @@ -18,8 +18,7 @@ use Web3\Utils; use Web3\Providers\Provider; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; +use Web3\Providers\WsProvider; class Web3 { @@ -85,16 +84,17 @@ class Web3 * construct * * @param string|\Web3\Providers\Provider $provider + * @param float $timeout * @return void */ - public function __construct($provider) + public function __construct($provider, $timeout = 1) { if (is_string($provider) && (filter_var($provider, FILTER_VALIDATE_URL) !== false)) { // check the uri schema if (preg_match('/^https?:\/\//', $provider) === 1) { - $requestManager = new HttpRequestManager($provider); - - $this->provider = new HttpProvider($requestManager); + $this->provider = new HttpProvider($provider, $timeout); + } else if (preg_match('/^wss?:\/\//', $provider) === 1) { + $this->provider = new WsProvider($provider, $timeout); } } else if ($provider instanceof Provider) { $this->provider = $provider; @@ -114,7 +114,7 @@ public function __call($name, $arguments) throw new \RuntimeException('Please set provider first.'); } - $class = explode('\\', get_class()); + $class = explode('\\', get_class($this)); if (preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { $method = strtolower($class[1]) . '_' . $name; diff --git a/test/TestCase.php b/test/TestCase.php index 18d298e6..46eac37a 100644 --- a/test/TestCase.php +++ b/test/TestCase.php @@ -4,7 +4,7 @@ use \PHPUnit\Framework\TestCase as BaseTestCase; use Web3\Web3; -use Web3\RequestManagers\HttpAsyncRequestManager; +use Web3\Providers\HttpAsyncProvider; use Web3\Providers\HttpProvider; class TestCase extends BaseTestCase @@ -30,6 +30,13 @@ class TestCase extends BaseTestCase */ protected $testHost = 'http://localhost:8545'; + /** + * testWsHost + * + * @var string + */ + protected $testWsHost = 'ws://localhost:8545'; + /** * coinbase * @@ -40,10 +47,17 @@ class TestCase extends BaseTestCase /** * asyncHttpProvider * - * @var \Web3\Providers\HttpProvider + * @var \Web3\Providers\HttpAsyncProvider */ protected $asyncHttpProvider; + /** + * EMPTY_ADDRESS + * + * @var string + */ + protected $EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + /** * setUp */ @@ -52,15 +66,23 @@ public function setUp(): void $web3 = new Web3($this->testHost); $this->web3 = $web3; - $asyncRequestManager = new HttpAsyncRequestManager($this->testHost); - $asyncHttpProvider = new HttpProvider($asyncRequestManager); + $asyncHttpProvider = new HttpAsyncProvider($this->testHost); $this->asyncHttpProvider = $asyncHttpProvider; - $web3->eth->coinbase(function ($err, $coinbase) { + $web3->eth->coinbase(function ($err, $coinbase) use ($web3) { if ($err !== null) { return $this->fail($err->getMessage()); } + // if ($coinbase === $this->EMPTY_ADDRESS) { + // $web3->eth->accounts(function ($err, $accounts) { + // if ($err !== null) { + // return $this->fail($err->getMessage()); + // } + // $this->coinbase = $accounts[rand(0, count($accounts) - 1)]; + // }); + // } else { $this->coinbase = $coinbase; + // } }); } diff --git a/test/unit/ContractTest.php b/test/unit/ContractTest.php index 3d72bde4..d0234169 100644 --- a/test/unit/ContractTest.php +++ b/test/unit/ContractTest.php @@ -4,8 +4,6 @@ use Test\TestCase; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; use Web3\Contract; use Web3\Utils; use Web3\Contracts\Ethabi; @@ -445,7 +443,6 @@ public function testInstance() $contract = new Contract($this->testHost, $this->testAbi); $this->assertTrue($contract->provider instanceof HttpProvider); - $this->assertTrue($contract->provider->requestManager instanceof RequestManager); } /** @@ -456,14 +453,13 @@ public function testInstance() public function testSetProvider() { $contract = $this->contract; - $requestManager = new HttpRequestManager('http://localhost:8545'); - $contract->provider = new HttpProvider($requestManager); + $contract->provider = new HttpProvider('http://localhost:8545'); - $this->assertEquals($contract->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($contract->provider->host, 'http://localhost:8545'); $contract->provider = null; - $this->assertEquals($contract->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($contract->provider->host, 'http://localhost:8545'); } /** diff --git a/test/unit/EthTest.php b/test/unit/EthTest.php index 702bd6b8..c8ed449e 100644 --- a/test/unit/EthTest.php +++ b/test/unit/EthTest.php @@ -5,8 +5,6 @@ use RuntimeException; use Test\TestCase; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; use Web3\Eth; class EthTest extends TestCase @@ -40,7 +38,6 @@ public function testInstance() $eth = new Eth($this->testHost); $this->assertTrue($eth->provider instanceof HttpProvider); - $this->assertTrue($eth->provider->requestManager instanceof RequestManager); } /** @@ -51,14 +48,13 @@ public function testInstance() public function testSetProvider() { $eth = $this->eth; - $requestManager = new HttpRequestManager('http://localhost:8545'); - $eth->provider = new HttpProvider($requestManager); + $eth->provider = new HttpProvider('http://localhost:8545'); - $this->assertEquals($eth->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($eth->provider->host, 'http://localhost:8545'); $eth->provider = null; - $this->assertEquals($eth->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($eth->provider->host, 'http://localhost:8545'); } /** diff --git a/test/unit/HttpProviderTest.php b/test/unit/HttpProviderTest.php index 57bf0605..03b6d775 100644 --- a/test/unit/HttpProviderTest.php +++ b/test/unit/HttpProviderTest.php @@ -4,8 +4,7 @@ use RuntimeException; use Test\TestCase; -use Web3\RequestManagers\HttpRequestManager; -use Web3\RequestManagers\HttpAsyncRequestManager; +use Web3\Providers\HttpAsyncProvider; use Web3\Providers\HttpProvider; use Web3\Methods\Web3\ClientVersion; @@ -18,8 +17,7 @@ class HttpProviderTest extends TestCase */ public function testSend() { - $requestManager = new HttpRequestManager($this->testHost); - $provider = new HttpProvider($requestManager); + $provider = new HttpProvider($this->testHost); $method = new ClientVersion('web3_clientVersion', []); $provider->send($method, function ($err, $version) { @@ -37,8 +35,7 @@ public function testSend() */ public function testBatch() { - $requestManager = new HttpRequestManager($this->testHost); - $provider = new HttpProvider($requestManager); + $provider = new HttpProvider($this->testHost); $method = new ClientVersion('web3_clientVersion', []); $callback = function ($err, $data) { if ($err !== null) { @@ -66,8 +63,7 @@ public function testBatch() */ public function testSendAsync() { - $requestManager = new HttpAsyncRequestManager($this->testHost); - $provider = new HttpProvider($requestManager); + $provider = new HttpAsyncProvider($this->testHost); $method = new ClientVersion('web3_clientVersion', []); // \React\Async\await($provider->send($method, function ($err, $version) { @@ -108,8 +104,7 @@ function () use ($c) { return $c; } */ public function testBatchAsync() { - $requestManager = new HttpAsyncRequestManager($this->testHost); - $provider = new HttpProvider($requestManager); + $provider = new HttpAsyncProvider($this->testHost); $method = new ClientVersion('web3_clientVersion', []); $callback = function ($err, $data) { if ($err !== null) { diff --git a/test/unit/NetTest.php b/test/unit/NetTest.php index ef5ef33e..24d5a14f 100644 --- a/test/unit/NetTest.php +++ b/test/unit/NetTest.php @@ -5,8 +5,6 @@ use RuntimeException; use Test\TestCase; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; use Web3\Net; class NetTest extends TestCase @@ -40,7 +38,6 @@ public function testInstance() $net = new Net($this->testHost); $this->assertTrue($net->provider instanceof HttpProvider); - $this->assertTrue($net->provider->requestManager instanceof RequestManager); } /** @@ -51,14 +48,13 @@ public function testInstance() public function testSetProvider() { $net = $this->net; - $requestManager = new HttpRequestManager('http://localhost:8545'); - $net->provider = new HttpProvider($requestManager); + $net->provider = new HttpProvider('http://localhost:8545'); - $this->assertEquals($net->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($net->provider->host, 'http://localhost:8545'); $net->provider = null; - $this->assertEquals($net->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($net->provider->host, 'http://localhost:8545'); } /** diff --git a/test/unit/PersonalApiTest.php b/test/unit/PersonalApiTest.php index 96853a28..61a9adbd 100644 --- a/test/unit/PersonalApiTest.php +++ b/test/unit/PersonalApiTest.php @@ -172,30 +172,34 @@ public function testSendTransaction() $this->assertTrue(is_string($account)); }); - $this->web3->eth->sendTransaction([ - 'from' => $this->coinbase, - 'to' => $this->newAccount, - 'value' => '0xfffffffffffff', - ], function ($err, $transaction) { - if ($err !== null) { - return $this->fail($err->getMessage()); - } - $this->assertTrue(is_string($transaction)); - $this->assertTrue(mb_strlen($transaction) === 66); + $this->web3->eth->accounts(function ($err, $accounts) use ($personal) { + $this->web3->eth->sendTransaction([ + 'from' => $accounts[0], + 'to' => $this->newAccount, + 'value' => '0xfffffffffffff', + ], function ($err, $transaction) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($transaction)); + $this->assertTrue(mb_strlen($transaction) === 66); + }); }); - $personal->sendTransaction([ - 'from' => $this->newAccount, - 'to' => $this->coinbase, - 'value' => '0x01', - 'gasLimit' => 21000, - 'gasPrice' => 5000000000, - ], '123456', function ($err, $transaction) { - if ($err !== null) { - return $this->fail($err->getMessage()); - } - $this->assertTrue(is_string($transaction)); - $this->assertTrue(mb_strlen($transaction) === 66); + $this->web3->eth->accounts(function ($err, $accounts) use ($personal) { + $personal->sendTransaction([ + 'from' => $this->newAccount, + 'to' => $accounts[0], + 'value' => '0x01', + 'gasLimit' => 21000, + 'gasPrice' => 5000000000, + ], '123456', function ($err, $transaction) { + if ($err !== null) { + return $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($transaction)); + $this->assertTrue(mb_strlen($transaction) === 66); + }); }); } diff --git a/test/unit/PersonalTest.php b/test/unit/PersonalTest.php index b5bab279..4f48a0cc 100644 --- a/test/unit/PersonalTest.php +++ b/test/unit/PersonalTest.php @@ -5,8 +5,6 @@ use RuntimeException; use Test\TestCase; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; use Web3\Personal; class PersonalTest extends TestCase @@ -40,7 +38,6 @@ public function testInstance() $personal = new Personal($this->testHost); $this->assertTrue($personal->provider instanceof HttpProvider); - $this->assertTrue($personal->provider->requestManager instanceof RequestManager); } /** @@ -51,14 +48,13 @@ public function testInstance() public function testSetProvider() { $personal = $this->personal; - $requestManager = new HttpRequestManager('http://localhost:8545'); - $personal->provider = new HttpProvider($requestManager); + $personal->provider = new HttpProvider('http://localhost:8545'); - $this->assertEquals($personal->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($personal->provider->host, 'http://localhost:8545'); $personal->provider = null; - $this->assertEquals($personal->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($personal->provider->host, 'http://localhost:8545'); } /** diff --git a/test/unit/ProviderTest.php b/test/unit/ProviderTest.php index 32af5176..f9718e88 100644 --- a/test/unit/ProviderTest.php +++ b/test/unit/ProviderTest.php @@ -3,27 +3,20 @@ namespace Test\Unit; use Test\TestCase; -use Web3\RequestManagers\RequestManager; use Web3\Providers\Provider; +use Web3\Providers\HttpProvider; class ProviderTest extends TestCase { /** - * testSetRequestManager + * testNewProvider * * @return void */ - public function testSetRequestManager() + public function testNewProvider() { - $requestManager = new RequestManager('http://localhost:8545'); - $provider = new Provider($requestManager); + $provider = new HttpProvider('http://localhost:8545'); - $this->assertEquals($provider->requestManager->host, 'http://localhost:8545'); - - $requestManager = new RequestManager($this->testHost2); - $provider->requestManager = $requestManager; - - // there is no setter for request manager - $this->assertEquals($provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($provider->host, 'http://localhost:8545'); } } \ No newline at end of file diff --git a/test/unit/RequestManagerTest.php b/test/unit/RequestManagerTest.php deleted file mode 100644 index 61dabdb2..00000000 --- a/test/unit/RequestManagerTest.php +++ /dev/null @@ -1,27 +0,0 @@ -assertEquals($requestManager->host, 'http://localhost:8545'); - $this->assertEquals($requestManager->timeout, 0.1); - - // there is no setter for host and timeout - $requestManager->host = $this->testHost2; - $requestManager->timeout = 1; - $this->assertEquals($requestManager->host, 'http://localhost:8545'); - $this->assertEquals($requestManager->timeout, 0.1); - } -} \ No newline at end of file diff --git a/test/unit/ShhTest.php b/test/unit/ShhTest.php index bcaeb8a0..61c5e88f 100644 --- a/test/unit/ShhTest.php +++ b/test/unit/ShhTest.php @@ -5,8 +5,6 @@ use RuntimeException; use Test\TestCase; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; use Web3\Shh; class ShhTest extends TestCase @@ -40,7 +38,6 @@ public function testInstance() $shh = new Shh($this->testHost); $this->assertTrue($shh->provider instanceof HttpProvider); - $this->assertTrue($shh->provider->requestManager instanceof RequestManager); } /** @@ -51,14 +48,13 @@ public function testInstance() public function testSetProvider() { $shh = $this->shh; - $requestManager = new HttpRequestManager('http://localhost:8545'); - $shh->provider = new HttpProvider($requestManager); + $shh->provider = new HttpProvider('http://localhost:8545'); - $this->assertEquals($shh->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($shh->provider->host, 'http://localhost:8545'); $shh->provider = null; - $this->assertEquals($shh->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($shh->provider->host, 'http://localhost:8545'); } /** diff --git a/test/unit/Web3Test.php b/test/unit/Web3Test.php index db3b0b9a..7e26e6f4 100644 --- a/test/unit/Web3Test.php +++ b/test/unit/Web3Test.php @@ -11,8 +11,6 @@ use Web3\Shh; use Web3\Utils; use Web3\Providers\HttpProvider; -use Web3\RequestManagers\RequestManager; -use Web3\RequestManagers\HttpRequestManager; class Web3Test extends TestCase { @@ -49,11 +47,9 @@ public function setUp(): void */ public function testInstance() { - $requestManager = new HttpRequestManager('http://localhost:8545'); - $web3 = new Web3(new HttpProvider($requestManager)); + $web3 = new Web3(new HttpProvider('http://localhost:8545')); $this->assertTrue($web3->provider instanceof HttpProvider); - $this->assertTrue($web3->provider->requestManager instanceof RequestManager); $this->assertTrue($web3->eth instanceof Eth); $this->assertTrue($web3->net instanceof Net); $this->assertTrue($web3->personal instanceof Personal); @@ -69,13 +65,12 @@ public function testInstance() public function testSetProvider() { $web3 = $this->web3; - $requestManager = new HttpRequestManager('http://localhost:8545'); - $web3->provider = new HttpProvider($requestManager); + $web3->provider = new HttpProvider('http://localhost:8545'); - $this->assertEquals($web3->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($web3->provider->host, 'http://localhost:8545'); $web3->provider = null; - $this->assertEquals($web3->provider->requestManager->host, 'http://localhost:8545'); + $this->assertEquals($web3->provider->host, 'http://localhost:8545'); } /** diff --git a/test/unit/WsProviderTest.php b/test/unit/WsProviderTest.php new file mode 100644 index 00000000..598e0746 --- /dev/null +++ b/test/unit/WsProviderTest.php @@ -0,0 +1,84 @@ +testWsHost); + $method = new ClientVersion('web3_clientVersion', []); + + // \React\Async\await($provider->send($method, function ($err, $version) { + // if ($err !== null) { + // $this->fail($err->getMessage()); + // } + // $this->assertTrue(is_string($version)); + // })); + $a = $provider->send($method, function ($err, $version) { + if ($err !== null) { + $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($version)); + }); + $b = $provider->send($method, function ($err, $version) { + if ($err !== null) { + $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($version)); + }); + $c = $provider->send($method, function ($err, $version) { + if ($err !== null) { + $this->fail($err->getMessage()); + } + $this->assertTrue(is_string($version)); + }); + \React\Async\await(\React\Async\parallel([ + function () use ($a) { return $a; }, + function () use ($b) { return $b; }, + function () use ($c) { return $c; } + ])); + // close connection + $provider->close(); + } + + /** + * testBatchAsync + * + * @return void + */ + public function testBatchAsync() + { + $provider = new WsProvider($this->testWsHost); + $method = new ClientVersion('web3_clientVersion', []); + $callback = function ($err, $data) { + if ($err !== null) { + $this->fail($err->getMessage()); + } + $this->assertEquals($data[0], $data[1]); + }; + + try { + \React\Async\await($provider->execute($callback)); + } catch (RuntimeException $err) { + $this->assertTrue($err->getMessage() !== true); + } + + $provider->batch(true); + $provider->send($method, null); + $provider->send($method, null); + \React\Async\await($provider->execute($callback)); + // close connection + $provider->close(); + } +} \ No newline at end of file