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

AbstractSchemaCommon::getReplacements()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

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 2
eloc 4
nc 2
nop 0
crap 6
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains AbstractSchemaCommon 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\Command\Command;
38
use Symfony\Component\Console\Helper\ProgressBar;
39
use Symfony\Component\Console\Input\InputInterface;
40
use Symfony\Component\Console\Input\InputOption;
41
use Symfony\Component\Console\Output\OutputInterface;
42
use Yapeal\Cli\ConfigFileTrait;
43
use Yapeal\Cli\VerbosityToStrategyTrait;
44
use Yapeal\CommonToolsTrait;
45
use Yapeal\Event\YEMAwareInterface;
46
use Yapeal\Event\YEMAwareTrait;
47
use Yapeal\Exception\YapealDatabaseException;
48
use Yapeal\FileSystem\CommonFileHandlingTrait;
49
use Yapeal\Log\Logger;
50
use Yapeal\Sql\SqlSubsTrait;
51
52
/**
53
 * Class AbstractSchemaCommon
54
 */
55
abstract class AbstractSchemaCommon extends Command implements YEMAwareInterface
56
{
57
    use CommonFileHandlingTrait, CommonToolsTrait, ConfigFileTrait, SqlSubsTrait, VerbosityToStrategyTrait, YEMAwareTrait;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 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...
58
    /**
59
     * Sets the help message and all the common options used by the Database:* commands.
60
     *
61
     * @param string $help Command help text.
62
     */
63
    protected function addOptions(string $help)
64
    {
65
        $this->addConfigFileOption();
66
        $this->addOption('database', 'd', InputOption::VALUE_REQUIRED, 'Name of the database.')
0 ignored issues
show
Bug introduced by
It seems like setHelp() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
67
            ->addOption('hostName', 'o', InputOption::VALUE_REQUIRED, 'Host name for database server.')
68
            ->addOption('password', 'p', InputOption::VALUE_REQUIRED, 'Password used to access database.')
69
            ->addOption('platform',
70
                'l',
71
                InputOption::VALUE_REQUIRED,
72
                'Platform of database driver. Currently only "mysql" can be used.',
73
                'mysql')
74
            ->addOption('port',
75
                null,
76
                InputOption::VALUE_REQUIRED,
77
                'Port number for remote server. Only needed if using http connection.')
78
            ->addOption('tablePrefix', 't', InputOption::VALUE_REQUIRED, 'Prefix for database table names.')
79
            ->addOption('userName', 'u', InputOption::VALUE_REQUIRED, 'User name used to access database.')
80
            ->setHelp($help);
81
    }
82
    /** @noinspection PhpMissingParentCallCommonInspection */
83
    /**
84
     * Executes the current command.
85
     *
86
     * This method is not abstract because you can use this class
87
     * as a concrete class. In this case, instead of defining the
88
     * execute() method, you set the code to execute by passing
89
     * a Closure to the setCode() method.
90
     *
91
     * @param InputInterface  $input  An InputInterface instance
92
     * @param OutputInterface $output An OutputInterface instance
93
     *
94
     * @return int|null null or 0 if everything went fine, or an error code
95
     * @throws \DomainException
96
     * @throws \LogicException
97
     * @throws \Yapeal\Exception\YapealException
98
     *
99
     * @see    setCode()
100
     */
101
    protected function execute(InputInterface $input, OutputInterface $output): int
102
    {
103
        if (!$this->hasYem()) {
104
            $this->setYem($this->getDic()['Yapeal.Event.Mediator']);
105
        }
106
        $this->setLogThresholdFromVerbosity($output);
107
        $this->processCliOptions($input);
108
        $this->processSql($output);
109
        return 0;
110
    }
111
    /**
112
     * @param string          $sqlStatements
113
     * @param string          $fileName
114
     * @param OutputInterface $output
115
     *
116
     * @throws \DomainException
117
     * @throws \InvalidArgumentException
118
     * @throws \LogicException
119
     * @throws \Symfony\Component\Console\Exception\LogicException
120
     * @throws \Yapeal\Exception\YapealDatabaseException
121
     * @internal param string $sqlStatements
122
     */
123
    protected function executeSqlStatements(string $sqlStatements, string $fileName, OutputInterface $output)
124
    {
125
        $pdo = $this->getPdo();
126
        $yem = $this->getYem();
127
        $statements = explode(';', $this->getCleanedUpSql($sqlStatements, $this->getReplacements()));
128
        $statements = array_filter($statements,
129
            function ($statement) {
130
                return '' !== trim($statement);
131
            });
132
        $progress = null;
133
        if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
134
            if (false === strpos($fileName, '::')) {
135
                $mess = sprintf('<info>Execute %1$s/%2$s</info>',
136
                    basename(dirname($fileName)),
137
                    basename($fileName));
138
            } else {
139
                $mess = sprintf('<info>Execute %s</info>', $fileName);
140
            }
141
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, strip_tags($mess));
142
            $output->writeln($mess);
143
            $progress = $this->createProgressBar($output, count($statements));
144
        }
