Completed
Push — master ( 598fe1...4a905b )
by Michael
03:41
created

DatabaseUpdater::processSql()   D

Complexity

Conditions 9
Paths 16

Size

Total Lines 44
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 44
ccs 0
cts 43
cp 0
rs 4.909
cc 9
eloc 33
nc 16
nop 1
crap 90
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains DatabaseUpdater class.
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) 2014-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 2014-2016 Michael Cummings
32
 * @license   http://www.gnu.org/copyleft/lesser.html GNU LGPL
33
 * @author    Michael Cummings <[email protected]>
34
 */
35
namespace Yapeal\Console\Command;
36
37
use Symfony\Component\Console\Output\OutputInterface;
38
use Yapeal\Container\ContainerInterface;
39
use Yapeal\Event\YEMAwareTrait;
40
use Yapeal\Exception\YapealDatabaseException;
41
use Yapeal\Log\Logger;
42
43
/**
44
 * Class DatabaseUpdater
45
 */
46
class DatabaseUpdater extends AbstractDatabaseCommon
47
{
48
    use YEMAwareTrait;
49
    /**
50
     * @param string             $name
51
     * @param ContainerInterface $dic
52
     *
53
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
54
     * @throws \Symfony\Component\Console\Exception\LogicException
55
     */
56
    public function __construct(string $name, ContainerInterface $dic)
57
    {
58
        $this->setDescription('Retrieves SQL from files and updates database');
59
        $this->setName($name);
60
        $this->setDic($dic);
61
        parent::__construct($name);
62
    }
63
    /**
64
     * @param OutputInterface $output
65
     *
66
     * @throws \DomainException
67
     * @throws \InvalidArgumentException
68
     * @throws \LogicException
69
     * @throws \Symfony\Component\Console\Exception\LogicException
70
     * @throws \Yapeal\Exception\YapealDatabaseException
71
     */
72
    protected function addDatabaseProcedure(OutputInterface $output)
73
    {
74
        $name = 'DatabaseUpdater::addDatabaseProcedure';
75
        $csq = $this->getCsq();
76
        $this->executeSqlStatements($csq->getDropAddOrModifyColumnProcedure()
77
            . PHP_EOL
78
            . $csq->getCreateAddOrModifyColumnProcedure(),
79
            $name,
80
            $output);
81
        $output->writeln('');
82
    }
83
    /**
84
     * Configures the current command.
85
     */
86
    protected function configure()
87
    {
88
        $help = <<<'HELP'
89
The <info>%command.full_name%</info> command is used to initialize (create) a new
90
 database and tables to be used by Yapeal. If you already have a
91
 config/yapeal.yaml file setup you can use the following:
92
93
    <info>php %command.full_name%</info>
94
95
EXAMPLES:
96
To use a configuration file in a different location:
97
    <info>%command.name% -c /my/very/special/config.yaml</info>
98
99
<info>NOTE:</info>
100
Only the Database section of the configuration file will be used.
101
102
You can also use the command before setting up a configuration file like so:
103
    <info>%command.name% -o "localhost" -d "yapeal" -u "YapealUser" -p "secret"
104
105
HELP;
106
        $this->addOptions($help);
107
    }
108
    /**
109
     * @param OutputInterface $output
110
     *
111
     * @throws \DomainException
112
     * @throws \InvalidArgumentException
113
     * @throws \LogicException
114
     * @throws \Symfony\Component\Console\Exception\LogicException
115
     * @throws \Yapeal\Exception\YapealDatabaseException
116
     */
117
    protected function dropDatabaseProcedure(OutputInterface $output)
118
    {
119
        $name = 'DatabaseUpdater::dropDatabaseProcedure';
120
        $this->executeSqlStatements($this->getCsq()
121
            ->getDropAddOrModifyColumnProcedure(),
122
            $name,
123
            $output);
124
    }
125
    /**
126
     * @param OutputInterface $output
127
     *
128
     * @return string
129
     * @throws \LogicException
130
     * @throws \Yapeal\Exception\YapealDatabaseException
131
     */
132
    protected function getLatestDatabaseVersion(OutputInterface $output): string
133
    {
134
        $sql = $this->getCsq()
135
            ->getUtilLatestDatabaseVersion();
136
        try {
137
            $result = $this->getPdo()
138
                ->query($sql, \PDO::FETCH_NUM);
139
            $version = sprintf('%018.3F', $result->fetchColumn());
140
            $result->closeCursor();
141
        } catch (\PDOException $exc) {
142
            $version = '19700101000001.000';
143
            $mess = sprintf('<error>Could NOT get latest database version using default %1$s</error>', $version);
144
            $output->writeln([$sql, $mess]);
145
            $mess = sprintf('<info>Error message from database connection was %s</info>',
146
                $exc->getMessage());
147
            $output->writeln($mess);
148
        }
149
        return $version;
150
    }
151
    /**
152
     * @return array
153
     * @throws \LogicException
154
     */
155
    protected function getReplacements()
156
    {
157
        $replacements = parent::getReplacements();
158
        $replacements['$$'] = ';';
159
        return $replacements;
160
    }
161
    /**
162
     * @param OutputInterface $output
163
     *
164
     * @return string[]
165
     * @throws \LogicException
166
     */
167
    protected function getUpdateFileList(OutputInterface $output): array
168
    {
169
        $fileNames = [];
170
        $path = $this->getDic()['Yapeal.Sql.dir'] . 'updates/';
171
        if (!is_readable($path) || !is_dir($path)) {
172
            $mess = sprintf('<info>Could NOT access update directory %1$s</info>',
173
                $path);
174
            $output->writeln($mess);
175
            return $fileNames;
176
        }
177
        foreach (new \DirectoryIterator($path) as $fileInfo) {
178
            if ($fileInfo->isDot() || $fileInfo->isDir()) {
179
                continue;
180
            }
181
            if ('sql' !== $fileInfo->getExtension()) {
182
                continue;
183
            }
184
            $fileNames[] = $this->getFpn()
185
                ->normalizeFile($fileInfo->getPathname());
186
        }
187
        asort($fileNames);
188
        return $fileNames;
189
    }
190
    /**
191
     * @param OutputInterface $output
192
     *
193
     * @throws \DomainException
194
     * @throws \InvalidArgumentException
195
     * @throws \LogicException
196
     * @throws \Symfony\Component\Console\Exception\LogicException
197
     * @throws \Yapeal\Exception\YapealDatabaseException
198
     */
199
    protected function processSql(OutputInterface $output)
200
    {
201
        if (!$this->hasYem()) {
202
            $this->setYem($this->getDic()['Yapeal.Event.Mediator']);
203
        }
204
        $yem = $this->getYem();
205
        $this->addDatabaseProcedure($output);
206
        foreach ($this->getUpdateFileList($output) as $fileName) {
207
            /** @noinspection DisconnectedForeachInstructionInspection */
208
            $latestVersion = $this->getLatestDatabaseVersion($output);
209
            if (!is_file($fileName)) {
210
                if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
211
                    $mess = sprintf('<info>Could NOT find SQL file %1$s</info>', $fileName);
212
                    $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
213
                    $output->writeln($mess);
214
                }
215
                continue;
216
            }
217
            $updateVersion = basename($fileName, '.sql');
218
            if ($updateVersion <= $latestVersion) {
219
                if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
220
                    $mess = sprintf('<info>Skipping SQL file %1$s since its <= the latest database version %2$s</info>',
221
                        basename($fileName),
222
                        $latestVersion);
223
                    $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
224
                    $output->writeln($mess);
225
                }
226
                continue;
227
            }
228
            $sqlStatements = file_get_contents($fileName);
229
            if (false === $sqlStatements) {
230
                if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
231
                    $mess = sprintf('<error>Could NOT get contents of SQL file %1$s</error>',
232
                        $fileName);
233
                    $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
234
                    $output->writeln($mess);
235
                }
236
                continue;
237
            }
