Completed
Push — master ( eaf5ea...97c539 )
by Michael
05:35
created

CommonEveApiTrait   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 379
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 52
lcom 1
cbo 6
dl 0
loc 379
ccs 0
cts 210
cp 0
rs 7.9487
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A oneShot() 0 13 3
C startEveApi() 0 47 10
B cachedUntilIsNotExpired() 0 41 6
A extractOwnerID() 0 9 3
C getActive() 0 39 7
A getMask() 0 4 1
A gotApiLock() 0 20 3
C processEvents() 0 25 7
A releaseApiLock() 0 20 3
B updateCachedUntil() 0 41 6
A processAccountKeys() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like CommonEveApiTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CommonEveApiTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains CommonEveApiTrait trait.
5
 *
6
 * PHP version 7.0+
7
 *
8
 * LICENSE:
9
 * This file is part of Yet Another Php Eve Api Library also know as Yapeal
10
 * which can be used to access the Eve Online API data and place it into a
11
 * database.
12
 * Copyright (C) 2015-2016 Michael Cummings
13
 *
14
 * This program is free software: you can redistribute it and/or modify it
15
 * under the terms of the GNU Lesser General Public License as published by the
16
 * Free Software Foundation, either version 3 of the License, or (at your
17
 * option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful, but WITHOUT
20
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
22
 * for more details.
23
 *
24
 * You should have received a copy of the GNU Lesser General Public License
25
 * along with this program. If not, see
26
 * <http://spdx.org/licenses/LGPL-3.0.html>.
27
 *
28
 * You should be able to find a copy of this license in the COPYING-LESSER.md
29
 * file. A copy of the GNU GPL should also be available in the COPYING.md file.
30
 *
31
 * @copyright 2015-2016 Michael Cummings
32
 * @license   http://www.gnu.org/copyleft/lesser.html GNU LGPL
33
 * @author    Michael Cummings <[email protected]>
34
 */
35
namespace Yapeal\EveApi;
36
37
use Monolog\Logger;
38
use PDO;
39
use Yapeal\CommonToolsTrait;
40
use Yapeal\Event\EveApiEventEmitterTrait;
41
use Yapeal\Event\EveApiEventInterface;
42
use Yapeal\Event\MediatorInterface;
43
use Yapeal\Xml\EveApiReadWriteInterface;
44
45
/**
46
 * Trait CommonEveApiTrait
47
 */
48
trait CommonEveApiTrait
49
{
50
    use CommonToolsTrait, EveApiEventEmitterTrait;
51
    /**
52
     * @param EveApiReadWriteInterface $data
53
     *
54
     * @return bool
55
     * @throws \DomainException
56
     * @throws \InvalidArgumentException
57
     * @throws \LogicException
58
     * @throws \Yapeal\Exception\YapealDatabaseException
59
     */
60
    public function oneShot(EveApiReadWriteInterface $data): bool
61
    {
62
        if (!$this->gotApiLock($data)) {
63
            return false;
64
        }
65
        $result = $this->processEvents($data);
66
        if ($result) {
67
            $this->updateCachedUntil($data);
68
            $this->emitEvents($data, 'end');
69
        }
70
        $this->releaseApiLock($data);
71
        return $result;
72
    }
73
    /**
74
     * @param EveApiEventInterface $event
75
     * @param string               $eventName
76
     * @param MediatorInterface    $yem
77
     *
78
     * @return EveApiEventInterface
79
     * @throws \DomainException
80
     * @throws \InvalidArgumentException
81
     * @throws \LogicException
82
     * @throws \Yapeal\Exception\YapealDatabaseException
83
     */
84
    public function startEveApi(EveApiEventInterface $event, string $eventName, MediatorInterface $yem)
85
    {
86
        if (!$this->hasYem()) {
87
            $this->setYem($yem);
88
        }
89
        $data = $event->getData();
90
        $yem->triggerLogEvent('Yapeal.Log.log',
91
            Logger::DEBUG,
92
            $this->getReceivedEventMessage($data, $eventName, __CLASS__));
93
        try {
94
            $records = $this->getActive($data);
95
        } catch (\PDOException $exc) {
96
            $mess = 'Could NOT get a list of active owners for';
97
            $this->getYem()
98
                ->triggerLogEvent('Yapeal.Log.log',
99
                    Logger::WARNING,
100
                    $this->createEveApiMessage($mess, $data),
101
                    ['exception' => $exc]);
102
            return $event;
103
        }
104
        if (0 === count($records)) {
105
            $mess = 'No active owners found for';
106
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data));
107
            $this->emitEvents($data, 'end');
108
            return $event->setHandledSufficiently();
109
        }
110
        if (0 !== count($this->accountKeys)) {
111
            $records = $this->processAccountKeys($records);
112
        }
113
        foreach ($records as $arguments) {
114
            $aClone = clone $data;
115
            if (false !== $arguments) {
116
                $aClone->setEveApiArguments($arguments);
117
                if (0 === strpos($data->getEveApiName(), 'Wallet')) {
118
                    $aClone->addEveApiArgument('rowCount', '2560');
119
                }
120
            }
121
            if ($this->cachedUntilIsNotExpired($aClone)) {
122
                $event->setHandledSufficiently();
123
                continue;
124
            }
125
            if ($this->oneShot($aClone)) {
126
                $event->setHandledSufficiently();
127
            }
128
        }
129
        return $event;
130
    }
