Total Complexity | 117 |
Total Lines | 509 |
Duplicated Lines | 11.2 % |
Coverage | 98.19% |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Comparator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Comparator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | class Comparator |
||
32 | { |
||
33 | /** |
||
34 | * @param \Doctrine\DBAL\Schema\Schema $fromSchema |
||
35 | * @param \Doctrine\DBAL\Schema\Schema $toSchema |
||
36 | * |
||
37 | * @return \Doctrine\DBAL\Schema\SchemaDiff |
||
38 | */ |
||
39 | 17 | public static function compareSchemas(Schema $fromSchema, Schema $toSchema) |
|
40 | { |
||
41 | 17 | $c = new self(); |
|
42 | |||
43 | 17 | return $c->compare($fromSchema, $toSchema); |
|
44 | } |
||
45 | |||
46 | /** |
||
47 | * Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema. |
||
48 | * |
||
49 | * The returned differences are returned in such a way that they contain the |
||
50 | * operations to change the schema stored in $fromSchema to the schema that is |
||
51 | * stored in $toSchema. |
||
52 | * |
||
53 | * @param \Doctrine\DBAL\Schema\Schema $fromSchema |
||
54 | * @param \Doctrine\DBAL\Schema\Schema $toSchema |
||
55 | * |
||
56 | * @return \Doctrine\DBAL\Schema\SchemaDiff |
||
57 | */ |
||
58 | 27 | public function compare(Schema $fromSchema, Schema $toSchema) |
|
59 | { |
||
60 | 27 | $diff = new SchemaDiff(); |
|
61 | 27 | $diff->fromSchema = $fromSchema; |
|
62 | |||
63 | 27 | $foreignKeysToTable = []; |
|
64 | |||
65 | 27 | foreach ($toSchema->getNamespaces() as $namespace) { |
|
66 | 2 | if ( ! $fromSchema->hasNamespace($namespace)) { |
|
67 | 2 | $diff->newNamespaces[$namespace] = $namespace; |
|
68 | } |
||
69 | } |
||
70 | |||
71 | 27 | foreach ($fromSchema->getNamespaces() as $namespace) { |
|
72 | 2 | if ( ! $toSchema->hasNamespace($namespace)) { |
|
73 | 2 | $diff->removedNamespaces[$namespace] = $namespace; |
|
74 | } |
||
75 | } |
||
76 | |||
77 | 27 | foreach ($toSchema->getTables() as $table) { |
|
78 | 21 | $tableName = $table->getShortestName($toSchema->getName()); |
|
79 | 21 | if ( ! $fromSchema->hasTable($tableName)) { |
|
80 | 5 | $diff->newTables[$tableName] = $toSchema->getTable($tableName); |
|
81 | } else { |
||
82 | 19 | $tableDifferences = $this->diffTable($fromSchema->getTable($tableName), $toSchema->getTable($tableName)); |
|
83 | 19 | if ($tableDifferences !== false) { |
|
84 | 21 | $diff->changedTables[$tableName] = $tableDifferences; |
|
85 | } |
||
86 | } |
||
87 | } |
||
88 | |||
89 | /* Check if there are tables removed */ |
||
90 | 27 | foreach ($fromSchema->getTables() as $table) { |
|
91 | 20 | $tableName = $table->getShortestName($fromSchema->getName()); |
|
92 | |||
93 | 20 | $table = $fromSchema->getTable($tableName); |
|
94 | 20 | if ( ! $toSchema->hasTable($tableName)) { |
|
95 | 5 | $diff->removedTables[$tableName] = $table; |
|
96 | } |
||
97 | |||
98 | // also remember all foreign keys that point to a specific table |
||
99 | 20 | foreach ($table->getForeignKeys() as $foreignKey) { |
|
100 | 2 | $foreignTable = strtolower($foreignKey->getForeignTableName()); |
|
101 | 2 | if (!isset($foreignKeysToTable[$foreignTable])) { |
|
102 | 2 | $foreignKeysToTable[$foreignTable] = []; |
|
103 | } |
||
104 | 20 | $foreignKeysToTable[$foreignTable][] = $foreignKey; |
|
105 | } |
||
106 | } |
||
107 | |||
108 | 27 | foreach ($diff->removedTables as $tableName => $table) { |
|
109 | 5 | if (isset($foreignKeysToTable[$tableName])) { |
|
110 | 2 | $diff->orphanedForeignKeys = array_merge($diff->orphanedForeignKeys, $foreignKeysToTable[$tableName]); |
|
111 | |||
112 | // deleting duplicated foreign keys present on both on the orphanedForeignKey |
||
113 | // and the removedForeignKeys from changedTables |
||
114 | 2 | foreach ($foreignKeysToTable[$tableName] as $foreignKey) { |
|
115 | // strtolower the table name to make if compatible with getShortestName |
||
116 | 2 | $localTableName = strtolower($foreignKey->getLocalTableName()); |
|
117 | 2 | if (isset($diff->changedTables[$localTableName])) { |
|
118 | 2 | foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) { |
|
119 | // We check if the key is from the removed table if not we skip. |
||
120 | 2 | if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) { |
|
121 | 1 | continue; |
|
122 | } |
||
123 | 5 | unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]); |
|
124 | } |
||
125 | } |
||
126 | } |
||
127 | } |
||
128 | } |
||
129 | |||
130 | 27 | foreach ($toSchema->getSequences() as $sequence) { |
|
131 | 4 | $sequenceName = $sequence->getShortestName($toSchema->getName()); |
|
132 | 4 | if ( ! $fromSchema->hasSequence($sequenceName)) { |
|
133 | 3 | if ( ! $this->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) { |
|
134 | 3 | $diff->newSequences[] = $sequence; |
|
135 | } |
||
136 | } else { |
||
137 | 2 | if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { |
|
138 | 4 | $diff->changedSequences[] = $toSchema->getSequence($sequenceName); |
|
139 | } |
||
140 | } |
||
141 | } |
||
142 | |||
143 | 27 | foreach ($fromSchema->getSequences() as $sequence) { |
|
144 | 4 | if ($this->isAutoIncrementSequenceInSchema($toSchema, $sequence)) { |
|
145 | 1 | continue; |
|
146 | } |
||
147 | |||
148 | 3 | $sequenceName = $sequence->getShortestName($fromSchema->getName()); |
|
149 | |||
150 | 3 | if ( ! $toSchema->hasSequence($sequenceName)) { |
|
151 | 3 | $diff->removedSequences[] = $sequence; |
|
152 | } |
||
153 | } |
||
154 | |||
155 | 27 | return $diff; |
|
156 | } |
||
157 | |||
158 | /** |
||
159 | * @param \Doctrine\DBAL\Schema\Schema $schema |
||
160 | * @param \Doctrine\DBAL\Schema\Sequence $sequence |
||
161 | * |
||
162 | * @return boolean |
||
163 | */ |
||
164 | 6 | private function isAutoIncrementSequenceInSchema($schema, $sequence) |
|
165 | { |
||
166 | 6 | foreach ($schema->getTables() as $table) { |
|
167 | 2 | if ($sequence->isAutoIncrementsFor($table)) { |
|
168 | 2 | return true; |
|
169 | } |
||
170 | } |
||
171 | |||
172 | 4 | return false; |
|
173 | } |
||
174 | |||
175 | /** |
||
176 | * @param \Doctrine\DBAL\Schema\Sequence $sequence1 |
||
177 | * @param \Doctrine\DBAL\Schema\Sequence $sequence2 |
||
178 | * |
||
179 | * @return boolean |
||
180 | */ |
||
181 | 3 | public function diffSequence(Sequence $sequence1, Sequence $sequence2) |
|
192 | } |
||
193 | |||
194 | /** |
||
195 | * Returns the difference between the tables $table1 and $table2. |
||
196 | * |
||
197 | * If there are no differences this method returns the boolean false. |
||
198 | * |
||
199 | * @param \Doctrine\DBAL\Schema\Table $table1 |
||
200 | * @param \Doctrine\DBAL\Schema\Table $table2 |
||
201 | * |
||
202 | * @return boolean|\Doctrine\DBAL\Schema\TableDiff |
||
203 | */ |
||
204 | 112 | public function diffTable(Table $table1, Table $table2) |
|
205 | { |
||
206 | 112 | $changes = 0; |
|
207 | 112 | $tableDifferences = new TableDiff($table1->getName()); |
|
208 | 112 | $tableDifferences->fromTable = $table1; |
|
209 | |||
210 | 112 | $table1Columns = $table1->getColumns(); |
|
211 | 112 | $table2Columns = $table2->getColumns(); |
|
212 | |||
213 | /* See if all the fields in table 1 exist in table 2 */ |
||
214 | 112 | foreach ($table2Columns as $columnName => $column) { |
|
215 | 107 | if ( !$table1->hasColumn($columnName)) { |
|
216 | 26 | $tableDifferences->addedColumns[$columnName] = $column; |
|
217 | 107 | $changes++; |
|
218 | } |
||
219 | } |
||
220 | /* See if there are any removed fields in table 2 */ |
||
221 | 112 | foreach ($table1Columns as $columnName => $column) { |
|
222 | // See if column is removed in table 2. |
||
223 | 107 | if ( ! $table2->hasColumn($columnName)) { |
|
224 | 27 | $tableDifferences->removedColumns[$columnName] = $column; |
|
225 | 27 | $changes++; |
|
226 | 27 | continue; |
|
227 | } |
||
228 | |||
229 | // See if column has changed properties in table 2. |
||
230 | 88 | $changedProperties = $this->diffColumn($column, $table2->getColumn($columnName)); |
|
231 | |||
232 | 88 | if ( ! empty($changedProperties)) { |
|
233 | 41 | $columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties); |
|
234 | 41 | $columnDiff->fromColumn = $column; |
|
235 | 41 | $tableDifferences->changedColumns[$column->getName()] = $columnDiff; |
|
236 | 88 | $changes++; |
|
237 | } |
||
238 | } |
||
239 | |||
240 | 112 | $this->detectColumnRenamings($tableDifferences); |
|
241 | |||
242 | 112 | $table1Indexes = $table1->getIndexes(); |
|
243 | 112 | $table2Indexes = $table2->getIndexes(); |
|
244 | |||
245 | /* See if all the indexes in table 1 exist in table 2 */ |
||
246 | 112 | foreach ($table2Indexes as $indexName => $index) { |
|
247 | 42 | if (($index->isPrimary() && $table1->hasPrimaryKey()) || $table1->hasIndex($indexName)) { |
|
248 | 29 | continue; |
|
249 | } |
||
250 | |||
251 | 13 | $tableDifferences->addedIndexes[$indexName] = $index; |
|
252 | 13 | $changes++; |
|
253 | } |
||
254 | /* See if there are any removed indexes in table 2 */ |
||
255 | 112 | foreach ($table1Indexes as $indexName => $index) { |
|
256 | // See if index is removed in table 2. |
||
257 | 40 | if (($index->isPrimary() && ! $table2->hasPrimaryKey()) || |
|
258 | 40 | ! $index->isPrimary() && ! $table2->hasIndex($indexName) |
|
259 | ) { |
||
260 | 15 | $tableDifferences->removedIndexes[$indexName] = $index; |
|
261 | 15 | $changes++; |
|
262 | 15 | continue; |
|
263 | } |
||
264 | |||
265 | // See if index has changed in table 2. |
||
266 | 29 | $table2Index = $index->isPrimary() ? $table2->getPrimaryKey() : $table2->getIndex($indexName); |
|
267 | |||
268 | 29 | if ($this->diffIndex($index, $table2Index)) { |
|
|
|||
269 | 11 | $tableDifferences->changedIndexes[$indexName] = $table2Index; |
|
270 | 29 | $changes++; |
|
271 | } |
||
272 | } |
||
273 | |||
274 | 112 | $this->detectIndexRenamings($tableDifferences); |
|
275 | |||
276 | 112 | $fromFkeys = $table1->getForeignKeys(); |
|
277 | 112 | $toFkeys = $table2->getForeignKeys(); |
|
278 | |||
279 | 112 | foreach ($fromFkeys as $key1 => $constraint1) { |
|
280 | 10 | foreach ($toFkeys as $key2 => $constraint2) { |
|
281 | 4 | if ($this->diffForeignKey($constraint1, $constraint2) === false) { |
|
282 | 1 | unset($fromFkeys[$key1]); |
|
283 | 1 | unset($toFkeys[$key2]); |
|
284 | } else { |
||
285 | 3 | if (strtolower($constraint1->getName()) == strtolower($constraint2->getName())) { |
|
286 | 2 | $tableDifferences->changedForeignKeys[] = $constraint2; |
|
287 | 2 | $changes++; |
|
288 | 2 | unset($fromFkeys[$key1]); |
|
289 | 10 | unset($toFkeys[$key2]); |
|
290 | } |
||
291 | } |
||
292 | } |
||
293 | } |
||
294 | |||
295 | 112 | foreach ($fromFkeys as $constraint1) { |
|
296 | 7 | $tableDifferences->removedForeignKeys[] = $constraint1; |
|
297 | 7 | $changes++; |
|
298 | } |
||
299 | |||
300 | 112 | foreach ($toFkeys as $constraint2) { |
|
301 | 2 | $tableDifferences->addedForeignKeys[] = $constraint2; |
|
302 | 2 | $changes++; |
|
303 | } |
||
304 | |||
305 | 112 | return $changes ? $tableDifferences : false; |
|
306 | } |
||
307 | |||
308 | /** |
||
309 | * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop |
||
310 | * however ambiguities between different possibilities should not lead to renaming at all. |
||
311 | * |
||
312 | * @param \Doctrine\DBAL\Schema\TableDiff $tableDifferences |
||
313 | * |
||
314 | * @return void |
||
315 | */ |
||
316 | 112 | View Code Duplication | private function detectColumnRenamings(TableDiff $tableDifferences) |
317 | { |
||
318 | 112 | $renameCandidates = []; |
|
319 | 112 | foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { |
|
320 | 26 | foreach ($tableDifferences->removedColumns as $removedColumn) { |
|
321 | 21 | if (count($this->diffColumn($addedColumn, $removedColumn)) == 0) { |
|
322 | 26 | $renameCandidates[$addedColumn->getName()][] = [$removedColumn, $addedColumn, $addedColumnName]; |
|
323 | } |
||
324 | } |
||
325 | } |
||
326 | |||
327 | 112 | foreach ($renameCandidates as $candidateColumns) { |
|
328 | 21 | if (count($candidateColumns) == 1) { |
|
329 | 20 | list($removedColumn, $addedColumn) = $candidateColumns[0]; |
|
330 | 20 | $removedColumnName = strtolower($removedColumn->getName()); |
|
331 | 20 | $addedColumnName = strtolower($addedColumn->getName()); |
|
332 | |||
333 | 20 | if ( ! isset($tableDifferences->renamedColumns[$removedColumnName])) { |
|
334 | 20 | $tableDifferences->renamedColumns[$removedColumnName] = $addedColumn; |
|
335 | 20 | unset($tableDifferences->addedColumns[$addedColumnName]); |
|
336 | 21 | unset($tableDifferences->removedColumns[$removedColumnName]); |
|
337 | } |
||
338 | } |
||
339 | } |
||
340 | 112 | } |
|
341 | |||
342 | /** |
||
343 | * Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop |
||
344 | * however ambiguities between different possibilities should not lead to renaming at all. |
||
345 | * |
||
346 | * @param \Doctrine\DBAL\Schema\TableDiff $tableDifferences |
||
347 | * |
||
348 | * @return void |
||
349 | */ |
||
350 | 112 | View Code Duplication | private function detectIndexRenamings(TableDiff $tableDifferences) |
378 | } |
||
379 | } |
||
380 | } |
||
381 | 112 | } |
|
382 | |||
383 | /** |
||
384 | * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $key1 |
||
385 | * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $key2 |
||
386 | * |
||
387 | * @return boolean |
||
388 | */ |
||
389 | 7 | public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) |
|
412 | } |
||
413 | |||
414 | /** |
||
415 | * Returns the difference between the fields $field1 and $field2. |
||
416 | * |
||
417 | * If there are differences this method returns $field2, otherwise the |
||
418 | * boolean false. |
||
419 | * |
||
420 | * @param \Doctrine\DBAL\Schema\Column $column1 |
||
421 | * @param \Doctrine\DBAL\Schema\Column $column2 |
||
422 | * |
||
423 | * @return array |
||
424 | */ |
||
425 | 129 | public function diffColumn(Column $column1, Column $column2) |
|
505 | } |
||
506 | |||
507 | /** |
||
508 | * TODO: kill with fire on v3.0 |
||
509 | * |
||
510 | * @deprecated |
||
511 | */ |
||
512 | 129 | private function isALegacyJsonComparison(Types\Type $one, Types\Type $other) : bool |
|
520 | } |
||
521 | |||
522 | /** |
||
523 | * Finds the difference between the indexes $index1 and $index2. |
||
524 | * |
||
525 | * Compares $index1 with $index2 and returns $index2 if there are any |
||
526 | * differences or false in case there are no differences. |
||
527 | * |
||
528 | * @param \Doctrine\DBAL\Schema\Index $index1 |
||
529 | * @param \Doctrine\DBAL\Schema\Index $index2 |
||
530 | * |
||
531 | * @return boolean |
||
532 | */ |
||
533 | 35 | public function diffIndex(Index $index1, Index $index2) |
|
542 |