238
            $this->executeSqlStatements($sqlStatements, $fileName, $output);
239
            $this->updateDatabaseVersion($updateVersion);
240
        }
241
        $this->dropDatabaseProcedure($output);
242
    }
243
    /**
244
     * @param string $updateVersion
245
     *
246
     * @return DatabaseUpdater
247
     * @throws \LogicException
248
     * @throws \Yapeal\Exception\YapealDatabaseException
249
     */
250
    protected function updateDatabaseVersion(string $updateVersion)
251
    {
252
        $pdo = $this->getPdo();
253
        $sql = $this->getCsq()
254
            ->getUtilLatestDatabaseVersionUpdate();
255
        try {
256
            $pdo->beginTransaction();
257
            $pdo->prepare($sql)
258
                ->execute([$updateVersion]);
259
            $pdo->commit();
260
        } catch (\PDOException $exc) {
261
            $mess = $sql . PHP_EOL;
262
            $mess .= sprintf('Database error message was %s', $exc->getMessage()) . PHP_EOL;
263
            $mess .= sprintf('Database "version" update failed for %1$s',
264
                $updateVersion);
265
            if ($this->getPdo()
266
                ->inTransaction()
267
            ) {
268
                $this->getPdo()
269
                    ->rollBack();
270
            }
271
            throw new YapealDatabaseException($mess, 2);
272
        }
273
        return $this;
274
    }
275
}
276