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

DatabaseInitializer::addCustomFile()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 24
ccs 0
cts 23
cp 0
rs 6.7272
cc 7
eloc 16
nc 10
nop 3
crap 56
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains DatabaseInitializer 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\Helper\QuestionHelper;
38
use Symfony\Component\Console\Input\InputInterface;
39
use Symfony\Component\Console\Input\InputOption;
40
use Symfony\Component\Console\Output\OutputInterface;
41
use Symfony\Component\Console\Question\ConfirmationQuestion;
42
use Yapeal\Container\ContainerInterface;
43
use Yapeal\Event\YEMAwareTrait;
44
use Yapeal\Log\Logger;
45
46
/**
47
 * Class DatabaseInitializer
48
 */
49
class DatabaseInitializer extends AbstractDatabaseCommon
50
{
51
    use YEMAwareTrait;
52
    /**
53
     * @param string             $name
54
     * @param ContainerInterface $dic
55
     *
56
     * @throws \Symfony\Component\Console\Exception\LogicException
57
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
58
     */
59
    public function __construct(string $name, ContainerInterface $dic)
60
    {
61
        $this->setDescription('Retrieves SQL from files and initializes database');
62
        $this->setName($name);
63
        $this->setDic($dic);
64
        parent::__construct($name);
65
    }
66
    /** @noinspection PhpMissingParentCallCommonInspection */
67
    /**
68
     * Configures the current command.
69
     *
70
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
71
     */
72
    protected function configure()
73
    {
74
        $help = <<<'HELP'
75
The <info>%command.full_name%</info> command is used to initialize (create) a new
76
 database and tables to be used by Yapeal. If you already have a
77
 config/yapeal.yaml file setup you can use the following:
78
79
    <info>php %command.full_name%</info>
80
81
EXAMPLES:
82
To use a configuration file in a different location:
83
    <info>%command.name% -c /my/very/special/config.yaml</info>
84
85
<info>NOTE:</info>
86
Only the Database section of the configuration file will be used.
87
88
You can also use the command before setting up a configuration file like so:
89
    <info>%command.name% -o "localhost" -d "yapeal" -u "YapealUser" -p "secret"
90
91
HELP;
92
        $this->addOptions($help);
93
        $desc = 'Drop existing schema(database) before re-creating. <comment>Warning all the tables will be dropped as well!</comment>';
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 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...
94
        $this->addOption('dropSchema', null, InputOption::VALUE_NONE, $desc);
95
    }
96
    /**
97
     * Executes the current command.
98
     *
99
     * This method is not abstract because you can use this class
100
     * as a concrete class. In this case, instead of defining the
101
     * execute() method, you set the code to execute by passing
102
     * a Closure to the setCode() method.
103
     *
104
     * @param InputInterface  $input  An InputInterface instance
105
     * @param OutputInterface $output An OutputInterface instance
106
     *
107
     * @return int null or 0 if everything went fine, or an error code
108
     * @throws \DomainException
109
     * @throws \InvalidArgumentException
110
     * @throws \LogicException
111
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
112
     * @throws \Symfony\Component\Console\Exception\LogicException
113
     * @throws \Symfony\Component\Console\Exception\RuntimeException
114
     * @throws \Yapeal\Exception\YapealDatabaseException
115
     * @throws \Yapeal\Exception\YapealException
116
     *
117
     * @see    setCode()
118
     */
119
    protected function execute(InputInterface $input, OutputInterface $output): int
120
    {
121
        if (!$this->hasYem()) {
122
            $this->setYem($this->getDic()['Yapeal.Event.Mediator']);
123
        }
124
        if ($input->getOption('dropSchema')) {
125
            /**
126
             * @var QuestionHelper $question
127
             */
128
            $question = $this->getHelper('question');
129
            $mess = '<comment>Are you sure you want to drop the schema(database) and it\'s tables with their data?(no)</comment>';
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 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...
130
            $confirm = new ConfirmationQuestion($mess, false);
131
            $this->dropSchema = $question->ask($input, $output, $confirm);
0 ignored issues
show
Documentation Bug introduced by
The property $dropSchema was declared of type boolean, but $question->ask($input, $output, $confirm) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
132
            if (!$this->dropSchema) {
133
                $output->writeln('<info>Ignoring drop schema(database)</info>');
134
            }
135
        }
136
        return parent::execute($input, $output);
137
    }
138
    /**
139
     * @param OutputInterface $output
140
     *
141
     * @throws \DomainException
142
     * @throws \InvalidArgumentException
143
     * @throws \LogicException
144
     * @throws \Symfony\Component\Console\Exception\LogicException
145
     * @throws \Yapeal\Exception\YapealDatabaseException
146
     * @throws \Yapeal\Exception\YapealFileSystemException
147
     */
148
    protected function processSql(OutputInterface $output)
149
    {
150
        $yem = $this->getYem();
151
        /**
152
         * @var array        $section
153
         * @var string|false $sqlStatements
154
         */
155
        foreach ($this->getCreateFileList($output) as $section) {
156
            foreach ($section as $keyName => $sqlStatements) {
157
                if (false === $sqlStatements) {
158
                    if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
159
                        $mess = sprintf('<error>Could NOT get contents of SQL file for %1$s</error>', $keyName);
160
                        $output->writeln($mess);
161
                        $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
162
                    }
163
                    continue;
164
                }
165
                $this->executeSqlStatements($sqlStatements, $keyName, $output);
166
            }
167
        }
168
    }
169
    /**
170
     * First custom tables sql file found is returned.
171
     *
172
     * @param string $path
173
     * @param string $platformExt
174
     * @param array  $fileList
175
     *
176
     * @return array
177
     * @throws \LogicException
178
     * @throws \Yapeal\Exception\YapealFileSystemException
179
     */
