|
1
|
|
|
<?php |
|
2
|
|
|
//---------------------------------------------------------------------------------------------------------------------- |
|
3
|
|
|
namespace SetBased\Audit\MySql; |
|
4
|
|
|
|
|
5
|
|
|
use SetBased\Audit\MySql\Metadata\TableColumnsMetadata; |
|
6
|
|
|
use SetBased\Audit\MySql\Sql\AlterAuditTableAddColumns; |
|
7
|
|
|
use SetBased\Audit\MySql\Sql\CreateAuditTable; |
|
8
|
|
|
use SetBased\Audit\MySql\Sql\CreateAuditTrigger; |
|
9
|
|
|
use SetBased\Helper\CodeStore\MySqlCompoundSyntaxCodeStore; |
|
10
|
|
|
use SetBased\Stratum\MySql\StaticDataLayer; |
|
11
|
|
|
use SetBased\Stratum\Style\StratumStyle; |
|
12
|
|
|
|
|
13
|
|
|
//---------------------------------------------------------------------------------------------------------------------- |
|
14
|
|
|
/** |
|
15
|
|
|
* Class for executing SQL statements and retrieving metadata from MySQL. |
|
16
|
|
|
*/ |
|
17
|
|
|
class AuditDataLayer |
|
18
|
|
|
{ |
|
19
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
20
|
|
|
/** |
|
21
|
|
|
* The connection to the MySQL instance. |
|
22
|
|
|
* |
|
23
|
|
|
* @var StaticDataLayer |
|
24
|
|
|
*/ |
|
25
|
|
|
private static $dl; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* The Output decorator. |
|
29
|
|
|
* |
|
30
|
|
|
* @var StratumStyle |
|
31
|
|
|
*/ |
|
32
|
|
|
private static $io; |
|
33
|
|
|
|
|
34
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
35
|
|
|
/** |
|
36
|
|
|
* Adds new columns to an audit table. |
|
37
|
|
|
* |
|
38
|
|
|
* @param string $auditSchemaName The name of audit schema. |
|
39
|
|
|
* @param string $tableName The name of the table. |
|
40
|
|
|
* @param TableColumnsMetadata $columns The metadata of the new columns. |
|
41
|
|
|
*/ |
|
42
|
8 |
|
public static function addNewColumns($auditSchemaName, $tableName, $columns) |
|
43
|
|
|
{ |
|
44
|
|
|
// Return immediately if there are no columns to add. |
|
45
|
8 |
|
if ($columns->getNumberOfColumns()==0) return; |
|
46
|
|
|
|
|
47
|
|
|
$helper = new AlterAuditTableAddColumns($auditSchemaName, $tableName, $columns); |
|
48
|
|
|
$sql = $helper->buildStatement(); |
|
49
|
|
|
|
|
50
|
|
|
self::executeNone($sql); |
|
51
|
|
|
} |
|
52
|
|
|
|
|
53
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
54
|
|
|
/** |
|
55
|
|
|
* Connects to a MySQL instance. |
|
56
|
|
|
* |
|
57
|
|
|
* Wrapper around [mysqli::__construct](http://php.net/manual/mysqli.construct.php), however on failure an exception |
|
58
|
|
|
* is thrown. |
|
59
|
|
|
* |
|
60
|
|
|
* @param string $host The hostname. |
|
61
|
|
|
* @param string $user The MySQL user name. |
|
62
|
|
|
* @param string $passWord The password. |
|
63
|
|
|
* @param string $database The default database. |
|
64
|
|
|
* @param int $port The port number. |
|
65
|
|
|
*/ |
|
66
|
9 |
|
public static function connect($host, $user, $passWord, $database, $port = 3306) |
|
67
|
|
|
{ |
|
68
|
9 |
|
self::$dl = new StaticDataLayer(); |
|
69
|
|
|
|
|
70
|
9 |
|
self::$dl->connect($host, $user, $passWord, $database, $port); |
|
71
|
9 |
|
} |
|
72
|
|
|
|
|
73
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
74
|
|
|
/** |
|
75
|
|
|
* Creates an audit table. |
|
76
|
|
|
* |
|
77
|
|
|
* @param string $dataSchemaName The name of the data schema. |
|
78
|
|
|
* @param string $auditSchemaName The name of the audit schema. |
|
79
|
|
|
* @param string $tableName The name of the table. |
|
80
|
|
|
* @param TableColumnsMetadata $columns The metadata of the columns of the audit table (i.e. the audit |
|
81
|
|
|
* columns and columns of the data table). |
|
82
|
|
|
*/ |
|
83
|
5 |
|
public static function createAuditTable($dataSchemaName, $auditSchemaName, $tableName, $columns) |
|
84
|
|
|
{ |
|
85
|
5 |
|
$helper = new CreateAuditTable($dataSchemaName, $auditSchemaName, $tableName, $columns); |
|
86
|
5 |
|
$sql = $helper->buildStatement(); |
|
87
|
|
|
|
|
88
|
5 |
|
self::executeNone($sql); |
|
89
|
5 |
|
} |
|
90
|
|
|
|
|
91
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
92
|
|
|
/** |
|
93
|
|
|
* Creates a trigger on a table. |
|
94
|
|
|
* |
|
95
|
|
|
* @param string $dataSchemaName The name of the data schema. |
|
96
|
|
|
* @param string $auditSchemaName The name of the audit schema. |
|
97
|
|
|
* @param string $tableName The name of the table. |
|
98
|
|
|
* @param string $triggerAction The trigger action (i.e. INSERT, UPDATE, or DELETE). |
|
99
|
|
|
* @param string $triggerName The name of the trigger. |
|
100
|
|
|
* @param TableColumnsMetadata $auditColumns The audit table columns. |
|
101
|
|
|
* @param TableColumnsMetadata $tableColumns The data table columns. |
|
102
|
|
|
* @param string $skipVariable The skip variable. |
|
103
|
|
|
* @param string[] $additionSql Additional SQL statements. |
|
104
|
|
|
*/ |
|
105
|
8 |
|
public static function createAuditTrigger($dataSchemaName, |
|
106
|
|
|
$auditSchemaName, |
|
107
|
|
|
$tableName, |
|
108
|
|
|
$triggerAction, |
|
109
|
|
|
$triggerName, |
|
110
|
|
|
$auditColumns, |
|
111
|
|
|
$tableColumns, |
|
112
|
|
|
$skipVariable, |
|
113
|
|
|
$additionSql) |
|
114
|
|
|
{ |
|
115
|
8 |
|
$helper = new CreateAuditTrigger($dataSchemaName, |
|
116
|
8 |
|
$auditSchemaName, |
|
117
|
8 |
|
$tableName, |
|
118
|
8 |
|
$triggerAction, |
|
119
|
8 |
|
$triggerName, |
|
120
|
8 |
|
$auditColumns, |
|
121
|
8 |
|
$tableColumns, |
|
122
|
8 |
|
$skipVariable, |
|
123
|
8 |
|
$additionSql); |
|
124
|
8 |
|
$sql = $helper->buildStatement(); |
|
125
|
|
|
|
|
126
|
8 |
|
self::executeNone($sql); |
|
127
|
8 |
|
} |
|
128
|
|
|
|
|
129
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
130
|
|
|
/** |
|
131
|
|
|
* Create temp table for getting column type information for audit columns. |
|
132
|
|
|
* |
|
133
|
|
|
* @param string $schemaName The name of the table schema. |
|
134
|
|
|
* @param string $tableName The table name. |
|
135
|
|
|
* @param array[] $auditColumns Audit columns from config file. |
|
136
|
|
|
*/ |
|
137
|
8 |
|
public static function createTemporaryTable($schemaName, $tableName, $auditColumns) |
|
138
|
|
|
{ |
|
139
|
8 |
|
$sql = new MySqlCompoundSyntaxCodeStore(); |
|
140
|
8 |
|
$sql->append(sprintf('create table `%s`.`%s` (', $schemaName, $tableName)); |
|
141
|
8 |
|
foreach ($auditColumns as $column) |
|
142
|
|
|
{ |
|
143
|
8 |
|
$sql->append(sprintf('%s %s', $column['column_name'], $column['column_type'])); |
|
144
|
8 |
|
if (end($auditColumns)!==$column) |
|
145
|
8 |
|
{ |
|
146
|
8 |
|
$sql->appendToLastLine(','); |
|
147
|
8 |
|
} |
|
148
|
8 |
|
} |
|
149
|
8 |
|
$sql->append(')'); |
|
150
|
|
|
|
|
151
|
8 |
|
self::executeNone($sql->getCode()); |
|
152
|
8 |
|
} |
|
153
|
|
|
|
|
154
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
155
|
|
|
/** |
|
156
|
|
|
* Closes the connection to the MySQL instance, if connected. |
|
157
|
|
|
*/ |
|
158
|
9 |
|
public static function disconnect() |
|
159
|
|
|
{ |
|
160
|
9 |
|
if (self::$dl!==null) |
|
161
|
9 |
|
{ |
|
162
|
9 |
|
self::$dl->disconnect(); |
|
163
|
9 |
|
self::$dl = null; |
|
164
|
9 |
|
} |
|
165
|
9 |
|
} |
|
166
|
|
|
|
|
167
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
168
|
|
|
/** |
|
169
|
|
|
* Drop table. |
|
170
|
|
|
* |
|
171
|
|
|
* @param string $schemaName The name of the table schema. |
|
172
|
|
|
* @param string $tableName The name of the table. |
|
173
|
|
|
*/ |
|
174
|
8 |
|
public static function dropTemporaryTable($schemaName, $tableName) |
|
175
|
|
|
{ |
|
176
|
8 |
|
$sql = sprintf('drop table `%s`.`%s`', $schemaName, $tableName); |
|
177
|
|
|
|
|
178
|
8 |
|
self::executeNone($sql); |
|
179
|
8 |
|
} |
|
180
|
|
|
|
|
181
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
182
|
|
|
/** |
|
183
|
|
|
* Drops a trigger. |
|
184
|
|
|
* |
|
185
|
|
|
* @param string $triggerSchema The name of the trigger schema. |
|
186
|
|
|
* @param string $triggerName The mame of trigger. |
|
187
|
|
|
*/ |
|
188
|
3 |
|
public static function dropTrigger($triggerSchema, $triggerName) |
|
189
|
|
|
{ |
|
190
|
3 |
|
$sql = sprintf('drop trigger `%s`.`%s`', $triggerSchema, $triggerName); |
|
191
|
|
|
|
|
192
|
3 |
|
self::executeNone($sql); |
|
193
|
3 |
|
} |
|
194
|
|
|
|
|
195
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
196
|
|
|
/** |
|
197
|
|
|
* @param string $query The SQL statement. |
|
198
|
|
|
* |
|
199
|
|
|
* @return int The number of affected rows (if any). |
|
200
|
|
|
*/ |
|
201
|
8 |
|
public static function executeNone($query) |
|
202
|
|
|
{ |
|
203
|
8 |
|
self::logQuery($query); |
|
204
|
|
|
|
|
205
|
8 |
|
return self::$dl->executeNone($query); |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
209
|
|
|
/** |
|
210
|
|
|
* Executes a query that returns 0 or 1 row. |
|
211
|
|
|
* Throws an exception if the query selects 2 or more rows. |
|
212
|
|
|
* |
|
213
|
|
|
* @param string $query The SQL statement. |
|
214
|
|
|
* |
|
215
|
|
|
* @return array|null The selected row. |
|
216
|
|
|
*/ |
|
217
|
|
|
public static function executeRow0($query) |
|
218
|
|
|
{ |
|
219
|
|
|
self::logQuery($query); |
|
220
|
|
|
|
|
221
|
|
|
return self::$dl->executeRow0($query); |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
225
|
|
|
/** |
|
226
|
|
|
* Executes a query that returns 1 and only 1 row. |
|
227
|
|
|
* Throws an exception if the query selects none, 2 or more rows. |
|
228
|
|
|
* |
|
229
|
|
|
* @param string $query The SQL statement. |
|
230
|
|
|
* |
|
231
|
|
|
* @return array The selected row. |
|
232
|
|
|
*/ |
|
233
|
5 |
|
public static function executeRow1($query) |
|
234
|
|
|
{ |
|
235
|
5 |
|
self::logQuery($query); |
|
236
|
|
|
|
|
237
|
5 |
|
return self::$dl->executeRow1($query); |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
241
|
|
|
/** |
|
242
|
|
|
* Executes a query that returns 0 or more rows. |
|
243
|
|
|
* |
|
244
|
|
|
* @param string $query The SQL statement. |
|
245
|
|
|
* |
|
246
|
|
|
* @return \array[] |
|
247
|
|
|
*/ |
|
248
|
9 |
|
public static function executeRows($query) |
|
249
|
|
|
{ |
|
250
|
9 |
|
self::logQuery($query); |
|
251
|
|
|
|
|
252
|
9 |
|
return self::$dl->executeRows($query); |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
256
|
|
|
/** |
|
257
|
|
|
* Executes a query that returns 0 or 1 row. |
|
258
|
|
|
* Throws an exception if the query selects 2 or more rows. |
|
259
|
|
|
* |
|
260
|
|
|
* @param string $query The SQL statement. |
|
261
|
|
|
* |
|
262
|
|
|
* @return int|string|null The selected row. |
|
263
|
|
|
*/ |
|
264
|
|
|
public static function executeSingleton0($query) |
|
265
|
|
|
{ |
|
266
|
|
|
self::logQuery($query); |
|
267
|
|
|
|
|
268
|
|
|
return self::$dl->executeSingleton0($query); |
|
269
|
|
|
} |
|
270
|
|
|
|
|
271
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
272
|
|
|
/** |
|
273
|
|
|
* Executes a query that returns 1 and only 1 row with 1 column. |
|
274
|
|
|
* Throws an exception if the query selects none, 2 or more rows. |
|
275
|
|
|
* |
|
276
|
|
|
* @param string $query The SQL statement. |
|
277
|
|
|
* |
|
278
|
|
|
* @return int|string The selected row. |
|
279
|
|
|
*/ |
|
280
|
|
|
public static function executeSingleton1($query) |
|
281
|
|
|
{ |
|
282
|
|
|
self::logQuery($query); |
|
283
|
|
|
|
|
284
|
|
|
return self::$dl->executeSingleton1($query); |
|
285
|
|
|
} |
|
286
|
|
|
|
|
287
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
288
|
|
|
/** |
|
289
|
|
|
* Selects metadata of all columns of table. |
|
290
|
|
|
* |
|
291
|
|
|
* @param string $schemaName The name of the table schema. |
|
292
|
|
|
* @param string $tableName The name of the table. |
|
293
|
|
|
* |
|
294
|
|
|
* @return \array[] |
|
295
|
|
|
*/ |
|
296
|
8 |
View Code Duplication |
public static function getTableColumns($schemaName, $tableName) |
|
|
|
|
|
|
297
|
|
|
{ |
|
298
|
8 |
|
$sql = sprintf(' |
|
299
|
|
|
select COLUMN_NAME as column_name |
|
300
|
|
|
, COLUMN_TYPE as column_type |
|
301
|
|
|
, IS_NULLABLE as is_nullable |
|
302
|
|
|
, CHARACTER_SET_NAME as character_set_name |
|
303
|
|
|
, COLLATION_NAME as collation_name |
|
304
|
|
|
from information_schema.COLUMNS |
|
305
|
|
|
where TABLE_SCHEMA = %s |
|
306
|
|
|
and TABLE_NAME = %s |
|
307
|
8 |
|
order by ORDINAL_POSITION', |
|
308
|
8 |
|
self::$dl->quoteString($schemaName), |
|
309
|
8 |
|
self::$dl->quoteString($tableName)); |
|
310
|
|
|
|
|
311
|
8 |
|
return self::executeRows($sql); |
|
312
|
|
|
} |
|
313
|
|
|
|
|
314
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
315
|
|
|
/** |
|
316
|
|
|
* Selects table engine, character_set_name and table_collation. |
|
317
|
|
|
* |
|
318
|
|
|
* @param string $schemaName The name of the table schema. |
|
319
|
|
|
* @param string $tableName The name of the table. |
|
320
|
|
|
* |
|
321
|
|
|
* @return array |
|
322
|
|
|
*/ |
|
323
|
5 |
View Code Duplication |
public static function getTableOptions($schemaName, $tableName) |
|
|
|
|
|
|
324
|
|
|
{ |
|
325
|
5 |
|
$sql = sprintf(' |
|
326
|
|
|
SELECT t1.TABLE_COLLATION as table_collation |
|
327
|
|
|
, t1.ENGINE as engine |
|
328
|
|
|
, t2.CHARACTER_SET_NAME as character_set_name |
|
329
|
|
|
FROM information_schema.TABLES t1 |
|
330
|
|
|
inner join information_schema.COLLATION_CHARACTER_SET_APPLICABILITY t2 on t2.COLLATION_NAME = t1.TABLE_COLLATION |
|
331
|
|
|
WHERE t1.TABLE_SCHEMA = %s |
|
332
|
5 |
|
AND t1.TABLE_NAME = %s', |
|
333
|
5 |
|
self::$dl->quoteString($schemaName), |
|
334
|
5 |
|
self::$dl->quoteString($tableName)); |
|
335
|
|
|
|
|
336
|
5 |
|
return self::executeRow1($sql); |
|
337
|
|
|
} |
|
338
|
|
|
|
|
339
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
340
|
|
|
/** |
|
341
|
|
|
* Selects all triggers on a table. |
|
342
|
|
|
* |
|
343
|
|
|
* @param string $schemaName The name of the table schema. |
|
344
|
|
|
* @param string $tableName The name of the table. |
|
345
|
|
|
* |
|
346
|
|
|
* @return \array[] |
|
347
|
|
|
*/ |
|
348
|
8 |
View Code Duplication |
public static function getTableTriggers($schemaName, $tableName) |
|
|
|
|
|
|
349
|
|
|
{ |
|
350
|
8 |
|
$sql = sprintf(' |
|
351
|
|
|
select Trigger_Name as trigger_name |
|
352
|
|
|
from information_schema.TRIGGERS |
|
353
|
|
|
where TRIGGER_SCHEMA = %s |
|
354
|
|
|
and EVENT_OBJECT_TABLE = %s |
|
355
|
8 |
|
order by Trigger_Name', |
|
356
|
8 |
|
self::$dl->quoteString($schemaName), |
|
357
|
8 |
|
self::$dl->quoteString($tableName)); |
|
358
|
|
|
|
|
359
|
8 |
|
return self::executeRows($sql); |
|
360
|
|
|
} |
|
361
|
|
|
|
|
362
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
363
|
|
|
/** |
|
364
|
|
|
* Selects all table names in a schema. |
|
365
|
|
|
* |
|
366
|
|
|
* @param string $schemaName The name of the schema. |
|
367
|
|
|
* |
|
368
|
|
|
* @return \array[] |
|
369
|
|
|
*/ |
|
370
|
9 |
|
public static function getTablesNames($schemaName) |
|
371
|
|
|
{ |
|
372
|
9 |
|
$sql = sprintf(" |
|
373
|
|
|
select TABLE_NAME as table_name |
|
374
|
|
|
from information_schema.TABLES |
|
375
|
|
|
where TABLE_SCHEMA = %s |
|
376
|
|
|
and TABLE_TYPE = 'BASE TABLE' |
|
377
|
9 |
|
order by TABLE_NAME", self::$dl->quoteString($schemaName)); |
|
378
|
|
|
|
|
379
|
9 |
|
return self::executeRows($sql); |
|
380
|
|
|
} |
|
381
|
|
|
|
|
382
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
383
|
|
|
/** |
|
384
|
|
|
* Acquires a write lock on a table. |
|
385
|
|
|
* |
|
386
|
|
|
* @param string $tableName The table name. |
|
387
|
|
|
*/ |
|
388
|
8 |
|
public static function lockTable($tableName) |
|
389
|
|
|
{ |
|
390
|
8 |
|
$sql = sprintf('lock tables `%s` write', $tableName); |
|
391
|
|
|
|
|
392
|
8 |
|
self::executeNone($sql); |
|
393
|
8 |
|
} |
|
394
|
|
|
|
|
395
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
396
|
|
|
/** |
|
397
|
|
|
* Sets the Output decorator. |
|
398
|
|
|
* |
|
399
|
|
|
* @param StratumStyle $io The Output decorator. |
|
400
|
|
|
*/ |
|
401
|
9 |
|
public static function setIo($io) |
|
402
|
|
|
{ |
|
403
|
9 |
|
self::$io = $io; |
|
404
|
9 |
|
} |
|
405
|
|
|
|
|
406
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
407
|
|
|
/** |
|
408
|
|
|
* Drop table. |
|
409
|
|
|
* |
|
410
|
|
|
* @param string $schemaName The name of the table schema. |
|
411
|
|
|
* @param string $tableName The name of the table. |
|
412
|
|
|
* |
|
413
|
|
|
* @return \array[] |
|
414
|
|
|
*/ |
|
415
|
|
|
public static function showColumns($schemaName, $tableName) |
|
416
|
|
|
{ |
|
417
|
|
|
$sql = sprintf('SHOW COLUMNS FROM `%s`.`%s`', $schemaName, $tableName); |
|
418
|
|
|
|
|
419
|
|
|
return self::executeRows($sql); |
|
420
|
|
|
} |
|
421
|
|
|
|
|
422
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
423
|
|
|
/** |
|
424
|
|
|
* Releases all table locks. |
|
425
|
|
|
*/ |
|
426
|
8 |
|
public static function unlockTables() |
|
427
|
|
|
{ |
|
428
|
8 |
|
$sql = 'unlock tables'; |
|
429
|
|
|
|
|
430
|
8 |
|
self::executeNone($sql); |
|
431
|
8 |
|
} |
|
432
|
|
|
|
|
433
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
434
|
|
|
/** |
|
435
|
|
|
* Logs the query on the console. |
|
436
|
|
|
* |
|
437
|
|
|
* @param string $query The query. |
|
438
|
|
|
*/ |
|
439
|
9 |
|
private static function logQuery($query) |
|
440
|
|
|
{ |
|
441
|
9 |
|
$query = trim($query); |
|
442
|
|
|
|
|
443
|
9 |
|
if (strpos($query, "\n")!==false) |
|
444
|
9 |
|
{ |
|
445
|
|
|
// Query is a multi line query. |
|
446
|
9 |
|
self::$io->logVeryVerbose('Executing query:'); |
|
447
|
9 |
|
self::$io->logVeryVerbose('<sql>%s</sql>', $query); |
|
448
|
9 |
|
} |
|
449
|
|
|
else |
|
450
|
|
|
{ |
|
451
|
|
|
// Query is a single line query. |
|
452
|
8 |
|
self::$io->logVeryVerbose('Executing query: <sql>%s</sql>', $query); |
|
453
|
|
|
} |
|
454
|
9 |
|
} |
|
455
|
|
|
|
|
456
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
457
|
|
|
} |
|
458
|
|
|
|
|
459
|
|
|
//---------------------------------------------------------------------------------------------------------------------- |
|
460
|
|
|
|
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.