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.

AbstractList::getResponses()   B
last analyzed

Complexity

Conditions 8
Paths 16

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 26
cts 26
cp 1
rs 8.1315
c 0
b 0
f 0
cc 8
nc 16
nop 0
crap 8
1
<?php
2
3
namespace SimaLand\API;
4
5
use GuzzleHttp\Psr7\Response;
6
use SimaLand\API\Rest\Client;
7
use SimaLand\API\Rest\Request;
8
use SimaLand\API\Rest\ResponseException;
9
use GuzzleHttp\Exception\RequestException;
10
11
/**
12
 * Абстрактный класс для загрузки данных сущности.
13
 *
14
 * Класс реализует интерфейс Iterator.
15
 *
16
 * @property $getParams GET параметры запроса.
17
 */
18
abstract class AbstractList extends BaseObject implements \Iterator
19
{
20
    /**
21
     * Кол-во потоков.
22
     *
23
     * @var int
24
     */
25
    public $countThreads = 5;
26
27
    /**
28
     * GET параметр отвечающий за поток.
29
     *
30
     * @var string
31
     */
32
    public $keyThreads = 'page';
33
34
    /**
35
     * Ключ альтернативной пагинации.
36
     *
37
     * @var string
38
     */
39
    public $keyAlternativePagination = 'id-greater-than';
40
41
    /**
42
     * Использовать альтернативную пагинацию.
43
     *
44
     * @var bool
45
     */
46
    public $useAlternativePagination = false;
47
48
    /**
49
     * GET параметры запроса.
50
     *
51
     * @var array
52
     */
53
    protected $_getParams = [];
54
55
    /**
56
     * Кол-во повторов обращение к ресурсу при ошибках.
57
     *
58
     * @var int
59
     */
60
    public $repeatTimeout = 30;
61
62
    /**
63
     * Время в секундак до следующего обращения к ресурсу.
64
     *
65
     * @var int
66
     */
67
    public $repeatCount = 30;
68
69
70
    /**
71
     * SimaLand кдиент для запросов.
72
     *
73
     * @var \SimaLand\API\Rest\Client
74
     */
75
    private $client;
76
77
    /**
78
     * Список запросов.
79
     *
80
     * @var Request[]
81
     */
82
    private $requests = [];
83
84
    /**
85
     * Список данных полученные по API.
86
     *
87
     * @var array
88
     */
89
    private $values = [];
90
91
    /**
92
     * Ключ текущей записи.
93
     *
94
     * @var int
95
     */
96
    private $key;
97
98
    /**
99
     * Текущая запись.
100
     *
101
     * @var mixed
102
     */
103
    private $current;
104
105
    /**
106
     * Массив полей, которые пользователь хочет получить из API.
107
     *
108
     * @var array
109
     */
110
    public $fields = [];
111
112
    /**
113
     * @param Client $client
114
     * @param array $options
115
     */
116 87
    public function __construct(Client $client, array $options = [])
117
    {
118 87
        $this->client = $client;
119 87
        parent::__construct($options);
120 87
    }
121
122
    /**
123
     * Получить наименование сущности.
124
     *
125
     * @return string
126
     */
127
    abstract public function getEntity();
128
129
    /**
130
     * Добавить get параметры.
131
     *
132
     * @param array $params
133
     * @return AbstractList
134
     */
135 6
    public function addGetParams(array $params)
136
    {
137 6
        $this->setGetParams(array_merge($this->_getParams, $params));
138 6
        return $this;
139
    }
140
141
    /**
142
     * Назначить следующую страницу запросу.
143
     *
144
     * @param Request $request
145
     * @param Record|null $record
146
     */
147 33
    public function assignPage(Request &$request, Record $record = null)
148
    {
149 33
        if ($this->useAlternativePagination) {
150 18
            $this->assignAlternativePage($request, $record);
151 6
        } else {
152 15
            $this->assignDefaultPage($request);
153
        }
154 33
    }
155
156
    /**
157
     * Назначить следующую страницу запросу, используя стандартную пагинацию.
158
     *
159
     * @param Request $request
160
     */
161 15
    protected function assignDefaultPage(Request &$request)
162
    {
163 15
        $currentPage = 1;
164 15
        if (!is_array($request->getParams)) {
165 3
            $request->getParams = (array)$request->getParams;
166 1
        }
167 15
        if (isset($request->getParams[$this->keyThreads])) {
168 12
            $currentPage = (int)$request->getParams[$this->keyThreads];
169 4
        }
170 15
        $request->getParams[$this->keyThreads] = $currentPage + $this->countThreads;
171 15
    }
172
173
    /**
174
     * Назначить следующую страницу запросу, используют альтернативную пагинацию.
175
     *
176
     * @param Request $request
177
     * @param Record|null $record
178
     */
179 18
    protected function assignAlternativePage(Request &$request, Record $record = null)
180
    {
181 18
        $lastId = 0;
182 18
        if ($record && $record->data) {
183 18
            $lastId = (int)$record->data['id'];
184 6
        }
185 18
        if (!is_array($request->getParams)) {
186
            $request->getParams = (array)$request->getParams;
187
        }
188 18
        $request->getParams[$this->keyAlternativePagination] = $lastId;
189 18
    }
190
191
    /**
192
     * Назначить номер потока для запроса.
193
     *
194
     * @param Request $request
195
     * @param int $number
196
     */
197 30
    public function assignThreadsNumber(Request &$request, $number = 0)
198
    {
199 30
        if ("id-mf" == $this->keyThreads) {
200 21
            $this->assignMfThreadsNumber($request, $number);
201 7
        } else {
202 9
            $this->assignDefaultThreadsNumber($request, $number);
203
        }
204 30
    }
205
206
    /**
207
     * Назначить по умолчанию номер потока для запроса.
208
     *
209
     * @param Request $request
210
     * @param int $number
211
     */
212 9
    public function assignDefaultThreadsNumber(Request &$request, $number = 0)
213
    {
214 9
        if (!is_array($request->getParams)) {
215 3
            $request->getParams = (array)$request->getParams;
216 1
        }
217 9
        if (!isset($request->getParams[$this->keyThreads])) {
218 9
            $request->getParams[$this->keyThreads] = 1;
219 3
        }
220 9
        $request->getParams[$this->keyThreads] += $number;
221 9
    }
222
223
    /**
224
     * Назначить альтернативный номер потока для запроса.
225
     *
226
     * @param Request $request
227
     * @param int $number
228
     */
229 21
    public function assignMfThreadsNumber(Request &$request, $number = 0)
230
    {
231 21
        if (!is_array($request->getParams)) {
232 6
            $request->getParams = (array)$request->getParams;
233 2
        }
234 21
        $request->getParams[$this->keyThreads] = "{$this->countThreads},$number";
235 21
    }
236
237
    /**
238
     * Наименование ключа содержащего набора данных сущности.
239
     *
240
     * @return string
241
     */
242 27
    public function getCollectionKey()
243
    {
244 27
        return 'items';
245
    }
246
247
    /**
248
     * Наименование ключа содержащего мета данные.
249
     *
250
     * @return string
251
     */
252 27
    public function getMetaKey()
253
    {
254 27
        return '_meta';
255
    }
256
257
    /**
258
     * Палучить набор данных сущности.
259
     *
260
     * @return Response[]
261
     * @throws \Exception
262
     */
263 51
    public function get()
264
    {
265 51
        return $this->client->batchQuery($this->getRequests());
266
    }
267
268
    /**
269
     * Установить запросы к API.
270
     *
271
     * @param Request[] $requests
272
     * @throws \Exception
273
     */
274 33
    public function setRequests(array $requests)
275
    {
276 33
        $this->requests = [];
277 33
        foreach ($requests as $request) {
278 33
            if (!$request instanceof Request) {
279 3
                throw new \Exception('Request must be implement "\SimaLand\API\Rest\Request"');
280
            }
281 30
            $this->requests[] = $request;
282 10
        }
283 30
    }
284
285
    /**
286
     * Получить запросы к API.
287
     *
288
     * @return array|Rest\Request[]
289
     */
290 54
    public function getRequests()
291
    {
292 54
        if (empty($this->requests)) {
293 54
            $requests = [];
294 54
            if (!is_null($this->keyThreads) && $this->countThreads > 1) {
295 15
                for ($i = 0; $i < $this->countThreads; $i++) {
296 15
                    $requests[$i] = new Request([
297 15
                        'entity' => $this->getEntity(),
298 15
                        'getParams' => $this->_getParams,
299 5
                    ]);
300 15
                    $this->assignThreadsNumber($requests[$i], $i);
301 5
                }
302 5
            } else {
303 39
                $requests[] = new Request([
304 39
                    'entity' => $this->getEntity(),
305 39
                    'getParams' => $this->_getParams,
306 13
                ]);
307
            }
308 54
            $this->requests = $requests;
309 18
        }
310 54
        return $this->requests;
311
    }
312
313
    /**
314
     * @inheritdoc
315
     */
316 24
    public function current()
317
    {
318 24
        return $this->current;
319
    }
320
321
    /**
322
     * @inheritdoc
323
     */
324 45
    public function next()
325
    {
326 45
        if (empty($this->values)) {
327 45
            $this->getData();
328 9
        }
329 27
        $this->current = array_shift($this->values);
330 27
    }
331
332
    /**
333
     * @inheritdoc
334
     */
335 18
    public function key()
336
    {
337 18
        return $this->key++;
338
    }
339
340
    /**
341
     * @inheritdoc
342
     */
343 24
    public function valid()
344
    {
345 24
        return !empty($this->current);
346
    }
347
348
    /**
349
     * @inheritdoc
350
     */
351 36
    public function rewind()
352
    {
353 36
        $this->values = [];
354 36
        $this->current = null;
355 36
        $this->key = 0;
356 36
        $this->next();
357 24
    }
358
359
    /**
360
     * Обработка ответов от API.
361
     *
362
     * @param Response[] $responses
363
     * @throws ResponseException
364
     */
365 42
    private function processingResponses(array $responses)
366
    {
367 42
        foreach ($responses as $response) {
368 42
            $statusCode = $response->getStatusCode();
369 42
            if (($statusCode < 200 || $statusCode >= 300) && $statusCode != 404) {
370 35
                throw new ResponseException($response);
371
            }
372 9
        }
373 27
    }
374
375
    /**
376
     * Получение ответов от API
377
     *
378
     * @return \GuzzleHttp\Psr7\Response[]
379
     * @throws \Exception
380
     */
381 45
    private function getResponses()
382
    {
383 45
        $i = 0;
384 45
        $responses = [];
385 45
        $logger = $this->getLogger();
386
        do {
387 45
            $e = null;
388 45
            if ($i > 0) {
389 6
                $logger->info("Wait time {$this->repeatTimeout} second to the next request");
390 6
                sleep($this->repeatTimeout);
391 6
                $attempt = $i + 1;
392 6
                $logger->info("Attempt {$attempt} of {$this->repeatCount}");
393 2
            }
394
            try {
395 45
                $responses = $this->get();
396 42
                $this->processingResponses($responses);
397 31
            } catch (\Exception $e) {
398
                if (
399 24
                    ($e instanceof RequestException) ||
400 24
                    ($e instanceof ResponseException)
401 8
                ) {
402 21
                    $logger->warning($e->getMessage(), ['code' => $e->getCode()]);
403 7
                } else {
404 3
                    throw $e;
405
                }
406
            }
407 42
            $i++;
408 42
        } while ($i <= $this->repeatCount && !is_null($e));
409 42
        if ($e) {
410 18
            $logger->error($e->getMessage(), ['code' => $e->getCode()]);
411 18
            throw new Exception($e->getMessage(), $e->getCode(), $e);
412
        }
413 27
        return $responses;
414
    }
415
416
    /**
417
     * Получить тело ответа от API.
418
     *
419
     * @param Response $response
420
     * @return bool
421
     */
422 27
    private function getBody(Response $response)
423
    {
424 27
        $body = json_decode($response->getBody(), true);
425
        if (
426 27
            !$body ||
427 27
            ($body && !isset($body[$this->getCollectionKey()]))
428 9
        ) {
429 21
            return false;
430
        }
431 27
        return $body;
432
    }
433
434
    /**
435
     * Получить набор данных от API.
436
     *
437
     * @throws Exception
438
     */
439 45
    private function getData()
440
    {
441 45
        $responses = $this->getResponses();
442 27
        $collectionKey = $this->getCollectionKey();
443 27
        $metaKey = $this->getMetaKey();
444 27
        $requests = $this->getRequests();
445 27
        $record = null;
446 27
        foreach ($responses as $key => $response) {
447 27
            $body = $this->getBody($response);
448 27
            if (!$body) {
449 21
                unset($requests[$key]);
450 21
                continue;
451
            }
452 27
            foreach ($body[$collectionKey] as $item) {
453 24
                $record = new Record([
454 24
                    'data' => $this->cleanData($item),
455 24
                    'meta' => isset($body[$metaKey]) ? $body[$metaKey] : null,
456 8
                ]);
457 24
                $this->values[] = $record;
458 9
            }
459 27
            if (!is_null($record)) {
460 26
                $this->assignPage($requests[$key], $record);
461 8
            }
462 9
        }
463 27
        $this->setRequests($requests);
464 27
    }
465
466
    /**
467
     * Установить GET параметры запроса.
468
     *
469
     * @param array $value
470
     */
471 27
    public function setGetParams(array $value)
472
    {
473 27
        if ($this->useAlternativePagination) {
474 18
            if (!isset($value[$this->keyAlternativePagination])) {
475 15
                $value[$this->keyAlternativePagination] = 0;
476 5
            }
477 18
            $r = new \ReflectionClass(__CLASS__);
478 18
            $defaultProperties = $r->getDefaultProperties();
479 18
            $valueKeyThreads = $defaultProperties['keyThreads'];
480 18
            if (isset($value[$valueKeyThreads])) {
481 3
                unset($value[$valueKeyThreads]);
482 1
            }
483 6
        }
484 27
        $this->_getParams = $value;
485 27
    }
486
487
    /**
488
     * Получить GET параметры запроса.
489
     *
490
     * @return array
491
     */
492 21
    public function getGetParams()
493
    {
494 21
        return $this->_getParams;
495
    }
496
497
    /**
498
     * Получить только те данные из ответа,
499
     * которые указаны в свойсте fields объекта сущности,
500
     * если аттрибуты не указаны в свойсте fields,
501
     * то метод вернет все аттрибуты из ответа
502
     *
503
     * @param array $item
504
     * @return array
505
     */
506 24
    private function cleanData($item)
507
    {
508 24
        if (!empty($this->fields)) {
509 3
            $cleanItem = [];
510 3
            foreach ($this->fields as $value) {
511 3
                if (array_key_exists($value, $item)) {
512 3
                    $cleanItem[$value] = $item[$value];
513 1
                }
514 1
            }
515 1
        } else {
516 21
            $cleanItem = $item;
517
        }
518 24
        return $cleanItem;
519
    }
520
}
521