Completed
Push — master ( 8e94f8...744b49 )
by P.R.
04:26
created

DiffCommand::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 0
crap 1
1
<?php
2
//----------------------------------------------------------------------------------------------------------------------
3
namespace SetBased\Audit\MySql\Command;
4
5
use SetBased\Audit\MySql\AuditDataLayer;
6
use SetBased\Audit\MySql\Helper\ColumnTypesExtended;
7
use SetBased\Audit\MySql\Helper\ColumnTypesHelper;
8
use SetBased\Audit\MySql\Helper\TableHelper;
9
use SetBased\Audit\MySql\Table\Columns;
10
use SetBased\Stratum\MySql\StaticDataLayer;
11
use SetBased\Stratum\Style\StratumStyle;
12
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
13
use Symfony\Component\Console\Helper\Table;
14
use Symfony\Component\Console\Input\InputArgument;
15
use Symfony\Component\Console\Input\InputInterface;
16
use Symfony\Component\Console\Input\InputOption;
17
use Symfony\Component\Console\Output\OutputInterface;
18
19
//----------------------------------------------------------------------------------------------------------------------
20
/**
21
 * Command for comparing data tables with audit tables.
22
 */
23
class DiffCommand extends AuditCommand
24
{
25
  //--------------------------------------------------------------------------------------------------------------------
26
  /**
27
   * Array with columns for each table.
28
   * array [
29
   *    table_name [
30
   *            column [
31
   *                    data table type,
32
   *                    audit table type
33
   *                    ],
34
   *                      ...
35
   *               ]
36
   *       ]
37
   *
38
   * @var array[]
39
   */
40
  private $diffColumns;
41
42
  /**
43
   * If set all tables and columns are shown.
44
   *
45
   * @var boolean
46
   */
47
  private $full;
48
49
  //--------------------------------------------------------------------------------------------------------------------
50
  /**
51
   * Check full full and return array without new or obsolete columns if full not set.
52
   *
53
   * @param array[] $columns The metadata of the columns of a table.
54
   *
55
   * @return array[]
56
   */
57
  private static function removeMatchingColumns($columns)
58
  {
59
    $cleaned = [];
60
    /** @var ColumnTypesHelper $column */
61
    foreach ($columns as $column)
62
    {
63
      $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...
64
      if (!isset($columnsArray['data_column_type']))
65
      {
66
        if ($columnsArray['audit_column_type']!=$columnsArray['config_column_type'])
67
        {
68
          $cleaned[] = $column;
69
        }
70
      }
71 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...
72
      {
73
        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']))
74
        {
75
          $cleaned[] = $column;
76
        }
77
      }
78 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...
79
      {
80
        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'])))
81
        {
82
          $cleaned[] = $column;
83
        }
84
      }
85
    }
86
87
    return $cleaned;
88
  }
89
90
  //--------------------------------------------------------------------------------------------------------------------
91
  /**
92
   * {@inheritdoc}
93
   */
94 1
  protected function configure()
95
  {
96 1
    $this->setName('diff')
97 1
         ->setDescription('Compares data tables and audit tables')
98 1
         ->addArgument('config file', InputArgument::OPTIONAL, 'The audit configuration file', 'etc/audit.json')
99 1
         ->addOption('full', 'f', InputOption::VALUE_NONE, 'Show all columns');
100 1
  }
101
102
  //--------------------------------------------------------------------------------------------------------------------
103
  /**
104
   * {@inheritdoc}
105
   */
106
  protected function execute(InputInterface $input, OutputInterface $output)
107
  {
108
    $this->io = new StratumStyle($input, $output);
109
    // Style for column names with miss matched column types.
110
    $style = new OutputFormatterStyle(null, 'red');
111
    $output->getFormatter()->setStyle('mm_column', $style);
112
113
    // Style for column types of columns with miss matched column types.
114
    $style = new OutputFormatterStyle('yellow');
115
    $output->getFormatter()->setStyle('mm_type', $style);
116
117
    // Style for obsolete tables.
118
    $style = new OutputFormatterStyle('yellow');
119
    $output->getFormatter()->setStyle('obsolete_table', $style);
120
121
    // Style for missing tables.
122
    $style = new OutputFormatterStyle('red');
123
    $output->getFormatter()->setStyle('miss_table', $style);
124
125
    $this->configFileName = $input->getArgument('config file');
126
    $this->readConfigFile();
127
128
    $this->full = $input->getOption('full');
129
130
    $this->connect($this->config);
131
132
    $this->auditColumnTypes();
0 ignored issues
show
Bug introduced by
The method auditColumnTypes() does not seem to exist on object<SetBased\Audit\MySql\Command\DiffCommand>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
133
134
    $this->listOfTables();
0 ignored issues
show
Bug introduced by
The method listOfTables() does not seem to exist on object<SetBased\Audit\MySql\Command\DiffCommand>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
135
136
    $this->getDiff();
137
    $this->printDiff($output);
138
  }
139
140
  //--------------------------------------------------------------------------------------------------------------------
141
  /**
142
   * Add not null to audit columns if it not nullable.
143
   *
144
   * @param array $theColumns Audit columns.
145
   *
146
   * @return array
147
   */
148
  private function addNotNull($theColumns)
149
  {
150
    $modifiedColumns = [];
151
    foreach ($theColumns as $column)
152
    {
153
      $modifiedColumn = $column;
154
      $auditColumn    = StaticDataLayer::searchInRowSet('column_name', $modifiedColumn['column_name'], $this->config['audit_columns']);
155
      if (isset($auditColumn))
156
      {
157
        if ($modifiedColumn['is_nullable']==='NO')
158
        {
159
          $modifiedColumn['column_type'] = sprintf('%s not null', $modifiedColumn['column_type']);
160
        }
161
      }
162
      $modifiedColumns[] = $modifiedColumn;
163
    }
164
165
    return $modifiedColumns;
166
  }
167
168
  //--------------------------------------------------------------------------------------------------------------------
169
  /**
170
   * Get the difference between data and audit tables.
171
   *
172
   * @param Columns $dataColumns  The table columns from data schema.
173
   * @param Columns $auditColumns The table columns from audit schema.
174
   *
175
   * @return \array[]
0 ignored issues
show
Documentation introduced by
Should the return type not be ColumnTypesExtended?

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...
176
   */
177
  private function createDiffArray($dataColumns, $auditColumns)
178
  {
179
    $diff = new ColumnTypesExtended($this->config['audit_columns'], $auditColumns, $dataColumns);
180
181
    return $diff;
182
  }
183
184
  //--------------------------------------------------------------------------------------------------------------------
185
  /**
186
   * Writes the difference between the audit tables and metadata tables to the output.
187
   *
188
   * @param OutputInterface $output The output.
189
   */
190
  private function diffTables($output)
191
  {
192
    foreach ($this->config['tables'] as $tableName => $table)
193
    {
194
      $res = StaticDataLayer::searchInRowSet('table_name', $tableName, $this->auditSchemaTables);
0 ignored issues
show
Bug introduced by
The property auditSchemaTables does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
195
      if ($table['audit'] && !isset($res))
196
      {
197
        $output->writeln(sprintf('<miss_table>%s</>', $tableName));
198
      }
199
      else if (!$table['audit'] && isset($res))
200
      {
201
        $output->writeln(sprintf('<obsolete_table>%s</>', $tableName));
202
      }
203
    }
204
  }
205
206
  //--------------------------------------------------------------------------------------------------------------------
207
  /**
208
   * Computes the difference between data and audit tables.
209
   */
210
  private function getDiff()
211
  {
212
    foreach ($this->dataSchemaTables as $table)
0 ignored issues
show
Bug introduced by
The property dataSchemaTables does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
213
    {
214
      if ($this->config['tables'][$table['table_name']]['audit'])
215
      {
216
        $res = StaticDataLayer::searchInRowSet('table_name', $table['table_name'], $this->auditSchemaTables);
217
        if (isset($res))
218
        {
219
          $dataColumns  = new Columns(AuditDataLayer::getTableColumns($this->config['database']['data_schema'], $table['table_name']));
220
          $auditColumns = AuditDataLayer::getTableColumns($this->config['database']['audit_schema'], $table['table_name']);
221
          $auditColumns = $this->addNotNull($auditColumns);
222
          $auditColumns = new Columns($auditColumns);
223
224
          $this->diffColumns[$table['table_name']] = $this->createDiffArray($dataColumns, $auditColumns);
225
        }
226
      }
227
    }
228
  }
229
230
  //--------------------------------------------------------------------------------------------------------------------
231
  /**
232
   * Writes the difference between the audit and data tables to the output.
233
   *
234
   * @param OutputInterface $output The output.
235
   */
236
  private function printDiff($output)
237
  {
238
    $first = true;
239
    if (isset($this->diffColumns))
240
    {
241
      /**
242
       * @var ColumnTypesExtended $columns
243
       */
244
      foreach ($this->diffColumns as $tableName => $columns)
245
      {
246
        $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...
247
        // Remove matching columns unless the full option is used.
248
        if (!$this->full)
249
        {
250
          /** @var array[] $columns */
251
          $columns = self::removeMatchingColumns($columns);
252
        }
253
254
        if (!empty($columns))
255
        {
256
          // Add an empty line between tables.
257
          if ($first)
258
          {
259
            $first = false;
260
          }
261
          else
262
          {
263
            $output->writeln('');
264
          }
265
266
          // Write table name.
267
          $output->writeln($tableName);
268
269
          // Write table with columns.
270
          $rows = new TableHelper($this->config['database']['data_schema'], $this->config['database']['audit_schema'], $tableName, $this->config['audit_columns'], $this->full);
271
          $rows->appendRows($columns);
272
          $rows->addHighlighting();
273
          $table = new Table($output);
274
          $table->setHeaders(['column', 'data table', 'audit table', 'config'])
275
                ->setRows($rows->getRows());
276
          $table->render();
277
        }
278
      }
279
    }
280
    $this->diffTables($output);
281
  }
282
283
  //--------------------------------------------------------------------------------------------------------------------
284
}
285
286
//----------------------------------------------------------------------------------------------------------------------
287