Completed
Push — master ( 2b818c...df4948 )
by Michael
04:25
created

AbstractDatabaseCommon::processSql()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 1
nc 1
1
<?php
2
/**
3
 * Contains AbstractDatabaseCommon class.
4
 *
5
 * PHP version 5.5
6
 *
7
 * LICENSE:
8
 * This file is part of Yet Another Php Eve Api Library also know as Yapeal
9
 * which can be used to access the Eve Online API data and place it into a
10
 * database.
11
 * Copyright (C) 2014-2016 Michael Cummings
12
 *
13
 * This program is free software: you can redistribute it and/or modify it
14
 * under the terms of the GNU Lesser General Public License as published by the
15
 * Free Software Foundation, either version 3 of the License, or (at your
16
 * option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful, but WITHOUT
19
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
21
 * for more details.
22
 *
23
 * You should have received a copy of the GNU Lesser General Public License
24
 * along with this program. If not, see
25
 * <http://www.gnu.org/licenses/>.
26
 *
27
 * You should be able to find a copy of this license in the LICENSE.md file. A
28
 * copy of the GNU GPL should also be available in the GNU-GPL.md file.
29
 *
30
 * @copyright 2014-2016 Michael Cummings
31
 * @license   http://www.gnu.org/copyleft/lesser.html GNU LGPL
32
 * @author    Michael Cummings <[email protected]>
33
 */
34
namespace Yapeal\Console\Command;
35
36
use FilePathNormalizer\FilePathNormalizerTrait;
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\CommonToolsTrait;
43
use Yapeal\Configuration\Wiring;
44
use Yapeal\Container\ContainerInterface;
45
use Yapeal\Exception\YapealDatabaseException;
46
47
/**
48
 * Class AbstractDatabaseCommon
49
 */
