Completed
Push — master ( b659c5...c65770 )
by Michael
10:22
created

CommonEveApiTrait::processEvents()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 0
cts 23
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 5
nop 2
crap 56
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-2017 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-2017 Michael Cummings
32
 * @license   LGPL-3.0+
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
     * @param MediatorInterface        $yem
54
     *
55
     * @return bool
56
     * @throws \DomainException
57
     * @throws \InvalidArgumentException
58
     * @throws \LogicException
59
     * @throws \UnexpectedValueException
60
     */
61
    public function oneShot(EveApiReadWriteInterface $data, MediatorInterface $yem): bool
62
    {
63
        if (!$this->gotApiLock($data, $yem)) {
64
            return false;
65
        }
66
        if ($this->cachedUntilIsNotExpired($data, $yem)) {
67
            return true;
68
        }
69
        $result = $this->processEvents($data, $yem);
70
        if ($result) {
71
            $this->updateCachedUntil($data, $yem);
72
            $this->emitEvents($data, 'end');
73
        }
74
        $this->releaseApiLock($data, $yem);
75
        return $result;
76
    }
77
    /**
78
     * @param EveApiEventInterface $event
79
     * @param string               $eventName
80
     * @param MediatorInterface    $yem
81
     *
82
     * @return EveApiEventInterface
83
     * @throws \DomainException
84
     * @throws \InvalidArgumentException
85
     * @throws \LogicException
86
     * @throws \UnexpectedValueException
87
     * @throws \Yapeal\Exception\YapealDatabaseException
88
     */
89
    public function startEveApi(EveApiEventInterface $event, string $eventName, MediatorInterface $yem)
90
    {
91
        $this->setYem($yem);
92
        $data = $event->getData();
93
        $yem->triggerLogEvent('Yapeal.Log.log',
94
            Logger::DEBUG,
95
            $this->getReceivedEventMessage($data, $eventName, __CLASS__));
96
        try {
97
            $records = $this->getActive($data, $yem);
98
        } catch (\PDOException $exc) {
99
            $mess = 'Could NOT get a list of active owners during the processing of';
100
            $yem->triggerLogEvent('Yapeal.Log.log',
101
                Logger::WARNING,
102
                $this->createEveApiMessage($mess, $data),
103
                ['exception' => $exc]);
104
            return $event;
105
        }
106
        if (0 === count($records)) {
107
            $mess = 'No active owners found during the processing of';
108
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data));
109
            $this->emitEvents($data, 'end');
110
            return $event->setHandledSufficiently();
111
        }
112
        if (0 !== count($this->accountKeys)) {
113
            $records = $this->processAccountKeys($records);
114
        }
115
        foreach ($records as $arguments) {
116
            $aClone = clone $data;
117
            if (false !== $arguments) {
118
                $aClone->setEveApiArguments($arguments);
119
                if (0 === strpos($data->getEveApiName(), 'Wallet')) {
120
                    $aClone->addEveApiArgument('rowCount', '2560');
121
                }
122
            }
123
            if ($this->oneShot($aClone, $yem)) {
124
                $event->setHandledSufficiently();
125
            }
126
        }
127
        return $event;
128
    }
129
    /**
130
     * @param EveApiReadWriteInterface $data
131
     * @param MediatorInterface        $yem
132
     *
133
     * @return bool
134
     * @throws \DomainException
135
     * @throws \InvalidArgumentException
136
     * @throws \LogicException
137
     * @throws \UnexpectedValueException
138
     */
139
    protected function cachedUntilIsNotExpired(EveApiReadWriteInterface $data, MediatorInterface $yem): bool
140
    {
141
        $sql = $this->getCsq()
142
            ->getCachedUntilExpires($data->hasEveApiArgument('accountKey') ? (int)$data->getEveApiArgument('accountKey') : 0,
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 125 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...
143
                $data->getEveApiName(),
144
                $this->extractOwnerID($data->getEveApiArguments()));
145
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, 'sql - ' . $sql);
146
        try {
147
            $expires = $this->getPdo()
148
                ->query($sql)
149
                ->fetchAll(\PDO::FETCH_ASSOC);
150
        } catch (\PDOException $exc) {
151
            $mess = 'Could NOT query cache expired during the processing of';
152
            $yem->triggerLogEvent('Yapeal.Log.log',
153
                    Logger::WARNING,
154
                    $this->createEveApiMessage($mess, $data),
155
                    ['exception' => $exc]);
156
            return false;
157
        }
158
        if (0 === count($expires)) {
159
            $mess = 'No cached until row found during the processing of';
160
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $this->createEveApiMessage($mess, $data));
161
            return false;
162
        }
