Completed
Push — master ( 4a905b...82874d )
by Michael
02:55
created

SchemaInitializer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 0
cts 7
cp 0
rs 9.4285
cc 1
eloc 5
nc 1
nop 2
crap 2
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains SchemaInitializer 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\Cli\Schema;
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 SchemaInitializer
48
 */
49
class SchemaInitializer extends AbstractSchemaCommon
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 schema');
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
 schema and tables to be used by Yapeal-ng. 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 Sql 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
        $this->setAliases(['Database:Init']);
96
    }
97
    /**
98
     * Executes the current command.
99
     *
100
     * This method is not abstract because you can use this class
101
     * as a concrete class. In this case, instead of defining the
102
     * execute() method, you set the code to execute by passing
103
     * a Closure to the setCode() method.
104
     *
105
     * @param InputInterface  $input  An InputInterface instance
106
     * @param OutputInterface $output An OutputInterface instance
107
     *
108
     * @return int null or 0 if everything went fine, or an error code
109
     * @throws \DomainException
110
     * @throws \InvalidArgumentException
111
     * @throws \LogicException
112
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
113
     * @throws \Symfony\Component\Console\Exception\LogicException
114
     * @throws \Symfony\Component\Console\Exception\RuntimeException
115
     * @throws \Yapeal\Exception\YapealDatabaseException
116
     * @throws \Yapeal\Exception\YapealException
117
     *
118
     * @see    setCode()
119
     */
120
    protected function execute(InputInterface $input, OutputInterface $output): int
121
    {
122
        if (!$this->hasYem()) {
123
            $this->setYem($this->getDic()['Yapeal.Event.Mediator']);
124
        }
125
        if ($input->getOption('dropSchema')) {
126
            /**
127
             * @var QuestionHelper $question
128
             */
129
            $question = $this->getHelper('question');
130
            $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...
131
            $confirm = new ConfirmationQuestion($mess, false);
132
            $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...
133
            if (!$this->dropSchema) {
134
                $output->writeln('<info>Ignoring drop schema(database)</info>');
135
            }
136
        }
137
        return parent::execute($input, $output);
138
    }
139
    /**
140
     * @param OutputInterface $output
141
     *
142
     * @throws \DomainException
143
     * @throws \InvalidArgumentException
144
     * @throws \LogicException
145
     * @throws \Symfony\Component\Console\Exception\LogicException
146
     * @throws \Yapeal\Exception\YapealDatabaseException
147
     * @throws \Yapeal\Exception\YapealFileSystemException
148
     */
149
    protected function processSql(OutputInterface $output)
150
    {
151
        $yem = $this->getYem();
152
        /**
153
         * @var array        $section
154
         * @var string|false $sqlStatements
155
         */
156
        foreach ($this->getCreateFileList($output) as $section) {
157
            foreach ($section as $keyName => $sqlStatements) {
158
                if (false === $sqlStatements) {
159
                    if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
160
                        $mess = sprintf('<error>Could NOT get contents of SQL file for %1$s</error>', $keyName);
161
                        $output->writeln($mess);
162
                        $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
163
                    }
164
                    continue;
165
                }
166
                $this->executeSqlStatements($sqlStatements, $keyName, $output);
167
            }
168
        }
169
    }
170
    /**
171
     * First custom tables sql file found is returned.
172
     *
173
     * @param string $path
174
     * @param string $platformExt
175
     * @param array  $fileList
176
     *
177
     * @return array
178
     * @throws \LogicException
179
     * @throws \Yapeal\Exception\YapealFileSystemException
180
     */
181
    private function addCustomFile(string $path, string $platformExt, array $fileList): array
182
    {
183
        $fileNames = '%1$sCreateCustomTables,%2$sconfig/CreateCustomTables';
184
        $subs = [$path, $this->getDic()['Yapeal.baseDir']];
185
        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...
186
            $fileNames .= ',%3$sconfig/CreateCustomTables';
187
            $subs[] = $this->getDic()['Yapeal.vendorParentDir'];
188
        }
189
        $customFiles = array_reverse(explode(',', vsprintf($fileNames, $subs)));
190
        // First one found wins.
191
        foreach ([$platformExt, '.sql'] as $ext) {
192
            foreach ($customFiles as $keyName) {
193
                if (is_readable($keyName . $ext) && is_file($keyName . $ext)) {
194
                    $contents = $this->safeFileRead($keyName . $ext);
195
                    if (false === $contents) {
196
                        continue;
197
                    }
198
                    $fileList['Custom'][$keyName] = $contents;
199
                    break 2;
200
                }
201
            }
202
        }
