Completed
Push — master ( b7d034...3d835d )
by P.R.
03:58
created

Table::getTriggerName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
//----------------------------------------------------------------------------------------------------------------------
3
namespace SetBased\Audit\MySql\Table;
4
5
use SetBased\Audit\MySql\DataLayer;
6
use SetBased\Stratum\Style\StratumStyle;
7
8
//--------------------------------------------------------------------------------------------------------------------
9
/**
10
 * Class for metadata of tables.
11
 */
12
class Table
13
{
14
  //--------------------------------------------------------------------------------------------------------------------
15
  /**
16
   * The unique alias for this data table.
17
   *
18
   * @var string
19
   */
20
  private $alias;
21
22
  /**
23
   * The metadata (additional) audit columns (as stored in the config file).
24
   *
25
   * @var Columns
26
   */
27
  private $auditColumns;
28
29
  /**
30
   * The name of the schema with the audit tables.
31
   *
32
   * @var string
33
   */
34
  private $auditSchemaName;
35
36
  /**
37
   * The name of the schema with the data tables.
38
   *
39
   * @var string
40
   */
41
  private $dataSchemaName;
42
43
  /**
44
   * The metadata of the columns of the data table as stored in the config file.
45
   *
46
   * @var Columns
47
   */
48
  private $dataTableColumnsConfig;
49
50
  /**
51
   * The metadata of the columns of the data table retrieved from information_schema.
52
   *
53
   * @var Columns
54
   */
55
  private $dataTableColumnsDatabase;
56
57
  /**
58
   * The output decorator
59
   *
60
   * @var StratumStyle
61
   */
62
  private $io;
63
64
  /**
65
   * The skip variable for triggers.
66
   *
67
   * @var string
68
   */
69
  private $skipVariable;
70
71
  /**
72
   * The name of this data table.
73
   *
74
   * @var string
75
   */
76
  private $tableName;
77
78
  //--------------------------------------------------------------------------------------------------------------------
79
  /**
80
   * Object constructor.
81
   *
82
   * @param StratumStyle $io                    The output for log messages.
83
   * @param string       $tableName             The table name.
84
   * @param string       $dataSchema            The name of the schema with data tables.
85
   * @param string       $auditSchema           The name of the schema with audit tables.
86
   * @param array[]      $configColumnsMetadata The columns of the data table as stored in the config file.
87
   * @param array[]      $auditColumnsMetadata  The columns of the audit table as stored in the config file.
88
   * @param string       $alias                 An unique alias for this table.
89
   * @param string       $skipVariable          The skip variable
90
   */
91 4
  public function __construct($io,
92
                              $tableName,
93
                              $dataSchema,
94
                              $auditSchema,
95
                              $configColumnsMetadata,
96
                              $auditColumnsMetadata,
97
                              $alias,
98
                              $skipVariable)
99
  {
100 4
    $this->io                       = $io;
101 4
    $this->tableName                = $tableName;
102 4
    $this->dataTableColumnsConfig   = new Columns($configColumnsMetadata);
103 4
    $this->dataSchemaName           = $dataSchema;
104 4
    $this->auditSchemaName          = $auditSchema;
105 4
    $this->dataTableColumnsDatabase = new Columns($this->getColumnsFromInformationSchema());
106 4
    $this->auditColumns             = new Columns($auditColumnsMetadata);
107 4
    $this->alias                    = $alias;
108 4
    $this->skipVariable             = $skipVariable;
109 4
  }
110
111
  //--------------------------------------------------------------------------------------------------------------------
112
  /**
113
   * Returns a random alias for this table.
114
   *
115
   * @return string
116
   */
117
  public static function getRandomAlias()
118
  {
119
    return uniqid();
120
  }
121
122
  //--------------------------------------------------------------------------------------------------------------------
123
  /**
124
   * Creates missing audit table for this table.
125
   */
126 3
  public function createMissingAuditTable()
127
  {
128 3
    $this->io->logInfo('Creating audit table <dbo>%s.%s<dbo>', $this->auditSchemaName, $this->tableName);
129
130 3
    $columns = Columns::combine($this->auditColumns, $this->dataTableColumnsDatabase);
131 3
    DataLayer::createAuditTable($this->dataSchemaName, $this->auditSchemaName, $this->tableName, $columns);
132 3
  }
133
134
  //--------------------------------------------------------------------------------------------------------------------
135
  /**
136
   * Creates audit triggers on this table.
137
   *
138
   * @param string[] $additionalSql Additional SQL statements to be include in triggers.
139
   */
140 4
  public function createTriggers($additionalSql)
141
  {
142
    // Lock the table to prevent insert, updates, or deletes between dropping and creating triggers.
143 4
    $this->lockTable($this->tableName);
144
145
    // Drop all triggers, if any.
146 4
    $this->dropTriggers();
147
148
    // Create or recreate the audit triggers.
149 4
    $this->createTableTrigger('INSERT', $this->skipVariable, $additionalSql);
150 4
    $this->createTableTrigger('UPDATE', $this->skipVariable, $additionalSql);
151 4
    $this->createTableTrigger('DELETE', $this->skipVariable, $additionalSql);
152
153
    // Insert, updates, and deletes are no audited again. So, release lock on the table.
154 4
    $this->unlockTables();
155 4
  }
156
157
  //--------------------------------------------------------------------------------------------------------------------
158
  /**
159
   * Returns the name of this table.
160
   *
161
   * @return string
162
   */
163 4
  public function getTableName()
164
  {
165 4
    return $this->tableName;
166
  }
167
168
  //--------------------------------------------------------------------------------------------------------------------
169
  /**
170
   * Main function for work with table.
171
   *
172
   * @param string[] $additionalSql Additional SQL statements to be include in triggers.
173
   *
174
   * @return \array[] Columns for config file
0 ignored issues
show
Documentation introduced by
Should the return type not be array<string,Columns>?

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...
175
   */
176 4
  public function main($additionalSql)
177
  {
178 4
    $comparedColumns = null;
179 4
    if (isset($this->dataTableColumnsConfig))
180 4
    {
181 4
      $comparedColumns = $this->getTableColumnInfo();
182 4
    }
183
184 4
    $newColumns      = $comparedColumns['new_columns']->getColumns();
185 4
    $obsoleteColumns = $comparedColumns['obsolete_columns']->getColumns();
186 4
    if (empty($newColumns) && empty($obsoleteColumns))
187 4
    {
188 4
      $alteredColumns = $comparedColumns['altered_columns']->getColumns();
189 4
      if (empty($alteredColumns))
190 4
      {
191 4
        $this->createTriggers($additionalSql);
192 4
      }
193 4
    }
194
195 4
    return $comparedColumns;
196
  }
197
198
  //--------------------------------------------------------------------------------------------------------------------
199
  /**
200
   * Adds new columns to audit table.
201
   *
202
   * @param Columns $columns Columns array
203
   */
204 4
  private function addNewColumns($columns)
205
  {
206 4
    DataLayer::addNewColumns($this->auditSchemaName, $this->tableName, $columns);
207 4
  }
208
209
  //--------------------------------------------------------------------------------------------------------------------
210
  /**
211
   * Creates a triggers for this table.
212
   *
213
   * @param string      $action      The trigger action (INSERT, DELETE, or UPDATE).
214
   * @param string|null $skipVariable
215
   * @param string[]    $additionSql The additional SQL statements to be included in triggers.
216
   */
217 4
  private function createTableTrigger($action, $skipVariable, $additionSql)
218
  {
219 4
    $triggerName = $this->getTriggerName($action);
220
221 4
    $this->io->logVerbose('Creating trigger <dbo>%s.%s</dbo> on table <dbo>%s.%s</dbo>',
222 4
                          $this->dataSchemaName,
223 4
                          $triggerName,
224 4
                          $this->dataSchemaName,
225 4
                          $this->tableName);
226
227 4
    DataLayer::createAuditTrigger($this->dataSchemaName,
228 4
                                  $this->auditSchemaName,
229 4
                                  $this->tableName,
230 4
                                  $triggerName,
231 4
                                  $action,
232 4
                                  $this->auditColumns,
233 4
                                  $this->dataTableColumnsDatabase,
234 4
                                  $skipVariable,
235 4
                                  $additionSql);
236 4
  }
237
238
  //--------------------------------------------------------------------------------------------------------------------
239
  /**
240
   * Drops all triggers from this table.
241
   */
242 4
  private function dropTriggers()
243
  {
244 4
    $triggers = DataLayer::getTableTriggers($this->dataSchemaName, $this->tableName);
245 4
    foreach ($triggers as $trigger)
246
    {
247 1
      $this->io->logVerbose('Dropping trigger <dbo>%s</dbo> on <dbo>%s.%s</dbo>',
248 1
                            $trigger['trigger_name'],
249 1
                            $this->dataSchemaName,
250 1
                            $this->tableName);
251
252 1
      DataLayer::dropTrigger($this->dataSchemaName, $trigger['trigger_name']);
253 4
    }
254 4
  }
255
256
  //--------------------------------------------------------------------------------------------------------------------
257
  /**
258
   * Compares columns types from table in data_schema with columns in config file.
259
   *
260
   * @return Columns
261
   */
262 4
  private function getAlteredColumns()
263
  {
264 4
    $alteredColumnsTypes = Columns::differentColumnTypes($this->dataTableColumnsDatabase,
265 4
                                                         $this->dataTableColumnsConfig);
266
267 4
    return $alteredColumnsTypes;
268
  }
269
270
  //--------------------------------------------------------------------------------------------------------------------
271
  /**
272
   * Selects and returns the metadata of the columns of this table from information_schema.
273
   *
274
   * @return array[]
0 ignored issues
show
Documentation introduced by
Should the return type not be \array[]?

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...
275
   */
276 4
  private function getColumnsFromInformationSchema()
277
  {
278 4
    $result = DataLayer::getTableColumns($this->dataSchemaName, $this->tableName);
279
280 4
    return $result;
281
  }
282
283
  //--------------------------------------------------------------------------------------------------------------------
284
  /**
285
   * Compare columns from table in data_schema with columns in config file.
286
   *
287
   * @return \array[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array<string,Columns>?

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...
288
   */
289 4
  private function getTableColumnInfo()
290
  {
291 4
    $columnActual  = new Columns(DataLayer::getTableColumns($this->auditSchemaName, $this->tableName));
292 4
    $columnsConfig = Columns::combine($this->auditColumns, $this->dataTableColumnsConfig);
293 4
    $columnsTarget = Columns::combine($this->auditColumns, $this->dataTableColumnsDatabase);
294
295 4
    $newColumns      = Columns::notInOtherSet($columnsTarget, $columnActual);
296 4
    $obsoleteColumns = Columns::notInOtherSet($columnsConfig, $columnsTarget);
297 4
    $alteredColumns  = $this->getAlteredColumns();
298
299 4
    $this->loggingColumnInfo($newColumns, $obsoleteColumns, $alteredColumns);
300 4
    $this->addNewColumns($newColumns);
301
302 4
    return ['full_columns'     => $this->getTableColumnsFromConfig($newColumns, $obsoleteColumns),
303 4
            'new_columns'      => $newColumns,
304 4
            'obsolete_columns' => $obsoleteColumns,
305 4
            'altered_columns'  => $alteredColumns];
306
  }
307
308
  //--------------------------------------------------------------------------------------------------------------------
309
  /**
310
   * Check for know what columns array returns.
311
   *
312
   * @param Columns $newColumns
313
   * @param Columns $obsoleteColumns
314
   *
315
   * @return Columns
316
   */
317 4
  private function getTableColumnsFromConfig($newColumns, $obsoleteColumns)
318
  {
319 4
    $new      = $newColumns->getColumns();
320 4
    $obsolete = $obsoleteColumns->getColumns();
321 4
    if (!empty($new) && !empty($obsolete))
322 4
    {
323
      return $this->dataTableColumnsConfig;
324
    }
325
326 4
    return $this->dataTableColumnsDatabase;
327
  }
328
329
  //--------------------------------------------------------------------------------------------------------------------
330
  /**
331
   * Create and return trigger name.
332
   *
333
   * @param string $action Trigger on action (Insert, Update, Delete)
334
   *
335
   * @return string
336
   */
337 4
  private function getTriggerName($action)
338
  {
339 4
    return strtolower(sprintf('trg_%s_%s', $this->alias, $action));
340
  }
341
342
  //--------------------------------------------------------------------------------------------------------------------
343
  /**
344
   * Lock the table to prevent insert, updates, or deletes between dropping and creating triggers.
345
   *
346
   * @param string $tableName Name of table
347
   */
348 4
  private function lockTable($tableName)
349
  {
350 4
    DataLayer::lockTable($tableName);
351 4
  }
352
353
  //--------------------------------------------------------------------------------------------------------------------
354
  /**
355
   * Logging new and obsolete columns.
356
   *
357
   * @param Columns $newColumns
358
   * @param Columns $obsoleteColumns
359
   * @param Columns $alteredColumns
360
   */
361 4
  private function loggingColumnInfo($newColumns, $obsoleteColumns, $alteredColumns)
362
  {
363 4
    $new      = $newColumns->getColumns();
364 4
    $obsolete = $obsoleteColumns->getColumns();
365 4
    if (!empty($new) && !empty($obsolete))
366 4
    {
367
      $this->io->logInfo('Found both new and obsolete columns for table %s', $this->tableName);
368
      $this->io->logInfo('No action taken');
369
370
      /** @var ColumnType $column */
371
      foreach ($newColumns->getColumns() as $column)
372
      {
373
        $this->io->logInfo('New column %s', $column->getProperty('column_name'));
374
      }
375
      foreach ($obsoleteColumns->getColumns() as $column)
376
      {
377
        $this->io->logInfo('Obsolete column %s', $column->getProperty('column_name'));
378
      }
379
    }
380
381
    /** @var ColumnType $column */
382 4
    foreach ($obsoleteColumns->getColumns() as $column)
383
    {
384
      $this->io->logInfo('Obsolete column %s.%s', $this->tableName, $column->getProperty('column_name'));
385 4
    }
386
387
    /** @var ColumnType $column */
388 4
    foreach ($newColumns->getColumns() as $column)
389
    {
390
      $this->io->logInfo('New column %s.%s', $this->tableName, $column->getProperty('column_name'));
391 4
    }
392
393 4
    foreach ($alteredColumns->getColumns() as $column)
394
    {
395
      $this->io->logInfo('Type of <dbo>%s.%s</dbo> has been altered to <dbo>%s</dbo>',
396
                         $this->tableName,
397
                         $column['column_name'],
398
                         $column['column_type']);
399 4
    }
400 4
  }
401
402
  //--------------------------------------------------------------------------------------------------------------------
403
  /**
404
   * Releases all table locks.
405
   */
406 4
  private function unlockTables()
407
  {
408 4
    DataLayer::unlockTables();
409 4
  }
410
411
  //--------------------------------------------------------------------------------------------------------------------
412
}
413
414
//----------------------------------------------------------------------------------------------------------------------
415