163
        if (1 < count($expires)) {
164
            $mess = 'Multiple cached until rows found during the processing of';
165
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($mess, $data));
166
            return false;
167
        }
168
        if (strtotime($expires[0]['expires'] . '+00:00') < time()) {
169
            $mess = 'Expired cached until row found during the processing of';
170
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $this->createEveApiMessage($mess, $data));
171
            return false;
172
        }
173
        return true;
174
    }
175
    /**
176
     * @param string[] $candidates
177
     *
178
     * @return int
179
     */
180
    protected function extractOwnerID(array $candidates): int
181
    {
182
        foreach (['corporationID', 'characterID', 'keyID'] as $item) {
183
            if (array_key_exists($item, $candidates)) {
184
                return (int)$candidates[$item];
185
            }
186
        }
187
        return 0;
188
    }
189
    /**
190
     * @param EveApiReadWriteInterface $data
191
     * @param MediatorInterface        $yem
192
     *
193
     * @return array
194
     * @throws \DomainException
195
     * @throws \InvalidArgumentException
196
     * @throws \LogicException
197
     * @throws \UnexpectedValueException
198
     */
199
    protected function getActive(EveApiReadWriteInterface $data, MediatorInterface $yem)
200
    {
201
        switch (strtolower($data->getEveApiSectionName())) {
202
            case 'account':
203
                if ('APIKeyInfo' === $data->getEveApiName()) {
204
                    $sql = $this->getCsq()
205
                        ->getActiveRegisteredKeys();
206
                    break;
207
                }
208
                $sql = $this->getCsq()
209
                    ->getActiveRegisteredAccountStatus($this->getMask());
210
                break;
211
            case 'char':
212
                if ('MailBodies' === $data->getEveApiName()) {
213
                    $sql = $this->getCsq()
214
                        ->getActiveMailBodiesWithOwnerID((int)$data->getEveApiArgument('characterID'));
215
                    break;
216
                }
217
                $sql = $this->getCsq()
218
                    ->getActiveRegisteredCharacters($this->getMask());
219
                break;
220
            case 'corp':
221
                if ('StarbaseDetails' === $data->getEveApiName()) {
222
                    $sql = $this->getCsq()
223
                        ->getActiveStarbaseTowers($this->getMask(), (int)$data->getEveApiArgument('corporationID'));
224
                    break;
225
                }
226
                $sql = $this->getCsq()
227
                    ->getActiveRegisteredCorporations($this->getMask());
228
                break;
229
            default:
230
                return [false];
231
        }
232
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, 'sql - ' . $sql);
233
        return $this->getPdo()
234
            ->query($sql)
235
            ->fetchAll(PDO::FETCH_ASSOC);
236
    }
237
    /**
238
     * @return int
239
     */
240
    protected function getMask(): int
241
    {
242
        return $this->mask;
243
    }
244
    /**
245
     * @param EveApiReadWriteInterface $data
246
     * @param MediatorInterface        $yem
247
     *
248
     * @return bool
249
     * @throws \DomainException
250
     * @throws \InvalidArgumentException
251
     * @throws \LogicException
252
     * @throws \UnexpectedValueException
253
     */
254
    protected function gotApiLock(EveApiReadWriteInterface $data, MediatorInterface $yem): bool
255
    {
256
        $sql = $this->getCsq()
257
            ->getApiLock(crc32($data->getHash()));
258
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, 'sql - ' . $sql);
259
        $context = [];
260
        $success = false;
261
        try {
262
            $success = (bool)$this->getPdo()
263
                ->query($sql)
264
                ->fetchColumn();
265
        } catch (\PDOException $exc) {
266
            $context = ['exception' => $exc];
267
        }
268
        $mess = $success ? 'Got lock during the processing of' : 'Could NOT get lock during the processing of';
269
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data), $context);
270
        return $success;
271
    }
272
    /**
273
     * Used to make duplicate records for each accountKey.
274
     *
275
     * Eve APIs like the corp accountBalance, walletJournal, and walletTransactions are all per wallet as seen in game
276
     * so they have to be processed for each of the 'accountKey' to the Eve API servers. Currently that means 8 plus the
277
     * faction warfare/console game wallet for those corps involved with that part of the game.
278
     *
279
     * The same APIs for chars allow accountKey as well but they currently only have the one wallet 1000 so it could be
280
     * considered optional for them but CCP may decide to change that in the future so Yapeal uses it with them as well.
281
     *
282
     * @param array $records
283
     *
284
     * @return array
285
     */
286
    protected function processAccountKeys(array $records): array
287
    {
288
        $replacements = [];
289
        foreach ($records as $arguments) {
290
            $newArgs = $arguments;
291
            foreach ($this->accountKeys as $accountKey) {
292
                $newArgs['accountKey'] = $accountKey;
293
                $replacements[] = $newArgs;
294
            }
295
        }
296
        return $replacements;
297
    }