145
        foreach ($statements as $statement => $sql) {
146
            try {
147
                $pdo->exec($sql);
148
                if (null !== $progress) {
149
                    $progress->setMessage('<comment>executing</comment>');
150
                    $progress->advance();
151
                }
152
            } catch (\PDOException $exc) {
153
                if (null !== $progress) {
154
                    $progress->setMessage('<error>Failed</error>');
155
                    $progress->finish();
156
                    $output->writeln('');
157
                }
158
                $mess = $sql . PHP_EOL;
159
                $mess .= sprintf('Sql failed in %1$s on statement %2$s with (%3$s) %4$s',
160
                    $fileName,
161
                    $statement,
162
                    $exc->getCode(),
163
                    $exc->getMessage());
164
                throw new YapealDatabaseException($mess, 2);
165
            }
166
        }
167
        if (null !== $progress) {
168
            $progress->setMessage('');
169
            $progress->finish();
170
            $output->writeln('');
171
        }
172
    }
173
    /**
174
     * @return array
175
     * @throws \LogicException
176
     */
177
    protected function getReplacements()
178
    {
179
        if (null === $this->replacements) {
180
            $this->replacements = $this->getSqlSubs($this->getDic());
181
        }
182
        return $this->replacements;
183
    }
184
    /**
185
     * @param InputInterface $input
186
     *
187
     * @return AbstractSchemaCommon
188
     * @throws \DomainException
189
     * @throws \LogicException
190
     * @throws \Yapeal\Exception\YapealException
191
     */
192
    protected function processCliOptions(InputInterface $input)
193
    {
194
        $dic = $this->getDic();
195
        $options = $input->getOptions();
196
        if (!empty($options['configFile'])) {
197
            $this->processConfigFile($options['configFile'], $dic);
198
        }
199
        $base = 'Yapeal.Sql.';
200
        foreach (['class', 'database', 'hostName', 'password', 'platform', 'tablePrefix', 'userName'] as $option) {
201
            if (array_key_exists($option, $options) && null !== $options[$option]) {
202
                $dic[$base . $option] = $options[$option];
203
            }
204
        }
205
        return $this;
206
    }
207
    /**
208
     * @param OutputInterface $output
209
     */
210
    abstract protected function processSql(OutputInterface $output);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
211
    /**
212
     * @var array $replacements Holds a list of Sql section replacement pairs.
213
     */
214
    protected $replacements;
215
    /**
216
     * @param OutputInterface $output
217
     * @param int             $statementCount
218
     *
219
     * @return ProgressBar
220
     */
221
    private function createProgressBar(OutputInterface $output, int $statementCount): ProgressBar
222
    {
223
        $progress = new ProgressBar($output);
224
        $progress->setRedrawFrequency(1);
225
        $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s% %message%');
226
        $progress->setMessage('<info>starting</info>');
227
        $progress->start($statementCount);
228
        $progress->setBarWidth(min(4 * $statementCount + 2, 50));
229
        return $progress;
230
    }
231
}
232