50
abstract class AbstractDatabaseCommon extends Command
51
{
52
    use CommonToolsTrait, FilePathNormalizerTrait;
53
    /**
54
     * Sets the help message and all the common options used by the Database:* commands.
55
     *
56
     * @param string $help Command help text.
57
     */
58
    protected function addOptions($help)
59
    {
60
        $mess = 'Configuration file to get settings from.'
61
            . ' <comment>NOTE: A (missing, unreadable, empty, etc) file will be silently ignored.</comment>';
62
        $this->addOption('configFile', 'c', InputOption::VALUE_REQUIRED, $mess)
63
             ->addOption('database', 'd', InputOption::VALUE_REQUIRED, 'Name of the database.')
64
             ->addOption('hostName', 'o', InputOption::VALUE_REQUIRED, 'Host name for database server.')
65
             ->addOption('password', 'p', InputOption::VALUE_REQUIRED, 'Password used to access database.')
66
             ->addOption('platform', null, InputOption::VALUE_REQUIRED,
67
                 'Platform of database driver. Currently only "mysql" can be used.')
68
             ->addOption('port', null, InputOption::VALUE_REQUIRED,
69
                 'Port number for remote server. Only needed if using http connection.')
70
             ->addOption('tablePrefix', 't', InputOption::VALUE_REQUIRED, 'Prefix for database table names.')
71
             ->addOption('userName', 'u', InputOption::VALUE_REQUIRED, 'User name used to access database.')
72
             ->setHelp($help);
73
    }
74
    /** @noinspection PhpMissingParentCallCommonInspection */
75
    /**
76
     * Executes the current command.
77
     *
78
     * This method is not abstract because you can use this class
79
     * as a concrete class. In this case, instead of defining the
80
     * execute() method, you set the code to execute by passing
81
     * a Closure to the setCode() method.
82
     *
83
     * @param InputInterface  $input  An InputInterface instance
84
     * @param OutputInterface $output An OutputInterface instance
85
     *
86
     * @return int|null null or 0 if everything went fine, or an error code
87
     * @throws \DomainException
88
     * @throws \LogicException
89
     * @throws \Yapeal\Exception\YapealException
90
     *
91
     * @see    setCode()
92
     */
93
    protected function execute(InputInterface $input, OutputInterface $output)
94
    {
95
        $this->processCliOptions($input->getOptions());
96
        return $this->processSql($output);
97
    }
98
    /**
99
     * @param string          $sqlStatements
100
     * @param string          $fileName
101
     * @param OutputInterface $output
102
     *
103
     * @throws \InvalidArgumentException
104
     * @throws \LogicException
105
     * @throws \Symfony\Component\Console\Exception\LogicException
106
     * @throws \Yapeal\Exception\YapealDatabaseException
107
     */
108
    protected function executeSqlStatements($sqlStatements, $fileName, OutputInterface $output)
109
    {
110
        $replacements = [
111
            ';' => '',
112
            '{database}' => $this->getDic()['Yapeal.Sql.database'],
113
            '{engine}' => $this->getDic()['Yapeal.Sql.engine'],
114
            '{ engine}' => $this->getDic()['Yapeal.Sql.engine'],
115
            '{table_prefix}' => $this->getDic()['Yapeal.Sql.tablePrefix'],
116
            '$$' => ';'
117
        ];
118
        $pdo = $this->getPdo();
119
        // Split up SQL into statements on ';'.
120
        // Replace {database}, {table_prefix}, {engine}, ';', and '$$' in statements.
121
        /**
122
         * @var string[] $statements
123
         */
124
        $statements = str_replace(array_keys($replacements), array_values($replacements), explode(';', $sqlStatements));
125
        // 5 is a 'magic' number that I think is shorter than any legal SQL statement.
126
        $statements = array_filter($statements, function ($value) {
127
            return 5 <= strlen(trim($value));
128
        });
129
        $progress = null;
130
        if ($output::VERBOSITY_QUIET !== $output->getVerbosity()) {
131
            if (false === strpos($fileName, '::')) {
132
                $mess = sprintf('<info>Execute %1$s/%2$s</info>', basename(dirname($fileName)),
133
                    basename($fileName, '.sql'));
134
            } else {
135
                $mess = sprintf('<info>Execute %s</info>', $fileName);
136
            }
137
            $output->writeln($mess);
138
            $progress = $this->createProgressBar($output, count($statements));
139
        }
140
        foreach ($statements as $statement => $sql) {
141
            $sql = trim($sql);
142
            try {
143
                $pdo->exec($sql);
144
                if (null !== $progress) {
145
                    $progress->setMessage('<comment>executing</comment>');
146
                    $progress->advance();
147
                }
148
            } catch (\PDOException $exc) {
149
                if (null !== $progress) {
150
                    $progress->setMessage('<error>Failed</error>');
151
                    $progress->finish();
152
                    $output->writeln('');
153
                }
154
                $mess = $sql . PHP_EOL;
155
                $mess .= sprintf(
156
                    'Sql failed in %1$s on statement %2$s with (%3$s) %4$s',
157
                    $fileName,
158
                    $statement,
159
                    $exc->getCode(),
160
                    $exc->getMessage()
161
                );
162
                throw new YapealDatabaseException($mess, 2);
163
            }
164
        }
165
        if (null !== $progress) {
166
            $progress->setMessage('');
167
            $progress->finish();
168
            $output->writeln('');
169
        }
170
    }
171
    /**
172
     * @param array $options
173
     *
174
     * @return AbstractDatabaseCommon
175
     * @throws \DomainException
176
     * @throws \LogicException
177
     * @throws \Yapeal\Exception\YapealException
178
     */
179
    protected function processCliOptions(array $options)
180
    {
181
        $base = 'Yapeal.Sql.';
182
        $dic = $this->getDic();
183
        foreach (['class', 'database', 'hostName', 'password', 'platform', 'tablePrefix', 'userName'] as $option) {
184
            if (!empty($options[$option])) {
185
                $dic[$base . $option] = $options[$option];
186
            }
187
        }
188
        if (!empty($options['configFile'])) {
189
            $this->processConfigFile($options, $dic);
190
        }
191
        return $this;
192
    }
193
    /**
194
     * @param array              $options
195
     * @param ContainerInterface $dic
196
     *
197
     * @throws \DomainException
198
     * @throws \Yapeal\Exception\YapealException
199
     */
200
    protected function processConfigFile(array $options, ContainerInterface $dic)
201
    {
202
        $fpn = $this->getFpn();
203
        $configFile = $fpn->normalizeFile($options['configFile'],
204
            $fpn::ABSOLUTE_ALLOWED | $fpn::VFS_ALLOWED | $fpn::WRAPPER_ALLOWED);
205
        if (!is_file($configFile) || !is_readable($configFile)) {
206
            return;
207
        }
208
        $wiring = new Wiring($dic);
209
        $settings = $wiring->parserConfigFile($configFile);
210
        if (0 !== count($settings)) {
211
            foreach ($settings as $key => $setting) {
212
                $dic[$key] = $setting;
213
            }
214
        }
215
    }
216
    /**
217
     * @param OutputInterface $output
218
     */
219
    abstract protected function processSql(OutputInterface $output);
220
    /**
221
     * @param OutputInterface $output
222
     * @param int             $statementCount
223
     *
224
     * @return ProgressBar
225
     */
226
    private function createProgressBar(OutputInterface $output, $statementCount)
227
    {
228
        $progress = new ProgressBar($output);
229
        $progress->setRedrawFrequency(1);
230
        $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s% %message%');
231
        $progress->setMessage('<info>starting</info>');
232
        $progress->start($statementCount);
233
        $progress->setBarWidth(min(4 * $statementCount + 2, 50));
234
        return $progress;
235
    }
236
}
237