Passed
Push — master ( 3ccc44...cef086 )
by Emmanuel
02:16 queued 15s
created

RunCommand::execute()   B

Complexity

Conditions 7
Paths 18

Size

Total Lines 55
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 55
ccs 34
cts 34
cp 1
rs 7.8235
c 0
b 0
f 0
cc 7
eloc 33
nc 18
nop 2
crap 7

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