Completed
Pull Request — master (#43)
by Dima
09:15
created

AuditDiff::printMissObsoleteTables()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
ccs 0
cts 18
cp 0
rs 8.439
cc 5
eloc 16
nc 6
nop 2
crap 30
1
<?php
2
//----------------------------------------------------------------------------------------------------------------------
3
namespace SetBased\Audit\MySql;
4
5
use SetBased\Audit\MySql\Helper\DiffTableHelper;
6
use SetBased\Audit\MySql\Metadata\TableColumnsMetadata;
7
use SetBased\Exception\FallenException;
8
use SetBased\Stratum\MySql\StaticDataLayer;
9
use SetBased\Stratum\Style\StratumStyle;
10
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
11
use Symfony\Component\Console\Helper\Table;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Output\OutputInterface;
14
15
//----------------------------------------------------------------------------------------------------------------------
16
/**
17
 * Class for executing auditing actions for tables.
18
 */
19
class AuditDiff
20
{
21
  //--------------------------------------------------------------------------------------------------------------------
22
  /**
23
   * The metadata (additional) audit columns (as stored in the config file).
24
   *
25
   * @var TableColumnsMetadata
26
   */
27
  private $auditColumnsMetadata;
28
29
  /**
30
   * The names of all tables in audit schema.
31
   *
32
   * @var array
33
   */
34
  private $auditSchemaTables;
35
36
  /**
37
   * The content of the configuration file.
38
   *
39
   * @var array
40
   */
41
  private $config;
42
43
  /**
44
   * Config metadata columns.
45
   *
46
   * @var array
47
   */
48
  private $configMetadata;
49
50
  /**
51
   * The names of all tables in data schema.
52
   *
53
   * @var array
54
   */
55
  private $dataSchemaTables;
56
57
  /**
58
   * Array with columns for each table.
59
   * array [
60
   *    table_name [
61
   *            column [
62
   *                    data table type,
63
   *                    audit table type
64
   *                    ],
65
   *                      ...
66
   *               ]
67
   *       ]
68
   *
69
   * @var array[]
70
   */
71
  private $diffColumns;
72
73
  /**
74
   * If set all tables and columns are shown.
75
   *
76
   * @var boolean
77
   */
78
  private $full;
79
80
  /**
81
   * The Input interface.
82
   *
83
   * @var InputInterface
84
   */
85
  private $input;
86
87
  /**
88
   * The Output decorator.
89
   *
90
   * @var StratumStyle
91
   */
92
  private $io;
93
94
  /**
95
   * The Output interface.
96
   *
97
   * @var OutputInterface
98
   */
99
  private $output;
100
101
  //--------------------------------------------------------------------------------------------------------------------
102
  /**
103
   * Object constructor.
104
   *
105
   * @param array[]         $config         The content of the configuration file.
106
   * @param array[]         $configMetadata The content of the metadata file.
107
   * @param StratumStyle    $io             The Output decorator.
108
   * @param InputInterface  $input
109
   * @param OutputInterface $output
110
   */
111
  public function __construct(&$config, $configMetadata, $io, $input, $output)
112
  {
113
    $this->io             = $io;
114
    $this->config         = &$config;
115
    $this->configMetadata = $configMetadata;
116
    $this->input          = $input;
117
    $this->output         = $output;
118
  }
119
120
  //--------------------------------------------------------------------------------------------------------------------
121
  /**
122
   * The main method: executes the auditing actions for tables.
123
   */
124
  public function main()
125
  {
126
    // Style for column names with miss matched column types.
127
    $style = new OutputFormatterStyle(null, 'red');
128
    $this->output->getFormatter()->setStyle('mm_column', $style);
129
130
    // Style for column types of columns with miss matched column types.
131
    $style = new OutputFormatterStyle('yellow');
132
    $this->output->getFormatter()->setStyle('mm_type', $style);
133
134
    // Style for obsolete tables.
135
    $style = new OutputFormatterStyle('yellow');
136
    $this->output->getFormatter()->setStyle('obsolete_table', $style);
137
138
    // Style for missing tables.
139
    $style = new OutputFormatterStyle('red');
140
    $this->output->getFormatter()->setStyle('miss_table', $style);
141
142
    $this->full = $this->input->getOption('full');
143
144
    $this->resolveCanonicalAuditColumns();
145
146
    $this->listOfTables();
147
148
    $this->getDiff();
149
150
    $this->printDiff();
151
  }
152
153
  //--------------------------------------------------------------------------------------------------------------------
154
  /**
155
   * Writes the difference between the audit tables and metadata tables to the output.
156
   */
157
  private function diffTables()
158
  {
159
    $missTables     = [];
160
    $obsoleteTables = [];
161
    foreach ($this->config['tables'] as $tableName => $table)
162
    {
163
      $res = StaticDataLayer::searchInRowSet('table_name', $tableName, $this->auditSchemaTables);
164
      if ($table['audit'] && !isset($res))
165
      {
166
        $missTables[] = $tableName;
167
      }
168
      else if (!$table['audit'] && isset($res))
169
      {
170
        $obsoleteTables[] = $tableName;
171
      }
172
    }
173
    $this->printMissObsoleteTables('missing', $missTables);
174
    $this->printMissObsoleteTables('obsolete', $obsoleteTables);
175
  }
176
177
  //--------------------------------------------------------------------------------------------------------------------
178
  /**
179
   * Computes the difference between data and audit tables.
180
   */
181
  private function getDiff()
182
  {
183
    foreach ($this->dataSchemaTables as $table)
184
    {
185
      if ($this->config['tables'][$table['table_name']]['audit'])
186
      {
187
        $res = StaticDataLayer::searchInRowSet('table_name', $table['table_name'], $this->auditSchemaTables);
188
        if (isset($res))
189
        {
190
          $this->diffColumns[$table['table_name']] = new AuditDiffTable($this->config['database']['data_schema'],
191
                                                                        $this->config['database']['audit_schema'],
192
                                                                        $table['table_name'],
193
                                                                        $this->config['audit_columns'],
194
                                                                        $this->configMetadata['table_columns'][$table['table_name']]);
195
        }
196
      }
197
    }
198
  }
199
200
  //--------------------------------------------------------------------------------------------------------------------
201
  /**
202
   * Getting list of all tables from information_schema of database from config file.
203
   */
204
  private function listOfTables()
205
  {
206
    $this->dataSchemaTables = AuditDataLayer::getTablesNames($this->config['database']['data_schema']);
207
208
    $this->auditSchemaTables = AuditDataLayer::getTablesNames($this->config['database']['audit_schema']);
209
  }
210
211
  //--------------------------------------------------------------------------------------------------------------------
212
  /**
213
   * Writes the difference between the audit and data tables to the output.
214
   */
215
  private function printDiff()
216
  {
217
    $first = true;
218
    if (isset($this->diffColumns))
219
    {
220
      /** @var AuditDiffTable $diffTable */
221
      foreach ($this->diffColumns as $tableName => $diffTable)
222
      {
223
        $columns = $diffTable->getDiffColumns();
0 ignored issues
show
Bug introduced by
The method getDiffColumns cannot be called on $diffTable (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...
224
        // Remove matching columns unless the full option is used.
225
        if (!$this->full)
226
        {
227
          $columns = $diffTable->removeMatchingColumns();
0 ignored issues
show
Bug introduced by
The method removeMatchingColumns cannot be called on $diffTable (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...
228
        }
229
230
        if (!empty($columns))
231
        {
232
          // Add an empty line between tables.
233
          if ($first)
234
          {
235
            $first = false;
236
          }
237
          else
238
          {
239
            $this->output->writeln('');
240
          }
241
242
          // Write table name.
243
          $this->output->writeln($tableName);
244
245
          // Write table with columns.
246
          $rows = new DiffTableHelper($this->config['database']['data_schema'],
247
                                      $this->config['database']['audit_schema'],
248
                                      $tableName,
249
                                      $this->config['audit_columns'],
250
                                      $this->full);
251
          $rows->appendRows($columns);
252
          $rows->addHighlighting();
253
          $table = new Table($this->output);
254
          $table->setHeaders(['column', 'data table', 'audit table', 'config'])
255
                ->setRows($rows->getRows());
256
          $table->render();
257
        }
258
      }
259
      $this->diffTables();
260
    }
261
  }
262
263
  //--------------------------------------------------------------------------------------------------------------------
264
  /**
265
   * Print missing or obsolete tables;
266
   *
267
   * @param string  $tableType Missing or obsolete.
268
   * @param array[] $tables    Table names array.
269
   */
270
  private function printMissObsoleteTables($tableType, $tables)
271
  {
272
    if ($tables)
0 ignored issues
show
Bug Best Practice introduced by
The expression $tables of type array[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
273
    {
274
      switch ($tableType)
275
      {
276
        case 'missing':
277
          $tag = 'miss_table';
278
          $this->output->writeln('<miss_table>Missing Tables:</>');
279
          break;
280
        case 'obsolete':
281
          $tag = 'obsolete_table';
282
          $this->output->writeln('<obsolete_table>Obsolete Tables:</>');
283
          break;
284
285
        default:
286
          throw new FallenException('table type', $tableType);
287
          break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
288
      }
289
      foreach ($tables as $tableName)
290
      {
291
        $this->output->writeln(sprintf('<%s>%s</>', $tag, $tableName));
292
      }
293
    }
294
  }
295
296
  //--------------------------------------------------------------------------------------------------------------------
297
  /**
298
   * Resolves the canonical column types of the audit table columns.
299
   */
300 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...
301
  {
302
    if (empty($this->config['audit_columns']))
303
    {
304
      $this->auditColumnsMetadata = new TableColumnsMetadata();
305
    }
306
    else
307
    {
308
      $schema    = $this->config['database']['audit_schema'];
309
      $tableName = '_TMP_'.uniqid();
310
      AuditDataLayer::createTemporaryTable($schema, $tableName, $this->config['audit_columns']);
311
      $columns = AuditDataLayer::getTableColumns($schema, $tableName);
312
      AuditDataLayer::dropTemporaryTable($schema, $tableName);
313
314
      foreach ($this->config['audit_columns'] as $audit_column)
315
      {
316
        $key = StaticDataLayer::searchInRowSet('column_name', $audit_column['column_name'], $columns);
317
        if (isset($audit_column['value_type']))
318
        {
319
          $columns[$key]['value_type'] = $audit_column['value_type'];
320
        }
321
        if (isset($audit_column['expression']))
322
        {
323
          $columns[$key]['expression'] = $audit_column['expression'];
324
        }
325
      }
326
327
      $this->auditColumnsMetadata = new TableColumnsMetadata($columns, 'AuditColumnMetadata');
328
    }
329
  }
330
331
  //--------------------------------------------------------------------------------------------------------------------
332
}
333
334
//----------------------------------------------------------------------------------------------------------------------
335