Completed
Pull Request — master (#2)
by Pol
12:05
created

RandomOrgAPI   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 204
Duplicated Lines 3.92 %

Coupling/Cohesion

Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 21
c 0
b 0
f 0
cbo 4
dl 8
loc 204
ccs 38
cts 38
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getEndPoint() 0 4 1
A getHttpClient() 0 4 1
A __construct() 0 5 1
A withApiKey() 0 11 1
A withEndPoint() 0 7 1
A withHttpClient() 0 7 1
A getApiKey() 0 7 1
A call() 0 13 1
A get() 0 10 1
A getData() 0 14 4
A getConfiguration() 0 4 1
C validateResponse() 8 40 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace drupol\Yaroc;
4
5
use drupol\Yaroc\Plugin\MethodPluginInterface;
6
use Http\Client\HttpClient;
7
use Http\Discovery\HttpClientDiscovery;
8
use Psr\Http\Message\ResponseInterface;
9
10
/**
11
 * Class RandomOrgAPI.
12
 */
13
class RandomOrgAPI implements RandomOrgAPIInterface
14
{
15
    /**
16
     * The default Random.org endpoint template.
17
     *
18
     * @var string;
19
     */
20
    private $endpoint = 'https://api.random.org/json-rpc/1/invoke';
21
22
    /**
23
     * The configuration.
24
     *
25
     * @var array
26
     */
27
    private $configuration;
28
29
    /**
30
     * The HTTP client.
31
     *
32
     * @var \Http\Client\HttpClient
33
     */
34
    private $httpClient;
35
36
    /**
37
     * RandomOrgAPI constructor.
38
     *
39
     * @param \Http\Client\HttpClient $httpClient
0 ignored issues
show
Documentation introduced by
Should the type for parameter $httpClient not be null|HttpClient?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
40
     * @param array $configuration
41
     */
42
    public function __construct(HttpClient $httpClient = null, array $configuration = [])
43
    {
44
        $this->httpClient = $httpClient ?? HttpClientDiscovery::find();
45
        $this->configuration = $configuration;
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    public function withApiKey(string $apikey)
52
    {
53
        $clone = clone $this;
54
55
        $configuration = $clone->getConfiguration();
56
        $configuration['apiKey'] = $apikey;
57
58
        $clone->configuration = $configuration;
59
60
        return $clone;
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function withEndPoint(string $endpoint)
67
    {
68
        $clone = clone $this;
69
        $clone->endpoint = $endpoint;
70
71
        return $clone;
72
    }
73 16
74 16
    /**
75 16
     * {@inheritdoc}
76 16
     */
77 16
    public function withHttpClient(HttpClient $client)
78
    {
79
        $clone = clone $this;
80
        $clone->httpClient = $client;
81
82
        return $clone;
83
    }
84
85
    /**
86
     * {@inheritdoc}
87 16
     */
88 16
    public function getEndPoint()
89
    {
90 16
        return $this->endpoint;
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function getApiKey()
97
    {
98
        $configuration = $this->getConfiguration()
99 13
            + ['apiKey' => ''];
100 13
101
        return $configuration['apiKey'];
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function call(MethodPluginInterface $methodPlugin)
108
    {
109
        $parameters = $methodPlugin->getParameters() +
110
            ['apiKey' => $this->getApiKey()];
111 16
112 16
        return $this->validateResponse(
113 16
            $methodPlugin
114
                ->withHttpClient($this->getHttpClient())
115 16
                ->withEndPoint($this->getEndPoint())
116
                ->withParameters($parameters)
117
                ->call()
118
        );
119
    }
120
121
    /**
122
     * {@inheritdoc}
123 16
     */
124 16
    public function get(MethodPluginInterface $methodPlugin)
125
    {
126
        return json_decode(
127
            (string) $this
0 ignored issues
show
Bug introduced by
The method getBody does only exist in Psr\Http\Message\ResponseInterface, but not in Exception.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
128
                ->call($methodPlugin)
129
                ->getBody()
130
                ->getContents(),
131
            true
132
        );
133
    }
134
135 2
    /**
136 2
     * {@inheritdoc}
137 2
     */
138
    public function getData(MethodPluginInterface $methodPlugin)
139 2
    {
140
        $data = $this->get($methodPlugin);
141
142
        if (!isset($data['result'])) {
143
            return false;
144
        }
145
146
        if (isset($data['result']['random']) && isset($data['result']['random']['data'])) {
147 17
            return $data['result']['random']['data'];
148 17
        }
149
150
        return false;
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156
    public function getConfiguration()
157
    {
158
        return $this->configuration;
159
    }
160
161
    /**
162
     * {@inheritdoc}
163 16
     */
164
    public function getHttpClient()
165 16
    {
166 16
        return $this->httpClient;
167
    }
168
169 16
    /**
170 16
     * Validate the response.
171
     *
172
     * @param \Psr\Http\Message\ResponseInterface $response
173
     *
174 16
     * @return \Exception|ResponseInterface
175 16
     */
176 16
    private function validateResponse(ResponseInterface $response)
177
    {
178 16
        if (200 == $response->getStatusCode()) {
179
            $body = json_decode((string) $response->getBody()->getContents(), true);
180
181
            if (isset($body['error']['code'])) {
182
                switch ($body['error']['code']) {
183 View Code Duplication
                    case -32600:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
184
                        throw new \InvalidArgumentException(
185
                            'Invalid Request: ' . $body['error']['message'],
186 16
                            $body['error']['code']
187 16
                        );
188
                    case -32601:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
189
                        throw new \BadFunctionCallException(
190
                            'Procedure not found: ' . $body['error']['message'],
191
                            $body['error']['code']
192
                        );
193 View Code Duplication
                    case -32602:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
194
                        throw new \InvalidArgumentException(
195
                            'Invalid arguments: ' . $body['error']['message'],
196
                            $body['error']['code']
197
                        );
198 13 View Code Duplication
                    case -32603:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
199 13
                        throw new \RuntimeException(
200
                            'Internal Error: ' . $body['error']['message'],
201 13
                            $body['error']['code']
202
                        );
203 View Code Duplication
                    default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
204
                        throw new \RuntimeException(
205
                            'Invalid request/response: ' . $body['error']['message'],
206
                            $body['error']['code']
207
                        );
208
                }
209 12
            }
210 12
        }
211
212
        $response->getBody()->rewind();
213
214
        return $response;
215
    }
216
}
217