131
    /**
132
     * @param EveApiReadWriteInterface $data
133
     *
134
     * @return bool
135
     * @throws \DomainException
136
     * @throws \InvalidArgumentException
137
     * @throws \LogicException
138
     * @throws \Yapeal\Exception\YapealDatabaseException
139
     */
140
    protected function cachedUntilIsNotExpired(EveApiReadWriteInterface $data): bool
141
    {
142
        $sql = $this->getCsq()
143
            ->getUtilCachedUntilExpires($data->hasEveApiArgument('accountKey') ? $data->getEveApiArgument('accountKey') : '0',
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 126 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
144
                $data->getEveApiName(),
145
                $this->extractOwnerID($data->getEveApiArguments()));
146
        $this->getYem()
147
            ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $sql);
148
        try {
149
            $expires = $this->getPdo()
150
                ->query($sql)
151
                ->fetchAll(\PDO::FETCH_ASSOC);
152
        } catch (\PDOException $exc) {
153
            $mess = 'Could NOT get cache expired for';
154
            $this->getYem()
155
                ->triggerLogEvent('Yapeal.Log.log',
156
                    Logger::WARNING,
157
                    $this->createEveApiMessage($mess, $data),
158
                    ['exception' => $exc]);
159
            return false;
160
        }
161
        if (0 === count($expires)) {
162
            $mess = 'No UtilCachedUntil record found for';
163
            $this->getYem()
164
                ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $this->createEveApiMessage($mess, $data));
165
            return false;
166
        }
167
        if (1 < count($expires)) {
168
            $mess = 'Multiple UtilCachedUntil records found for';
169
            $this->getYem()
170
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($mess, $data));
171
            return false;
172
        }
173
        if (strtotime($expires[0]['expires'] . '+00:00') < time()) {
174
            $mess = 'Expired UtilCachedUntil record found for';
175
            $this->getYem()
176
                ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $this->createEveApiMessage($mess, $data));
177
            return false;
178
        }
179
        return true;
180
    }
181
    /**
182
     * @param string[] $candidates
183
     *
184
     * @return string
185
     */
186
    protected function extractOwnerID(array $candidates): string
187
    {
188
        foreach (['corporationID', 'characterID', 'keyID'] as $item) {
189
            if (array_key_exists($item, $candidates)) {
190
                return (string)$candidates[$item];
191
            }
192
        }
193
        return '0';
194
    }
195
    /**
196
     * @param EveApiReadWriteInterface $data
197
     *
198
     * @return array
199
     * @throws \DomainException
200
     * @throws \InvalidArgumentException
201
     * @throws \LogicException
202
     * @throws \PDOException
203
     * @throws \Yapeal\Exception\YapealDatabaseException
204
     */
205
    protected function getActive(EveApiReadWriteInterface $data)