180
    private function addCustomFile(string $path, string $platformExt, array $fileList): array
181
    {
182
        $fileNames = '%1$sCreateCustomTables,%2$sconfig/CreateCustomTables';
183
        $subs = [$path, $this->getDic()['Yapeal.baseDir']];
184
        if (!empty($dic['Yapeal.vendorParentDir'])) {
0 ignored issues
show
Bug introduced by
The variable $dic seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
185
            $fileNames .= ',%3$sconfig/CreateCustomTables';
186
            $subs[] = $this->getDic()['Yapeal.vendorParentDir'];
187
        }
188
        $customFiles = array_reverse(explode(',', vsprintf($fileNames, $subs)));
189
        // First one found wins.
190
        foreach ([$platformExt, '.sql'] as $ext) {
191
            foreach ($customFiles as $keyName) {
192
                if (is_readable($keyName . $ext) && is_file($keyName . $ext)) {
193
                    $contents = $this->safeFileRead($keyName . $ext);
194
                    if (false === $contents) {
195
                        continue;
196
                    }
197
                    $fileList['Custom'][$keyName] = $contents;
198
                    break 2;
199
                }
200
            }
201
        }
202
        return $fileList;
203
    }
204
    /**
205
     * @param OutputInterface $output
206
     *
207
     * @return array
208
     * @throws \DomainException
209
     * @throws \InvalidArgumentException
210
     * @throws \LogicException
211
     * @throws \Yapeal\Exception\YapealFileSystemException
212
     */
213
    private function getCreateFileList(OutputInterface $output): array
214
    {
215
        $dic = $this->getDic();
216
        $path = $dic['Yapeal.Sql.dir'];
217
        if (!is_readable($path)) {
218
            if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
219
                $mess = sprintf('<comment>Could NOT access Sql directory %1$s</comment>', $path);
220
                $this->getYem()
221
                    ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
222
                $output->writeln($mess);
223
            }
224
            return [];
225
        }
226
        $fileList = [];
227
        $platformExt = sprintf('.%1$s.sql', $dic['Yapeal.Sql.platform']);
228
        // First find any sql files with platform extension.
229
        $fileList = $this->getSectionsFileList($path, $fileList, $platformExt);
230
        $fileList = $this->prependDropSchema($path, $fileList, $platformExt);
231
        $fileList = $this->addCustomFile($path, $platformExt, $fileList);
232
        return $fileList;
233
    }
234
    /**
235
     * @param string $path
236
     * @param array  $fileList
237
     * @param string $ext
238
     *
239
     * @return array
240
     * @throws \Yapeal\Exception\YapealFileSystemException
241
     */
242
    private function getSectionsFileList(string $path, array $fileList, string $ext = '.sql'): array
243
    {
244
        $fpn = $this->getFpn();
245
        $sections = ['Schema', 'Util', 'Account', 'Api', 'Char', 'Corp', 'Eve', 'Map', 'Server'];
246
        foreach ($sections as $section) {
247
            foreach (new \DirectoryIterator($path . $section . '/') as $fileInfo) {
248
                // Add file path if it's a sql create file for the correct platform.
249
                if ($fileInfo->isFile() && 0 === strpos($fileInfo->getBasename(), 'Create')) {
250
                    $baseName = $fileInfo->getBasename();
251
                    $firstDot = strpos($baseName, '.');
252
                    $isSql = $firstDot === strpos($baseName, '.sql');
253
                    $isPlatform = $firstDot === strpos($baseName, $ext);
254
                    $baseName = substr($baseName, 0, $firstDot);
255
                    $keyName = $fpn->normalizePath($fileInfo->getPath()) . $baseName;
256
                    $notSet = !array_key_exists($section, $fileList)
257
                        || !array_key_exists($keyName, $fileList[$section])
258
                        || false === $fileList[$section][$keyName];
259
                    if ($isPlatform) {
260
                        $fileList[$section][$keyName] = $this->safeFileRead($keyName . $ext);
261
                    } elseif ($isSql && $notSet) {
262
                        $fileList[$section][$keyName] = $this->safeFileRead($keyName . '.sql');
263
                    }
264
                }
265
            }
266
        }
267
        return $fileList;
268
    }
269
    /**
270
     * Prepends drop schema if it is requested and exists.
271
     *
272
     * @param string $path
273
     * @param array  $fileList
274
     * @param string $platformExt
275
     *
276
     * @return array
277
     * @throws \Yapeal\Exception\YapealFileSystemException
278
     */
279
    private function prependDropSchema(string $path, array $fileList, string $platformExt)
280
    {
281
        if (true !== $this->dropSchema) {
282
            return $fileList;
283
        }
284
        // Add drop database file if requested and exists.
285
        $keyName = $path . 'Schema/DropSchema';
286
        foreach ([$platformExt, '.sql'] as $ext) {
287
            if (is_readable($keyName . $ext) && is_file($keyName . $ext)) {
288
                $contents = $this->safeFileRead($keyName . $ext);
289
                if (false === $contents) {
290
                    continue;
291
                }
292
                if (array_key_exists('Schema', $fileList)) {
293
                    $schema = array_reverse($fileList['Schema'], true);
294
                    $schema[$keyName] = $contents;
295
                    $fileList['Schema'] = array_reverse($schema, true);
296
                    break;
297
                } else {
298
                    $fileList = array_reverse($fileList, true);
299
                    $fileList['Schema'][$keyName] = $contents;
300
                    $fileList = array_reverse($fileList, true);
301
                }
302
            }
303
        }
304
        return $fileList;
305
    }
306
    /**
307
     * @var bool $dropSchema
308
     */
309
    private $dropSchema = false;
310
}
311