Completed
Pull Request — master (#40)
by Dima
06:19
created

AuditDiff::removeMatchingColumns()   C

Complexity

Conditions 12
Paths 7

Size

Total Lines 32
Code Lines 14

Duplication

Lines 14
Ratio 43.75 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 14
loc 32
ccs 0
cts 23
cp 0
rs 5.1612
cc 12
eloc 14
nc 7
nop 1
crap 156

How to fix   Complexity   

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
namespace SetBased\Audit\MySql;
4
5
use SetBased\Audit\MySql\Helper\ColumnsExtendedContainer;
6
use SetBased\Audit\MySql\Helper\ColumnTypeExtended;
7
use SetBased\Audit\MySql\Helper\TableHelper;
8
use SetBased\Audit\MySql\Metadata\TableColumnsMetadata;
9
use SetBased\Stratum\MySql\StaticDataLayer;
10
use SetBased\Stratum\Style\StratumStyle;
11
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
12
use Symfony\Component\Console\Helper\Table;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Output\OutputInterface;
15
16
//----------------------------------------------------------------------------------------------------------------------
17
/**
18
 * Class for executing auditing actions for tables.
19
 */
20
class AuditDiff
21
{
22
  //--------------------------------------------------------------------------------------------------------------------
23
  /**
24
   * The metadata (additional) audit columns (as stored in the config file).
25
   *
26
   * @var TableColumnsMetadata
27
   */
28
  private $auditColumnsMetadata;
29
30
  /**
31
   * The names of all tables in audit schema.
32
   *
33
   * @var array
34
   */
35
  private $auditSchemaTables;
36
37
  /**
38
   * The content of the configuration file.
39
   *
40
   * @var array
41
   */
42
  private $config;
43
44
  /**
45
   * The names of all tables in data schema.
46
   *
47
   * @var array
48
   */
49
  private $dataSchemaTables;
50
51
  /**
52
   * Array with columns for each table.
53
   * array [
54
   *    table_name [
55
   *            column [
56
   *                    data table type,
57
   *                    audit table type
58
   *                    ],
59
   *                      ...
60
   *               ]
61
   *       ]
62
   *
63
   * @var array[]
64
   */
65
  private $diffColumns;
66
67
  /**
68
   * If set all tables and columns are shown.
69
   *
70
   * @var boolean
71
   */
72
  private $full;
73
74
  /**
75
   * The Input interface.
76
   *
77
   * @var InputInterface
78
   */
79
  private $input;
80
81
  /**
82
   * The Output decorator.
83
   *
84
   * @var StratumStyle
85
   */
86
  private $io;
87
88
  /**
89
   * The Output interface.
90
   *
91
   * @var OutputInterface
92
   */
93
  private $output;
94
95
  //--------------------------------------------------------------------------------------------------------------------
96
  /**
97
   * Object constructor.
98
   *
99
   * @param array[]         $config The content of the configuration file.
100
   * @param StratumStyle    $io     The Output decorator.
101
   * @param InputInterface  $input
102
   * @param OutputInterface $output
103
   */
104
  public function __construct(&$config, $io, $input, $output)
105
  {
106
    $this->io     = $io;
107
    $this->config = &$config;
108
    $this->input  = $input;
109
    $this->output = $output;
110
  }
111
112
  //--------------------------------------------------------------------------------------------------------------------
113
  /**
114
   * Check full full and return array without new or obsolete columns if full not set.
115
   *
116
   * @param array[] $columns The metadata of the columns of a table.
117
   *
118
   * @return array[]
119
   */
120
  private static function removeMatchingColumns($columns)
121
  {
122
    $cleaned = [];
123
    /** @var ColumnTypeExtended $column */
124
    foreach ($columns as $column)
125
    {
126
      $columnsArray = $column->getTypes();
0 ignored issues
show
Bug introduced by
The method getTypes cannot be called on $column (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
127
      if (!isset($columnsArray['data_column_type']))
128
      {
129
        if ($columnsArray['audit_column_type']!=$columnsArray['config_column_type'])
130
        {
131
          $cleaned[] = $column;
132
        }
133
      }
134 View Code Duplication
      elseif (!isset($columnsArray['config_column_type']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
      {
136
        if (($columnsArray['audit_column_type']!=$columnsArray['data_column_type']) || ($columnsArray['audit_character_set_name']!=$columnsArray['data_character_set_name'] || $columnsArray['audit_collation_name']!=$columnsArray['data_collation_name']))
137
        {
138
          $cleaned[] = $column;
139
        }
140
      }
141 View Code Duplication
      else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
142
      {
143
        if (($columnsArray['data_column_type']!=$columnsArray['audit_column_type'] && $columnsArray['audit_column_type']!=$columnsArray['config_column_type']) || ($columnsArray['audit_column_type']!=$columnsArray['config_column_type'] && !empty($columnsArray['config_column_type'])))
144
        {
145
          $cleaned[] = $column;
146
        }
147
      }
148
    }
149
150
    return $cleaned;
151
  }
152
153
  //--------------------------------------------------------------------------------------------------------------------
154
  /**
155
   * The main method: executes the auditing actions for tables.
156
   *
157
   * @return int The exit status.
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
158
   */
159
  public function main()
160
  {
161
    // Style for column names with miss matched column types.
162
    $style = new OutputFormatterStyle(null, 'red');
163
    $this->output->getFormatter()->setStyle('mm_column', $style);
164
165
    // Style for column types of columns with miss matched column types.
166
    $style = new OutputFormatterStyle('yellow');
167
    $this->output->getFormatter()->setStyle('mm_type', $style);
168
169
    // Style for obsolete tables.
170
    $style = new OutputFormatterStyle('yellow');
171
    $this->output->getFormatter()->setStyle('obsolete_table', $style);
172
173
    // Style for missing tables.
174
    $style = new OutputFormatterStyle('red');
175
    $this->output->getFormatter()->setStyle('miss_table', $style);
176
177
    $this->full = $this->input->getOption('full');
178
179
180
    $this->resolveCanonicalAuditColumns();
181
182
    $this->listOfTables();
183
184
    $this->getDiff();
185
    $this->printDiff();
186
  }
187
188
  //--------------------------------------------------------------------------------------------------------------------
189
  /**
190
   * Add not null to audit columns if it not nullable.
191
   *
192
   * @param array $theColumns Audit columns.
193
   *
194
   * @return array
195
   */
196
  private function addNotNull($theColumns)
197
  {
198
    $modifiedColumns = [];
199
    foreach ($theColumns as $column)
200
    {
201
      $modifiedColumn = $column;
202
      $auditColumn    = StaticDataLayer::searchInRowSet('column_name', $modifiedColumn['column_name'], $this->config['audit_columns']);
203
      if (isset($auditColumn))
204
      {
205
        if ($modifiedColumn['is_nullable']==='NO')
206
        {
207
          $modifiedColumn['column_type'] = sprintf('%s not null', $modifiedColumn['column_type']);
208
        }
209
      }
210
      $modifiedColumns[] = $modifiedColumn;
211
    }
212
213
    return $modifiedColumns;
214
  }
215
216
  //--------------------------------------------------------------------------------------------------------------------
217
  /**
218
   * Get the difference between data and audit tables.
219
   *
220
   * @param TableColumnsMetadata $dataColumns  The table columns from data schema.
221
   * @param TableColumnsMetadata $auditColumns The table columns from audit schema.
222
   *
223
   * @return \array[]
0 ignored issues
show
Documentation introduced by
Should the return type not be ColumnsExtendedContainer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
224
   */
225
  private function createDiffArray($dataColumns, $auditColumns)
226
  {
227
    $diff = new ColumnsExtendedContainer($this->config['audit_columns'], $auditColumns, $dataColumns);
228
229
    return $diff;
230
  }
231
232
  //--------------------------------------------------------------------------------------------------------------------
233
  /**
234
   * Writes the difference between the audit tables and metadata tables to the output.
235
   */
236
  private function diffTables()
237
  {
238
    foreach ($this->config['tables'] as $tableName => $table)
239
    {
240
      $res = StaticDataLayer::searchInRowSet('table_name', $tableName, $this->auditSchemaTables);
241
      if ($table['audit'] && !isset($res))
242
      {
243
        $this->output->writeln(sprintf('<miss_table>%s</>', $tableName));
244
      }
245
      else if (!$table['audit'] && isset($res))
246
      {
247
        $this->output->writeln(sprintf('<obsolete_table>%s</>', $tableName));
248
      }
249
    }
250
  }
251
252
  //--------------------------------------------------------------------------------------------------------------------
253
  /**
254
   * Computes the difference between data and audit tables.
255
   */
256
  private function getDiff()
257
  {
258
    foreach ($this->dataSchemaTables as $table)
259
    {
260
      if ($this->config['tables'][$table['table_name']]['audit'])
261
      {
262
        $res = StaticDataLayer::searchInRowSet('table_name', $table['table_name'], $this->auditSchemaTables);
263
        if (isset($res))
264
        {
265
          $dataColumns  = new TableColumnsMetadata(AuditDataLayer::getTableColumns($this->config['database']['data_schema'], $table['table_name']));
266
          $auditColumns = AuditDataLayer::getTableColumns($this->config['database']['audit_schema'], $table['table_name']);
267
          $auditColumns = $this->addNotNull($auditColumns);
268
          $auditColumns = new TableColumnsMetadata($auditColumns);
269
270
          $this->diffColumns[$table['table_name']] = $this->createDiffArray($dataColumns, $auditColumns);
271
        }
272
      }
273
    }
274
  }
275
276
  //--------------------------------------------------------------------------------------------------------------------
277
  /**
278
   * Getting list of all tables from information_schema of database from config file.
279
   */
280
  private function listOfTables()
281
  {
282
    $this->dataSchemaTables = AuditDataLayer::getTablesNames($this->config['database']['data_schema']);
283
284
    $this->auditSchemaTables = AuditDataLayer::getTablesNames($this->config['database']['audit_schema']);
285
  }
286
287
  //--------------------------------------------------------------------------------------------------------------------
288
  /**
289
   * Writes the difference between the audit and data tables to the output.
290
   */
291
  private function printDiff()
292
  {
293
    $first = true;
294
    if (isset($this->diffColumns))
295
    {
296
      /**
297
       * @var ColumnsExtendedContainer $columns
298
       */
299
      foreach ($this->diffColumns as $tableName => $columns)
300
      {
301
        $columns = $columns->getTypes();
0 ignored issues
show
Bug introduced by
The method getTypes cannot be called on $columns (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
302
        // Remove matching columns unless the full option is used.
303
        if (!$this->full)
304
        {
305
          /** @var array[] $columns */
306
          $columns = self::removeMatchingColumns($columns);
307
        }
308
309
        if (!empty($columns))
310
        {
311
          // Add an empty line between tables.
312
          if ($first)
313
          {
314
            $first = false;
315
          }
316
          else
317
          {
318
            $this->output->writeln('');
319
          }
320
321
          // Write table name.
322
          $this->output->writeln($tableName);
323
324
          // Write table with columns.
325
          $rows = new TableHelper($this->config['database']['data_schema'], $this->config['database']['audit_schema'], $tableName, $this->config['audit_columns'], $this->full);
326
          $rows->appendRows($columns);
327
          $rows->addHighlighting();
328
          $table = new Table($this->output);
329
          $table->setHeaders(['column', 'data table', 'audit table', 'config'])
330
                ->setRows($rows->getRows());
331
          $table->render();
332
        }
333
      }
334
    }
335
    $this->diffTables();
336
  }
337
338
  //--------------------------------------------------------------------------------------------------------------------
339
  /**
340
   * Resolves the canonical column types of the audit table columns.
341
   */
342 View Code Duplication
  private function resolveCanonicalAuditColumns()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
343
  {
344
    if (empty($this->config['audit_columns']))
345
    {
346
      $this->auditColumnsMetadata = new TableColumnsMetadata();
347
    }
348
    else
349
    {
350
      $schema    = $this->config['database']['audit_schema'];
351
      $tableName = '_TMP_'.uniqid();
352
      AuditDataLayer::createTemporaryTable($schema, $tableName, $this->config['audit_columns']);
353
      $columns = AuditDataLayer::getTableColumns($schema, $tableName);
354
      AuditDataLayer::dropTemporaryTable($schema, $tableName);
355
356
      foreach ($this->config['audit_columns'] as $audit_column)
357
      {
358
        $key = StaticDataLayer::searchInRowSet('column_name', $audit_column['column_name'], $columns);
359
        if (isset($audit_column['value_type']))
360
        {
361
          $columns[$key]['value_type'] = $audit_column['value_type'];
362
        }
363
        if (isset($audit_column['expression']))
364
        {
365
          $columns[$key]['expression'] = $audit_column['expression'];
366
        }
367
      }
368
369
      $this->auditColumnsMetadata = new TableColumnsMetadata($columns, 'AuditColumnMetadata');
370
    }
371
  }
372
373
  //--------------------------------------------------------------------------------------------------------------------
374
}
375
376
//----------------------------------------------------------------------------------------------------------------------
377