203
        return $fileList;
204
    }
205
    /**
206
     * @param OutputInterface $output
207
     *
208
     * @return array
209
     * @throws \DomainException
210
     * @throws \InvalidArgumentException
211
     * @throws \LogicException
212
     * @throws \Yapeal\Exception\YapealFileSystemException
213
     */
214
    private function getCreateFileList(OutputInterface $output): array
215
    {
216
        $dic = $this->getDic();
217
        $path = $dic['Yapeal.Sql.dir'];
218
        if (!is_readable($path)) {
219
            if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
220
                $mess = sprintf('<comment>Could NOT access Sql directory %1$s</comment>', $path);
221
                $this->getYem()
222
                    ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
223
                $output->writeln($mess);
224
            }
225
            return [];
226
        }
227
        $fileList = [];
228
        $platformExt = sprintf('.%1$s.sql', $dic['Yapeal.Sql.platform']);
229
        // First find any sql files with platform extension.
230
        $fileList = $this->getSectionsFileList($path, $fileList, $platformExt);
231
        $fileList = $this->prependDropSchema($path, $fileList, $platformExt);
232
        $fileList = $this->addCustomFile($path, $platformExt, $fileList);
233
        return $fileList;
234
    }
235
    /**
236
     * @param string $path
237
     * @param array  $fileList
238
     * @param string $ext
239
     *
240
     * @return array
241
     * @throws \Yapeal\Exception\YapealFileSystemException
242
     */
243
    private function getSectionsFileList(string $path, array $fileList, string $ext = '.sql'): array
244
    {
245
        $fpn = $this->getFpn();
246
        $sections = ['Schema', 'Util', 'Account', 'Api', 'Char', 'Corp', 'Eve', 'Map', 'Server'];
247
        foreach ($sections as $section) {
248
            foreach (new \DirectoryIterator($path . $section . '/') as $fileInfo) {
249
                // Add file path if it's a sql create file for the correct platform.
250
                if ($fileInfo->isFile() && 0 === strpos($fileInfo->getBasename(), 'Create')) {
251
                    $baseName = $fileInfo->getBasename();
252
                    $firstDot = strpos($baseName, '.');
253
                    $isSql = $firstDot === strpos($baseName, '.sql');
254
                    $isPlatform = $firstDot === strpos($baseName, $ext);
255
                    $baseName = substr($baseName, 0, $firstDot);
256
                    $keyName = $fpn->normalizePath($fileInfo->getPath()) . $baseName;
257
                    $notSet = !array_key_exists($section, $fileList)
258
                        || !array_key_exists($keyName, $fileList[$section])
259
                        || false === $fileList[$section][$keyName];
260
                    if ($isPlatform) {
261
                        $fileList[$section][$keyName] = $this->safeFileRead($keyName . $ext);
262
                    } elseif ($isSql && $notSet) {
263
                        $fileList[$section][$keyName] = $this->safeFileRead($keyName . '.sql');
264
                    }
265
                }
266
            }
267
        }
268
        return $fileList;
269
    }
270
    /**
271
     * Prepends drop schema if it is requested and exists.
272
     *
273
     * @param string $path
274
     * @param array  $fileList
275
     * @param string $platformExt
276
     *
277
     * @return array
278
     * @throws \Yapeal\Exception\YapealFileSystemException
279
     */
280
    private function prependDropSchema(string $path, array $fileList, string $platformExt)
281
    {
282
        if (true !== $this->dropSchema) {
283
            return $fileList;
284
        }
285
        // Add drop database file if requested and exists.
286
        $keyName = $path . 'Schema/DropSchema';
287
        foreach ([$platformExt, '.sql'] as $ext) {
288
            if (is_readable($keyName . $ext) && is_file($keyName . $ext)) {
289
                $contents = $this->safeFileRead($keyName . $ext);
290
                if (false === $contents) {
291
                    continue;
292
                }
293
                if (array_key_exists('Schema', $fileList)) {
294
                    $schema = array_reverse($fileList['Schema'], true);
295
                    $schema[$keyName] = $contents;
296
                    $fileList['Schema'] = array_reverse($schema, true);
297
                    break;
298
                } else {
299
                    $fileList = array_reverse($fileList, true);
300
                    $fileList['Schema'][$keyName] = $contents;
301
                    $fileList = array_reverse($fileList, true);
302
                }
303
            }
304
        }
305
        return $fileList;
306
    }
307
    /**
308
     * @var bool $dropSchema
309
     */
310
    private $dropSchema = false;
311
}
312