Client::getConfig()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 4
nop 1
crap 3
1
<?php
2
3
namespace Bouhnosaure\Dogecoin;
4
5
use GuzzleHttp\Client as GuzzleHttp;
6
use GuzzleHttp\ClientInterface;
7
use GuzzleHttp\Exception\RequestException;
8
use GuzzleHttp\HandlerStack;
9
use GuzzleHttp\Middleware;
10
use Psr\Http\Message\ResponseInterface;
11
12
class Client
13
{
14
    /**
15
     * Http Client.
16
     *
17
     * @var \GuzzleHttp\Client
18
     */
19
    protected $client = null;
20
21
    /**
22
     * JSON-RPC Id.
23
     *
24
     * @var int
25
     */
26
    protected $rpcId = 0;
27
28
    /**
29
     * Constructs new client.
30
     *
31
     * @param mixed $config
32
     *
33
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
34
     */
35 78
    public function __construct($config = [])
36
    {
37
        // init defaults
38 78
        $config = $this->defaultConfig($this->parseUrl($config));
39
40 78
        $handlerStack = HandlerStack::create();
41 78
        $handlerStack->push(
42 78
            Middleware::mapResponse(function (ResponseInterface $response) {
43 30
                return DogecoindResponse::createFrom($response);
44 78
            }),
45 52
            'json_response'
46 26
        );
47
48
        // construct client
49 78
        $this->client = new GuzzleHttp([
50 26
            'base_uri' => "${config['scheme']}://${config['host']}:${config['port']}",
51
            'auth' => [
52 78
                $config['user'],
53 78
                $config['pass'],
54 26
            ],
55 78
            'verify' => isset($config['ca']) && is_file($config['ca']) ?
56 28
                $config['ca'] : true,
57 78
            'handler' => $handlerStack,
58 26
        ]);
59 78
    }
60
61
    /**
62
     * Gets http client config.
63
     *
64
     * @param string|null $option
65
     *
66
     * @return mixed
67
     */
68
    public function getConfig($option = null)
69
    {
70
        return (
71 66
            isset($this->client) &&
72 66
            $this->client instanceof ClientInterface
73 66
        ) ? $this->client->getConfig($option) : false;
74
    }
75
76
    /**
77
     * Gets http client.
78
     *
79
     * @return \GuzzleHttp\ClientInterface
80
     */
81
    public function getClient()
82
    {
83 3
        return $this->client;
84
    }
85
86
    /**
87
     * Sets http client.
88
     *
89
     * @param  \GuzzleHttp\ClientInterface
90
     *
91
     * @return Client
92
     */
93
    public function setClient(ClientInterface $client)
94
    {
95 45
        $this->client = $client;
0 ignored issues
show
Documentation Bug introduced by
$client is of type object<GuzzleHttp\ClientInterface>, but the property $client was declared to be of type object<GuzzleHttp\Client>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
96
97 45
        return $this;
98
    }
99
100
    /**
101
     * Makes request to Dogecoin Core.
102
     *
103
     * @param string $method
104
     * @param mixed $params
105
     *
106
     * @return array
107
     */
108
    public function request($method, $params = [])
109
    {
110
        try {
111
            $json = [
112 21
                'method' => strtolower($method),
113 21
                'params' => (array)$params,
114 21
                'id' => $this->rpcId++,
115 7
            ];
116
117 21
            $response = $this->client->request('POST', '/', ['json' => $json]);
118
119 9
            if ($response->hasError()) {
120
                // throw exception on error
121 3
                throw new Exceptions\DogecoindException($response->error());
122
            }
123
124 6
            return $response;
125 15
        } catch (RequestException $exception) {
126 12
            if ($exception->hasResponse() &&
127 11
                $exception->getResponse()->hasError()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Http\Message\ResponseInterface as the method hasError() does only exist in the following implementations of said interface: Bouhnosaure\Dogecoin\DogecoindResponse.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
128 4
            ) {
129 6
                throw new Exceptions\DogecoindException($exception->getResponse()->error());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Http\Message\ResponseInterface as the method error() does only exist in the following implementations of said interface: Bouhnosaure\Dogecoin\DogecoindResponse.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
130 1
            }
131
132 6
            throw new Exceptions\ClientException(
133 6
                $exception->getMessage(),
134 6
                $exception->getCode()
135 2
            );
136 3
        } catch (Exceptions\DogecoindException $exception) {
137 3
            throw $exception;
138
        }
139
    }
140
141
    /**
142
     * Makes async request to Dogecoin Core.
143
     *
144
     * @param string $method
145
     * @param mixed $params
146
     * @param callable|null $onFullfiled
147
     * @param callable|null $onRejected
148
     *
149
     * @return \GuzzleHttp\Promise\PromiseInterface
150
     */
151
    public function requestAsync(
152
        $method,
153
        $params = [],
154
        callable $onFullfiled = null,
155
        callable $onRejected = null
156
    ) {
157
    
158
159
        $json = [
160 21
            'method' => strtolower($method),
161 21
            'params' => (array)$params,
162 21
            'id' => $this->rpcId++,
163 7
        ];
164
165 21
        $promise = $this->client
166 21
            ->requestAsync('POST', '/', ['json' => $json]);
167
168 21
        $promise->then(
169
            function (ResponseInterface $response) use ($onFullfiled) {
170 9
                $error = null;
171 9
                if ($response->hasError()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Http\Message\ResponseInterface as the method hasError() does only exist in the following implementations of said interface: Bouhnosaure\Dogecoin\DogecoindResponse.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
172 3
                    $error = new Exceptions\DogecoindException($response->error());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Http\Message\ResponseInterface as the method error() does only exist in the following implementations of said interface: Bouhnosaure\Dogecoin\DogecoindResponse.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
173 1
                }
174
175 9
                if (is_callable($onFullfiled)) {
176 9
                    $onFullfiled($error ?: $response);
177 3
                }
178 21
            },
179
            function (RequestException $exception) use ($onRejected) {
180 12
                if ($exception->hasResponse() &&
181 11
                    $exception->getResponse()->hasError()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Http\Message\ResponseInterface as the method hasError() does only exist in the following implementations of said interface: Bouhnosaure\Dogecoin\DogecoindResponse.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
182 4
                ) {
183 6
                    $exception = new Exceptions\DogecoindException(
184 6
                        $exception->getResponse()->error()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Http\Message\ResponseInterface as the method error() does only exist in the following implementations of said interface: Bouhnosaure\Dogecoin\DogecoindResponse.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
185 2
                    );
186 2
                }
187
188 12
                if ($exception instanceof RequestException) {
189 6
                    $exception = new Exceptions\ClientException(
190 6
                        $exception->getMessage(),
191 6
                        $exception->getCode()
192 2
                    );
193 2
                }
194
195 12
                if (is_callable($onRejected)) {
196 12
                    $onRejected($exception);
197 4
                }
198 18
            }
199 7
        );
200
201 21
        return $promise;
202
    }
203
204
    /**
205
     * Makes request to Dogecoin Core.
206
     *
207
     * @param string $method
208
     * @param array $params
209
     *
210
     * @return array|\GuzzleHttp\Promise\PromiseInterface
211
     */
212
    public function __call($method, array $params = [])
213
    {
214 21
        $method = str_ireplace('async', '', $method, $count);
215 21
        if ($count > 0) {
216 3
            return $this->requestAsync($method, ...$params);
217
        }
218
219 18
        return $this->request($method, $params);
220
    }
221
222
    /**
223
     * Set default config values.
224
     *
225
     * @param array $config
226
     *
227
     * @return array
228
     */
229
    protected function defaultConfig(array $config = [])
230
    {
231
        $defaults = [
232 78
            'scheme' => 'http',
233 26
            'host' => '127.0.0.1',
234 26
            'port' => 22555,
235 26
            'user' => '',
236 26
            'pass' => '',
237 26
        ];
238
239 78
        return array_merge($defaults, $config);
240
    }
241
242
    /**
243
     * Expand URL config into components.
244
     *
245
     * @param mixed $config
246
     *
247
     * @return array
248
     */
249
    protected function parseUrl($config)
250
    {
251 78
        if (is_string($config)) {
252 24
            $allowed = ['scheme', 'host', 'port', 'user', 'pass'];
253
254 24
            $parts = (array)parse_url($config);
255 24
            $parts = array_intersect_key($parts, array_flip($allowed));
256
257 24
            if (!$parts || empty($parts)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
258 3
                throw new Exceptions\ClientException('Invalid url');
259
            }
260
261 21
            return $parts;
262
        }
263
264 78
        return $config;
265
    }
266
267
    /**
268
     * Converts amount from shibetoshi to dogecoin.
269
     *
270
     * @param int $amount
271
     *
272
     * @return float
273
     */
274
    public static function toDogecoin($amount)
275
    {
276 3
        return bcdiv((int)$amount, 1e8, 8);
277
    }
278
279
    /**
280
     * Converts amount from dogecoin to shibetoshi.
281
     *
282
     * @param float $amount
283
     *
284
     * @return int
285
     */
286
    public static function toShibetoshi($amount)
287
    {
288 3
        return bcmul(static::toFixed($amount, 8), 1e8);
289
    }
290
291
    /**
292
     * Converts amount from dogecoin to Koinu.
293
     *
294
     * @param float $amount
295
     *
296
     * @return int
297
     */
298
    public static function toKoinu($amount)
299
    {
300
        return self::toShibetoshi($amount);
301
    }
302
303
304
    /**
305
     * Brings number to fixed pricision without rounding.
306
     *
307
     * @param float $number
308
     * @param int $precision
309
     *
310
     * @return string
311
     */
312
    public static function toFixed($number, $precision = 8)
313
    {
314 6
        $number = $number * pow(10, $precision);
315
316 6
        return bcdiv($number, pow(10, $precision), $precision);
317
    }
318
}
319