206
    {
207
        switch (strtolower($data->getEveApiSectionName())) {
208
            case 'account':
209
                if ('APIKeyInfo' === $data->getEveApiName()) {
210
                    $sql = $this->getCsq()
211
                        ->getActiveRegisteredKeys();
212
                    break;
213
                }
214
                $sql = $this->getCsq()
215
                    ->getActiveRegisteredAccountStatus($this->getMask());
216
                break;
217
            case 'char':
218
                if ('MailBodies' === $data->getEveApiName()) {
219
                    $sql = $this->getCsq()
220
                        ->getActiveMailBodiesWithOwnerID($data->getEveApiArgument('characterID'));
221
                    break;
222
                }
223
                $sql = $this->getCsq()
224
                    ->getActiveRegisteredCharacters($this->getMask());
225
                break;
226
            case 'corp':
227
                if ('StarbaseDetails' === $data->getEveApiName()) {
228
                    $sql = $this->getCsq()
229
                        ->getActiveStarbaseTowers($this->getMask(), $data->getEveApiArgument('corporationID'));
230
                    break;
231
                }
232
                $sql = $this->getCsq()
233
                    ->getActiveRegisteredCorporations($this->getMask());
234
                break;
235
            default:
236
                return [false];
237
        }
238
        $this->getYem()
239
            ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $sql);
240
        return $this->getPdo()
241
            ->query($sql)
242
            ->fetchAll(PDO::FETCH_ASSOC);
243
    }
244
    /**
245
     * @return int
246
     */
247
    protected function getMask(): int
248
    {
249
        return $this->mask;
250
    }
251
    /**
252
     * @param EveApiReadWriteInterface $data
253
     *
254
     * @return bool
255
     * @throws \DomainException
256
     * @throws \InvalidArgumentException
257
     * @throws \LogicException
258
     * @throws \Yapeal\Exception\YapealDatabaseException
259
     */
260
    protected function gotApiLock(EveApiReadWriteInterface $data): bool
261
    {
262
        $sql = $this->getCsq()
263
            ->getApiLock(crc32($data->getHash()));
264
        $this->getYem()
265
            ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $sql);
266
        $context = [];
267
        $success = false;
268
        try {
269
            $success = (bool)$this->getPdo()
270
                ->query($sql)
271
                ->fetchColumn();
272
        } catch (\PDOException $exc) {
273
            $context = ['exception' => $exc];
274
        }
275
        $mess = $success ? 'Got lock for' : 'Could NOT get lock for';
276
        $this->getYem()
277
            ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data), $context);
278
        return $success;
279
    }
280
    /**
281
     * @param EveApiReadWriteInterface $data
282
     *
283
     * @return bool
284
     * @throws \DomainException
285
     * @throws \InvalidArgumentException
286
     * @throws \LogicException
287
     */
288
    protected function processEvents(EveApiReadWriteInterface $data): bool
289
    {
290
        $eventSuffixes = ['retrieve', 'transform', 'validate', 'preserve'];
291
        foreach ($eventSuffixes as $eventSuffix) {
292
            if (false === $this->emitEvents($data, $eventSuffix)) {
293
                return false;
294
            }
295
            if (false === $data->getEveApiXml()) {
296
                if ($data->hasEveApiArgument('accountKey') && '10000' === $data->getEveApiArgument('accountKey')
297
                    && 'corp' === strtolower($data->getEveApiSectionName())
298
                ) {
299
                    $mess = 'No faction warfare account data in';
300
                    $this->getYem()
301
                        ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data));
302
                    return false;
303
                }
304
                $this->getYem()
305
                    ->triggerLogEvent('Yapeal.Log.log',
306
                        Logger::INFO,
307
                        $this->getEmptyXmlDataMessage($data, $eventSuffix));
308
                return false;
309
            }
310
        }
311
        return true;
312
    }
313
    /**
314
     * @param EveApiReadWriteInterface $data
315
     *
316
     * @return bool
317
     * @throws \DomainException
318
     * @throws \InvalidArgumentException
319
     * @throws \LogicException
320
     * @throws \Yapeal\Exception\YapealDatabaseException
321
     */
