GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Client::search()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 9
c 1
b 0
f 1
nc 1
nop 3
dl 0
loc 15
ccs 10
cts 10
cp 1
crap 2
rs 9.9666
1
<?php
2
3
namespace Coinpaprika;
4
5
use Coinpaprika\Exception\InvalidResponseException;
6
use Coinpaprika\Exception\RateLimitExceededException;
7
use Coinpaprika\Exception\ResponseErrorException;
8
use Coinpaprika\Http\Request;
9
use Coinpaprika\Model\Coin;
10
use Coinpaprika\Model\GlobalStats;
11
use Coinpaprika\Model\Ico;
12
use Coinpaprika\Model\Search;
13
use Coinpaprika\Model\Ticker;
14
use GuzzleHttp\Exception\ClientException;
15
use JMS\Serializer\SerializerBuilder;
16
use JMS\Serializer\Serializer;
17
use GuzzleHttp\Exception\GuzzleException;
18
use Psr\Http\Message\ResponseInterface;
19
20
/**
21
 * Class Client
22
 *
23
 * @package Coinpaprika
24
 *
25
 * @author Krzysztof Przybyszewski <[email protected]>
26
 */
27
class Client
28
{
29
    const BASE_URL = 'https://api.coinpaprika.com/%ver%/';
30
31
32
    /**
33
     * @var string
34
     */
35
    private $apiVersion;
36
37
    /**
38
     * @var \GuzzleHttp\Client
39
     */
40
    private $httpClient;
41
42
    /**
43
     * @var Serializer
44
     */
45
    private $serializer;
46
47
    /**
48
     * @var string
49
     */
50
    private $apiBaseUrl = 'https://api.coinpaprika.com';
51
52
    /**
53
     * Client constructor.
54
     *
55
     * @param  string|null  $cacheDir
56
     * @param  \GuzzleHttp\Client|null  $httpClient
57
     * @param  string|null  $apiBaseUrl
58
     */
59 13
    public function __construct(
60
        string $cacheDir = null,
61
        \GuzzleHttp\Client $httpClient = null,
62
        string $apiBaseUrl = null
63
    ) {
64 13
        $serializerBuilder = SerializerBuilder::create()
65 13
            ->addMetadataDir(__DIR__.'/Resource/config/serializer');
66
67 13
        if ($cacheDir !== null) {
68 1
            $serializerBuilder->setCacheDir($cacheDir);
69
        }
70
71 13
        $this->apiVersion = 'v1';
72 13
        $this->serializer = $serializerBuilder->build();
73
74 13
        if ($httpClient === null) {
75 1
            $httpClient = new \GuzzleHttp\Client();
76
        }
77
78 13
        $this->httpClient = $httpClient;
79
80 13
        if ($apiBaseUrl) {
81
            $this->apiBaseUrl = $apiBaseUrl;
82
        }
83 13
    }
84
85
    /**
86
     * Get global stats
87
     *
88
     * @throws  GuzzleException
89
     * @throws  ResponseErrorException
90
     * @throws  RateLimitExceededException
91
     * @throws  InvalidResponseException
92
     *
93
     * @return  GlobalStats
94
     */
95 1
    public function getGlobalStats(): GlobalStats
96
    {
97 1
        $response = $this->sendRequest(
98 1
            Request::METHOD_GET,
99 1
            $this->getEndpointUrl('global')
100
        );
101
102 1
        return $this->response($response, GlobalStats::class);
103
    }
104
105
    /**
106
     * Get tickers
107
     *
108
     * @throws  GuzzleException
109
     * @throws  ResponseErrorException
110
     * @throws  RateLimitExceededException
111
     * @throws  InvalidResponseException
112
     *
113
     * @return  array|Ticker[]
114
     */
115 1
    public function getTickers(): array
116
    {
117 1
        $response = $this->sendRequest(
118 1
            Request::METHOD_GET,
119 1
            $this->getEndpointUrl('ticker')
120
        );
121
122 1
        return $this->response($response, sprintf('array<%s>', Ticker::class));
123
    }
124
125
    /**
126
     * Get coin`s ticker data
127
     *
128
     * @param   string $id
129
     *
130
     * @throws  GuzzleException
131
     * @throws  ResponseErrorException
132
     * @throws  RateLimitExceededException
133
     * @throws  InvalidResponseException
134
     *
135
     * @return  Ticker
136
     */
137 4
    public function getTickerByCoinId(string $id): Ticker
138
    {
139 4
        $response = $this->sendRequest(
140 4
            Request::METHOD_GET,
141 4
            $this->getEndpointUrl(sprintf('ticker/%s', $id))
142
        );
143
144 4
        return $this->response($response, Ticker::class);
145
    }
146
147
    /**
148
     * Get coins list
149
     *
150
     * @throws  GuzzleException
151
     * @throws  ResponseErrorException
152
     * @throws  RateLimitExceededException
153
     * @throws  InvalidResponseException
154
     *
155
     * @return  array|Coin[]
156
     */
157 1
    public function getCoins(): array
158
    {
159 1
        $response = $this->sendRequest(
160 1
            Request::METHOD_GET,
161 1
            $this->getEndpointUrl('coins')
162
        );
163
164 1
        return $this->response($response, sprintf('array<%s>', Coin::class));
165
166
    }
167
168
    /**
169
     *
170
     * @param array $parameters API url query params array
171
     *
172
     * @throws \Coinpaprika\Exception\InvalidResponseException
173
     * @throws \Coinpaprika\Exception\RateLimitExceededException
174
     * @throws \Coinpaprika\Exception\ResponseErrorException
175
     * @throws \GuzzleHttp\Exception\GuzzleException
176
     *
177
     * @return array|Ico[]
178
     */
179 1
    public function getIcos(array $parameters = []): array
180
    {
181 1
        $response = $this->sendRequest(
182 1
            Request::METHOD_GET,
183 1
            $this->getEndpointUrl('icos'),
184 1
            $parameters
185
        );
186
187 1
        return $this->response($response, sprintf('array<%s>', Ico::class));
188
    }
189
190
    /**
191
     * @param   string     $query       Search query string
192
     * @param   array|null $categories  When null it defaults to all possible categories
193
     * @param   int        $limit       Per category limit
194
     *
195
     * @throws  InvalidResponseException
196
     * @throws  RateLimitExceededException
197
     * @throws  ResponseErrorException
198
     * @throws  GuzzleException
199
     *
200
     * @return Search
201
     */
202 4
    public function search(string $query, array $categories = null, int $limit = null): Search
203
    {
204 4
        $params = array_filter([
205 4
            'q' => $query,
206 4
            'c' => $categories ? implode(',', $categories) : null,
207 4
            'limit' => $limit
208
        ]);
209
210 4
        $response = $this->sendRequest(
211 4
            Request::METHOD_GET,
212 4
            $this->getEndpointUrl('search'),
213 4
            $params
214
        );
215
216 4
        return $this->response($response, Search::class);
217
    }
218
219
    /**
220
     * Get the API version
221
     *
222
     * @return  string
223
     */
224 13
    public function getApiVersion(): string
225
    {
226 13
        return $this->apiVersion;
227
    }
228
229
    /**
230
     * Get the endpoint URL.
231
     *
232
     * @param   string  $endpoint
233
     *
234
     * @return  string
235
     */
236 12
    protected function getEndpointUrl(string $endpoint): string
237
    {
238 12
        return str_replace('%ver%', $this->getApiVersion(), $this->apiBaseUrl.'/%ver%/').$endpoint;
239
    }
240
241
    /**
242
     * @param   string $method
243
     * @param   string $url
244
     * @param   array  $params
245
     * @param   array  $headers
246
     *
247
     * @return  ResponseInterface
248
     * @throws \GuzzleHttp\Exception\GuzzleException
249
     */
250 12
    protected function sendRequest(
251
        string $method,
252
        string $url,
253
        array $params = [],
254
        array $headers = []
255
    ): ResponseInterface {
256
257
        $defaultHeaders = [
258 12
            'User-Agent' => 'Coinpaprika API Client - PHP'
259
        ];
260
261 12
        if (Request::METHOD_GET === $method) {
262 12
            $params = http_build_query($params);
263
264 12
            $url = sprintf('%s?%s', $url, $params);
265
        }
266
267
        try {
268
269 12
            return $this->httpClient->request($method, $url, [
270 12
                'headers' => array_merge($defaultHeaders, $headers)
271
            ]);
272
273 4
        } catch (ClientException $e) {
274 4
            return $e->getResponse();
275
        }
276
    }
277
278
    /**
279
     * @param   ResponseInterface   $response
280
     *
281
     * @throws  InvalidResponseException
282
     * @throws  RateLimitExceededException
283
     * @throws  ResponseErrorException
284
     */
285 12
    protected function validateResponse(ResponseInterface $response): void
286
    {
287 12
        $statusCode = $response->getStatusCode();
288
289
        // rate limit exceeded
290 12
        if ($statusCode === 429) {
291 1
            throw new RateLimitExceededException('Response code from API 429. Rate limit exceeded.');
292
        }
293
294
        // check for errors
295 11
        if ($statusCode >= 400 && $statusCode <= 500) {
296
297 3
            if (array_key_exists('error', $e = json_decode($response->getBody(), true))) {
298
299 2
                throw new ResponseErrorException(sprintf(
300 2
                    'Response code: %s, error: %s',
301 2
                    $statusCode,
302 2
                    $e['error']
303
                ));
304
            }
305
306 1
            throw new InvalidResponseException(sprintf(
307 1
                'Bad response from a server - status code: %s, but error field does not exists.',
308 1
                $statusCode
309
            ));
310
        }
311 8
    }
312
313
    /**
314
     * Unmarshal JSON
315
     *
316
     * @param   ResponseInterface $response
317
     * @param   string            $type
318
     *
319
     * @throws  ResponseErrorException
320
     * @throws  RateLimitExceededException
321
     * @throws  InvalidResponseException
322
     *
323
     * @see     https://api.coinpaprika.com/#section/Errors
324
     *
325
     * @return  mixed
326
     */
327 12
    protected function response(ResponseInterface $response, string $type)
328
    {
329 12
        $this->validateResponse($response);
330
331 8
        return $this->serializer->deserialize($response->getBody(), $type, 'json');
332
    }
333
}
334