Completed
Pull Request — master (#9)
by Sander
04:49
created

RunCommand::configure()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 68
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 65
dl 0
loc 68
ccs 0
cts 67
cp 0
rs 8.7636
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * neuralyzer : Data Anonymization Library and CLI Tool
4
 *
5
 * PHP Version 7.1
6
 *
7
 * @author    Emmanuel Dyan
8
 * @author    Rémi Sauvat
9
 * @copyright 2018 Emmanuel Dyan
10
 *
11
 * @package   edyan/neuralyzer
12
 *
13
 * @license   GNU General Public License v2.0
14
 *
15
 * @link      https://github.com/edyan/neuralyzer
16
 */
17
18
namespace Edyan\Neuralyzer\Console\Commands;
19
20
use Edyan\Neuralyzer\Anonymizer\DB;
21
use Edyan\Neuralyzer\Configuration\Reader;
22
use Edyan\Neuralyzer\Utils\DBUtils;
23
use Symfony\Component\Console\Command\Command;
24
use Symfony\Component\Console\Helper\ProgressBar;
25
use Symfony\Component\Console\Input\InputInterface;
26
use Symfony\Component\Console\Input\InputOption;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\Console\Question\Question;
29
use Symfony\Component\Stopwatch\Stopwatch;
30
31
/**
32
 * Command to launch an anonymization based on a config file
33
 */
34
class RunCommand extends Command
35
{
36
    /**
37
     * Store the DB Object
38
     *
39
     * @var DB
40
     */
41
    private $db;
42
43
    /**
44
     * Set the command shortcut to be used in configuration
45
     *
46
     * @var string
47
     */
48
    private $command = 'run';
49
50
    /**
51
     * Symfony's Input Class for parameters and options
52
     *
53
     * @var InputInterface
54
     */
55
    private $input;
56
57
    /**
58
     * Symfony's Output Class to display info
59
     *
60
     * @var OutputInterface
61
     */
62
    private $output;
63
64
    /**
65
     * Neuralyzer reader
66
     *
67
     * @var Reader
68
     */
69
    private $reader;
70
71
    /**
72
     * RunCommand constructor.
73
     *
74
     * @param DB $db
75
     */
76
    public function __construct(DB $db)
77
    {
78
        parent::__construct();
79
80
        $this->db = $db;
81
    }
82
83
84
    /**
85
     * Configure the command
86
     *
87
     * @return void
88
     */
89
    protected function configure(): void
90
    {
91
        // First command : Test the DB Connexion
92
        $this->setName($this->command)
93
            ->setDescription('Run Anonymizer')
94
            ->setHelp(
95
                'This command will connect to a DB and run the anonymizer from a yaml config'.PHP_EOL.
96
                "Usage: <info>./bin/neuralyzer {$this->command} -u app -p app -f neuralyzer.yml</info>"
97
            )->addOption(
98
                'driver',
99
                'D',
100
                InputOption::VALUE_REQUIRED,
101
                'Driver (check Doctrine documentation to have the list)',
102
                'pdo_mysql'
103
            )->addOption(
104
                'host',
105
                'H',
106
                InputOption::VALUE_REQUIRED,
107
                'Host',
108
                '127.0.0.1'
109
            )->addOption(
110
                'db',
111
                'd',
112
                InputOption::VALUE_REQUIRED,
113
                'Database Name'
114
            )->addOption(
115
                'user',
116
                'u',
117
                InputOption::VALUE_REQUIRED,
118
                'User Name',
119
                get_current_user()
120
            )->addOption(
121
                'password',
122
                'p',
123
                InputOption::VALUE_REQUIRED,
124
                'Password (or prompted)'
125
            )->addOption(
126
                'config',
127
                'c',
128
                InputOption::VALUE_REQUIRED,
129
                'Configuration File',
130
                'neuralyzer.yml'
131
            )->addOption(
132
                'table',
133
                't',
134
                InputOption::VALUE_REQUIRED,
135
                'Do a single table'
136
            )->addOption(
137
                'pretend',
138
                null,
139
                InputOption::VALUE_NONE,
140
                "Don't run the queries"
141
            )->addOption(
142
                'sql',
143
                's',
144
                InputOption::VALUE_NONE,
145
                'Display the SQL'
146
            )->addOption(
147
                'limit',
148
                'l',
149
                InputOption::VALUE_REQUIRED,
150
                'Limit the number of written records (update or insert). 100 by default for insert'
151
            )->addOption(
152
                'mode',
153
                'm',
154
                InputOption::VALUE_REQUIRED,
155
                'Set the mode : batch or queries',
156
                'batch'
157
            );
158
    }
159
160
    /**
161
     * @param InputInterface  $input
162
     * @param OutputInterface $output
163
     *
164
     * @throws \Doctrine\DBAL\DBALException
165
     * @throws \Edyan\Neuralyzer\Exception\NeuralizerException
166
     */
167
    protected function execute(InputInterface $input, OutputInterface $output): void
168
    {
169
        // Throw an exception immediately if we dont have the required DB parameter
170
        if (empty($input->getOption('db'))) {
171
            throw new \InvalidArgumentException('Database name is required (--db)');
172
        }
173
174
        // Throw an exception immediately if we dont have the right mode
175
        if (!in_array($input->getOption('mode'), ['queries', 'batch'])) {
176
            throw new \InvalidArgumentException('--mode could be only "queries" or "batch"');
177
        }
178
179
        $password = $input->getOption('password');
180
        if (null === $password) {
181
            $question = new Question('Password: ');
182
            $question->setHidden(true)->setHiddenFallback(false);
183
184
            $password = $this->getHelper('question')->ask($input, $output, $question);
185
        }
186
187
        $this->input = $input;
188
        $this->output = $output;
189
190
        // Anon READER
191
        $this->reader = new Reader($input->getOption('config'));
192
193
        // Now work on the DB
194
        $this->db->initDatabaseConnection(
195
            [
196
                'driver' => $input->getOption('driver'),
197
                'host' => $input->getOption('host'),
198
                'dbname' => $input->getOption('db'),
199
                'user' => $input->getOption('user'),
200
                'password' => $password,
201
            ]
202
        );
203
        $this->db->setConfiguration($this->reader);
204
        $this->db->setMode($this->input->getOption('mode'));
205
        $this->db->setPretend($this->input->getOption('pretend'));
206
        $this->db->setReturnRes($this->input->getOption('sql'));
207
208
        $stopwatch = new Stopwatch();
209
        $stopwatch->start('Neuralyzer');
210
        // Get tables
211
        $table = $input->getOption('table');
212
        $tables = empty($table) ? $this->reader->getEntities() : [$table];
213
        foreach ($tables as $table) {
214
            $this->anonymizeTable($table);
215
        }
216
217
        // Get memory and execution time information
218
        $event = $stopwatch->stop('Neuralyzer');
219
        $memory = round($event->getMemory() / 1024 / 1024, 2);
220
        $time = round($event->getDuration() / 1000, 2);
221
        $time = ($time > 180 ? round($time / 60, 2).'mins' : "$time sec");
222
223
        $output->writeln("<info>Done in $time using $memory Mb of memory</info>");
224
    }
225
226
    /**
227
     * Anonmyze a specific table and display info about the job
228
     *
229
     * @param  string $table
230
     */
231
    private function anonymizeTable(string $table): void
232
    {
233
        $total = $this->getTotal($table);
234
        if ($total === 0) {
235
            $this->output->writeln("<info>$table is empty</info>");
236
237
            return;
238
        }
239
240
        $bar = new ProgressBar($this->output, $total);
241
        $bar->setRedrawFrequency($total > 100 ? 100 : 0);
242
243
        $this->output->writeln("<info>Anonymizing $table</info>");
244
245
        try {
246
            $queries = $this->db->setLimit($total)->processEntity(
247
                $table,
248
                function () use ($bar) {
249
                    $bar->advance();
250
                }
251
            );
252
            // @codeCoverageIgnoreStart
253
        } catch (\Exception $e) {
254
            $msg = "<error>Error anonymizing $table. Message was : ".$e->getMessage()."</error>";
255
            $this->output->writeln(PHP_EOL.$msg.PHP_EOL);
256
257
            return;
258
        }
259
        // @codeCoverageIgnoreEnd
260
261
        $this->output->writeln(PHP_EOL);
262
263
        if ($this->input->getOption('sql')) {
264
            $this->output->writeln('<comment>Queries:</comment>');
265
            $this->output->writeln(implode(PHP_EOL, $queries));
266
            $this->output->writeln(PHP_EOL);
267
        }
268
    }
269
270
271
    /**
272
     * Define the total number of records to process for progress bar
273
     *
274
     * @param  string $table
275
     *
276
     * @return int
277
     */
278
    private function getTotal(string $table): int
279
    {
280
        $limit = (int) $this->input->getOption('limit');
281
        $config = $this->reader->getEntityConfig($table);
282
        if ($config['action'] === 'insert') {
283
            return empty($limit) ? 100 : $limit;
284
        }
285
286
        $rows = (new DBUtils($this->db->getConn()))->countResults($table);
287
        if (empty($limit)) {
288
            return $rows;
289
        }
290
291
        if (!empty($limit) && $limit > $rows) {
292
            return $rows;
293
        }
294
295
        return $limit;
296
    }
297
}
298