322
    protected function releaseApiLock(EveApiReadWriteInterface $data): bool
323
    {
324
        $sql = $this->getCsq()
325
            ->getApiLockRelease(crc32($data->getHash()));
326
        $this->getYem()
327
            ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $sql);
328
        $context = [];
329
        $success = false;
330
        try {
331
            $success = (bool)$this->getPdo()
332
                ->query($sql)
333
                ->fetchColumn();
334
        } catch (\PDOException $exc) {
335
            $context = ['exception' => $exc];
336
        }
337
        $mess = $success ? 'Released lock for' : 'Could NOT release lock for';
338
        $this->getYem()
339
            ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data), $context);
340
        return $success;
341
    }
342
    /**
343
     * @param EveApiReadWriteInterface $data
344
     *
345
     * @return self Fluent interface.
346
     * @throws \DomainException
347
     * @throws \InvalidArgumentException
348
     * @throws \LogicException
349
     * @throws \Yapeal\Exception\YapealDatabaseException
350
     */
351
    protected function updateCachedUntil(EveApiReadWriteInterface $data)
352
    {
353
        if (false === $data->getEveApiXml()) {
354
            return $this;
355
        }
356
        /** @noinspection PhpUndefinedFieldInspection */
357
        /** @noinspection UnnecessaryParenthesesInspection */
358
        $currentTime = (string)(new \SimpleXMLElement($data->getEveApiXml()))->currentTime[0];
359
        if ('' === $currentTime) {
360
            return $this;
361
        }
362
        $dateTime = gmdate('Y-m-d H:i:s', strtotime($currentTime . '+00:00') + $data->getCacheInterval());
363
        $row = [
364
            'accountKey' => $data->hasEveApiArgument('accountKey') ? $data->getEveApiArgument('accountKey') : '0',
365
            'apiName' => $data->getEveApiName(),
366
            'expires' => $dateTime,
367
            'ownerID' => $this->extractOwnerID($data->getEveApiArguments()),
368
            'sectionName' => $data->getEveApiSectionName()
369
        ];
370
        $sql = $this->getCsq()
371
            ->getUpsert('utilCachedUntil', array_keys($row), 1);
372
        $this->getYem()
373
            ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $sql);
374
        $pdo = $this->getPdo();
375
        $pdo->beginTransaction();
376
        $context = [];
377
        $success = false;
378
        try {
379
            $pdo->prepare($sql)
380
                ->execute(array_values($row));
381
            $pdo->commit();
382
            $success = true;
383
        } catch (\PDOException $exc) {
384
            $pdo->rollBack();
385
            $context = ['exception' => $exc];
386
        }
387
        $mess = $success ? 'Updated cached until date/time of' : 'Could NOT update cached until date/time of';
388
        $this->getYem()
389
            ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data), $context);
390
        return $this;
391
    }
392
    /**
393
     * @var array $accountKey
394
     */
395
    protected $accountKeys = [];
396
    /**
397
     * @var int $mask
398
     */
399
    protected $mask;
400
    /**
401
     * Used to make duplicate records for each accountKey.
402
     *
403
     * Eve APIs like the corp accountBalance, walletJournal, and walletTransactions are all per wallet as seen in game
404
     * so they have to be processed for each of the 'accountKey' to the Eve API servers. Currently that means 8 plus the
405
     * faction warfare/console game wallet for those corps involved with that part of the game.
406
     *
407
     * The same APIs for chars allow accountKey as well but they currently only have the one wallet 1000 so it could be
408
     * considered optional for them but CCP may decide to change that in the future so Yapeal uses it with them as well.
409
     *
410
     * @param array $records
411
     *
412
     * @return array
413
     */
414
    protected function processAccountKeys(array $records): array
415
    {
416
        $replacements = [];
417
        foreach ($records as $arguments) {
418
            foreach ($this->accountKeys as $accountKey) {
419
                $newArgs = $arguments;
420
                $newArgs['accountKey'] = $accountKey;
421
                $replacements[] = $newArgs;
422
            }
423
        }
424
        return $replacements;
425
    }
426
}
427