298
    /**
299
     * @param EveApiReadWriteInterface $data
300
     * @param MediatorInterface        $yem
301
     *
302
     * @return bool
303
     * @throws \DomainException
304
     * @throws \InvalidArgumentException
305
     * @throws \LogicException
306
     * @throws \UnexpectedValueException
307
     */
308
    protected function processEvents(EveApiReadWriteInterface $data, MediatorInterface $yem): bool
309
    {
310
        $eventSuffixes = ['retrieve', 'transform', 'validate', 'preserve'];
311
        foreach ($eventSuffixes as $eventSuffix) {
312
            if (false === $this->emitEvents($data, $eventSuffix)) {
313
                return false;
314
            }
315
            if ('' === $data->getEveApiXml()) {
316
                if ($data->hasEveApiArgument('accountKey') && '10000' === $data->getEveApiArgument('accountKey')
317
                    && 'corp' === strtolower($data->getEveApiSectionName())
318
                ) {
319
                    $mess = 'No faction warfare account data during the processing of';
320
                    $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data));
321
                    return false;
322
                }
323
                $yem->triggerLogEvent('Yapeal.Log.log',
324
                        Logger::INFO,
325
                        $this->getEmptyXmlDataMessage($data, $eventSuffix));
326
                return false;
327
            }
328
        }
329
        return true;
330
    }
331
    /**
332
     * @param EveApiReadWriteInterface $data
333
     * @param MediatorInterface        $yem
334
     *
335
     * @return bool
336
     * @throws \DomainException
337
     * @throws \InvalidArgumentException
338
     * @throws \LogicException
339
     * @throws \UnexpectedValueException
340
     */
341
    protected function releaseApiLock(EveApiReadWriteInterface $data, MediatorInterface $yem): bool
342
    {
343
        $sql = $this->getCsq()
344
            ->getApiLockRelease(crc32($data->getHash()));
345
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, 'sql - ' . $sql);
346
        $context = [];
347
        $success = false;
348
        try {
349
            $success = (bool)$this->getPdo()
350
                ->query($sql)
351
                ->fetchColumn();
352
        } catch (\PDOException $exc) {
353
            $context = ['exception' => $exc];
354
        }
355
        $mess = $success ? 'Released lock for' : 'Could NOT release lock for';
356
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data), $context);
357
        return $success;
358
    }
359
    /**
360
     * @param EveApiReadWriteInterface $data
361
     * @param MediatorInterface        $yem
362
     *
363
     * @return static Fluent interface.
364
     * @throws \DomainException
365
     * @throws \InvalidArgumentException
366
     * @throws \LogicException
367
     * @throws \UnexpectedValueException
368
     */
369
    protected function updateCachedUntil(EveApiReadWriteInterface $data, MediatorInterface $yem)
370
    {
371
        if ('' === $data->getEveApiXml()) {
372
            return $this;
373
        }
374
        $currentTime = (string)(new \SimpleXMLElement($data->getEveApiXml()))->currentTime[0];
375
        if ('' === $currentTime) {
376
            return $this;
377
        }
378
        $dateTime = gmdate('Y-m-d H:i:s', strtotime($currentTime . '+00:00') + $data->getCacheInterval());
379
        $row = [
380
            'accountKey' => $data->hasEveApiArgument('accountKey') ? $data->getEveApiArgument('accountKey') : '0',
381
            'apiName' => $data->getEveApiName(),
382
            'expires' => $dateTime,
383
            'ownerID' => $this->extractOwnerID($data->getEveApiArguments()),
384
            'sectionName' => $data->getEveApiSectionName()
385
        ];
386
        $sql = $this->getCsq()
387
            ->getUpsert('utilCachedUntil', array_keys($row), 1);
388
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, 'sql - ' . $sql);
389
        $pdo = $this->getPdo();
390
        $pdo->beginTransaction();
391
        $context = [];
392
        $success = false;
393
        try {
394
            $pdo->prepare($sql)
395
                ->execute(array_values($row));
396
            $pdo->commit();
397
            $success = true;
398
        } catch (\PDOException $exc) {
399
            $pdo->rollBack();
400
            $context = ['exception' => $exc];
401
        }
402
        $mess = $success ? 'Updated cached until date/time of' : 'Could NOT update cached until date/time of';
403
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($mess, $data), $context);
404
        return $this;
405
    }
406
    /**
407
     * @var array $accountKey
408
     */
409
    protected $accountKeys = [];
410
    /**
411
     * @var int $mask
412
     */
413
    protected $mask;
414
}
415