From eca04e1c913f38e5e594f24f5fcddbcd1e3ff833 Mon Sep 17 00:00:00 2001 From: sc0Vu Date: Tue, 12 Dec 2017 11:52:52 +0800 Subject: [PATCH] Web3 Web3 api: * clientVersion * sha3 Basic object: * HttpProvider * HttpRequestManager --- .gitignore | 3 +- composer.json | 29 ++++ phpunit.xml | 22 +++ src/Eth.php | 102 +++++++++++++ src/Providers/HttpProvider.php | 91 ++++++++++++ src/Providers/IProvider.php | 32 ++++ src/Providers/Provider.php | 106 ++++++++++++++ src/RequestManagers/HttpRequestManager.php | 79 ++++++++++ src/RequestManagers/IRequestManager.php | 15 ++ src/RequestManagers/RequestManager.php | 67 +++++++++ src/Web3.php | 161 +++++++++++++++++++++ test/TestCase.php | 22 +++ test/unit/ProviderTest.php | 13 ++ test/unit/Web3Test.php | 106 ++++++++++++++ 14 files changed, 847 insertions(+), 1 deletion(-) create mode 100644 composer.json create mode 100644 phpunit.xml create mode 100644 src/Eth.php create mode 100644 src/Providers/HttpProvider.php create mode 100644 src/Providers/IProvider.php create mode 100644 src/Providers/Provider.php create mode 100644 src/RequestManagers/HttpRequestManager.php create mode 100644 src/RequestManagers/IRequestManager.php create mode 100644 src/RequestManagers/RequestManager.php create mode 100644 src/Web3.php create mode 100644 test/TestCase.php create mode 100644 test/unit/ProviderTest.php create mode 100644 test/unit/Web3Test.php diff --git a/.gitignore b/.gitignore index c422267..efefe03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ composer.phar /vendor/ +.phpintel/ # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file -# composer.lock +composer.lock diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8de30aa --- /dev/null +++ b/composer.json @@ -0,0 +1,29 @@ +{ + "name": "sc0vu/web3.php", + "description": "Ethereum web3 interface.", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "sc0Vu", + "email": "alk03073135@gmail.com" + } + ], + "minimum-stability": "dev", + "require": { + "guzzlehttp/guzzle": "~6.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0@dev" + }, + "autoload": { + "psr-4": { + "Web3\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Test\\": "test/" + } + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..46cd23a --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,22 @@ + + + + + ./test/unit + + + + + ./app + + + \ No newline at end of file diff --git a/src/Eth.php b/src/Eth.php new file mode 100644 index 0000000..0ba5e99 --- /dev/null +++ b/src/Eth.php @@ -0,0 +1,102 @@ +provider = $provider; + } + } else if ($provider instanceof Provider) { + $this->provider = $provider; + } + } + + /** + * call + * + * @param string $name + * @param array $arguments + * @return void + */ + public function __call($name, $arguments) + { + // + } + + /** + * 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], []); + } + } + + /** + * 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; + } + + /** + * getProvider + * + * @return void + */ + public function getProvider() + { + return $this->provider; + } + + /** + * setProvider + * + * @param $provider + * @return bool + */ + public function setProvider($provider) + { + if ($provider instanceof Provider) { + $this->provider = $provider; + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/Providers/HttpProvider.php b/src/Providers/HttpProvider.php new file mode 100644 index 0000000..9ff1881 --- /dev/null +++ b/src/Providers/HttpProvider.php @@ -0,0 +1,91 @@ +createRpc($method, $arguments); + + if (!$this->isBatch) { + $this->requestManager->sendPayload(json_encode($rpc), $callback); + } else { + $this->batch[] = json_encode($rpc); + } + } + + /** + * 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.'); + } + $this->requestManager->sendPayload('[' . implode(',', $this->batch) . ']', $callback); + $this->batch = []; + } + + /** + * createRpc + * + * @param string $rpc + * @param array $arguments + * @return array + */ + protected function createRpc($rpc, $arguments) + { + $this->id += 1; + + $rpc = [ + 'id' => $this->id, + 'jsonrpc' => $this->rpcVersion, + 'method' => $rpc + ]; + + if (count($arguments) > 0) { + $rpc['params'] = $arguments; + } + return $rpc; + } +} \ No newline at end of file diff --git a/src/Providers/IProvider.php b/src/Providers/IProvider.php new file mode 100644 index 0000000..a5f19ed --- /dev/null +++ b/src/Providers/IProvider.php @@ -0,0 +1,32 @@ +requestManager = $requestManager; + } + + /** + * 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], []); + } + } + + /** + * 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; + } + + /** + * getRequestManager + * + * @return \Web3\RequestManagers\RequestManager + */ + public function getRequestManager() + { + return $this->requestManager; + } + + /** + * getIsBatch + * + * @return bool + */ + public function getIsBatch() + { + return $this->isBatch; + } +} \ No newline at end of file diff --git a/src/RequestManagers/HttpRequestManager.php b/src/RequestManagers/HttpRequestManager.php new file mode 100644 index 0000000..58028a9 --- /dev/null +++ b/src/RequestManagers/HttpRequestManager.php @@ -0,0 +1,79 @@ +client = new Client; + } + + /** + * sendPayload + * + * @param string $payload + * @param callable $callback + * @return void + */ + public function sendPayload($payload, $callback) + { + if (!is_string($payload)) { + throw new \RuntimeException('Payload must be string.'); + } + // $promise = $this->client->postAsync($this->host, [ + // 'headers' => [ + // 'content-type' => 'application/json' + // ], + // 'body' => $payload + // ]); + // $promise->then( + // function (ResponseInterface $res) use ($callback) { + // var_dump($res->body()); + // call_user_func($callback, null, $res); + // }, + // function (RequestException $err) use ($callback) { + // var_dump($err->getMessage()); + // call_user_func($callback, $err, null); + // } + // ); + try { + $res = $this->client->post($this->host, [ + 'headers' => [ + 'content-type' => 'application/json' + ], + 'body' => $payload + ]); + $json = json_decode($res->getBody()); + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new \InvalidArgumentException( + 'json_decode error: ' . json_last_error_msg()); + } + + call_user_func($callback, null, $json); + } catch (RequestException $err) { + call_user_func($callback, $err, null); + } + } +} \ No newline at end of file diff --git a/src/RequestManagers/IRequestManager.php b/src/RequestManagers/IRequestManager.php new file mode 100644 index 0000000..1164bc2 --- /dev/null +++ b/src/RequestManagers/IRequestManager.php @@ -0,0 +1,15 @@ +host = $host; + + } + + /** + * 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], []); + } + } + + /** + * 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 void + */ + public function getHost() + { + return $this->host; + } +} \ No newline at end of file diff --git a/src/Web3.php b/src/Web3.php new file mode 100644 index 0000000..7aab499 --- /dev/null +++ b/src/Web3.php @@ -0,0 +1,161 @@ +provider = new HttpProvider($requestManeger); + } + } else if ($provider instanceof Provider) { + $this->provider = $provider; + } + } + + /** + * call + * + * @param string $name + * @param array $arguments + * @return void + */ + public function __call($name, $arguments) + { + if (empty($this->provider)) { + return; + } + + $class = explode('\\', get_class()); + + if (strtolower($class[0]) === 'web3' && preg_match('/^[a-zA-Z0-9]+$/', $name) === 1) { + $method = strtolower($class[1]) . '_' . $name; + + if ($this->provider->isBatch) { + $this->provider->send($method, $arguments, null); + } else { + $callback = array_pop($arguments); + + if (is_callable($callback) !== true) { + throw new \RuntimeException('The last param must be callback function.'); + } + $this->provider->send($method, $arguments, $callback); + } + } + } + + /** + * 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], []); + } + } + + /** + * 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; + } + + /** + * getProvider + * + * @return void + */ + public function getProvider() + { + return $this->provider; + } + + /** + * setProvider + * + * @param $provider + * @return bool + */ + public function setProvider($provider) + { + if ($provider instanceof Provider) { + $this->provider = $provider; + return true; + } + return false; + } + + /** + * getEth + * + * @return void + */ + public function getEth() + { + if (!isset($this->eth)) { + $eth = new Eth($this->provider); + $this->eth = $eth; + } + return $this->eth; + } + + /** + * batch + * + * @param bool $status + * @return void + */ + public function batch($status) + { + $status = is_bool($status); + + $this->provider->batch($status); + } +} \ No newline at end of file diff --git a/test/TestCase.php b/test/TestCase.php new file mode 100644 index 0000000..4a2fa81 --- /dev/null +++ b/test/TestCase.php @@ -0,0 +1,22 @@ +assertTrue(true); + } +} \ No newline at end of file diff --git a/test/unit/Web3Test.php b/test/unit/Web3Test.php new file mode 100644 index 0000000..d3902e8 --- /dev/null +++ b/test/unit/Web3Test.php @@ -0,0 +1,106 @@ +web3 = $web3; + } + + /** + * testInstance + * + * @return void + */ + public function testInstance() + { + $web3 = $this->web3; + + $this->assertTrue($web3->provider instanceof HttpProvider); + $this->assertTrue($web3->provider->requestManager instanceof RequestManager); + $this->assertTrue($web3->eth instanceof Eth); + } + + /** + * testSend + * + * @return void + */ + public function testSend() + { + $web3 = $this->web3; + + $web3->clientVersion(function ($err, $version) { + if ($err !== null) { + return $this->markTestIncomplete($err->getMessage()); + } + $this->assertTrue(is_string($version->result)); + }); + + $web3->sha3($this->testHex, function ($err, $hash) { + if ($err !== null) { + return $this->markTestIncomplete($err->getMessage()); + } + $this->assertEquals($hash->result, $this->testHash); + }); + } + + /** + * testBatch + * + * @return void + */ + public function testBatch() + { + $web3 = $this->web3; + + $web3->batch(true); + $web3->clientVersion(); + $web3->sha3($this->testHex); + + $web3->provider->execute(function ($err, $data) { + if ($err !== null) { + return $this->markTestIncomplete($err->getMessage()); + } + $this->assertTrue(is_string($data[0]->result)); + $this->assertEquals($data[1]->result, $this->testHash); + }); + } +} \ No newline at end of file