Complex classes like SchemaAnalyzer 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 SchemaAnalyzer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class SchemaAnalyzer |
||
24 | { |
||
25 | private static $WEIGHT_FK = 1; |
||
26 | private static $WEIGHT_INHERITANCE_FK = 0.1; |
||
27 | private static $WEIGHT_JOINTURE_TABLE = 1.5; |
||
28 | |||
29 | const WEIGHT_IMPORTANT = 0.75; |
||
30 | const WEIGHT_IRRELEVANT = 2; |
||
31 | const WEIGHT_IGNORE = INF; |
||
32 | |||
33 | /** |
||
34 | * @var AbstractSchemaManager |
||
35 | */ |
||
36 | private $schemaManager; |
||
37 | |||
38 | /** |
||
39 | * @var Schema |
||
40 | */ |
||
41 | private $schema; |
||
42 | |||
43 | /** |
||
44 | * @var Cache |
||
45 | */ |
||
46 | private $cache; |
||
47 | |||
48 | /** |
||
49 | * @var string |
||
50 | */ |
||
51 | private $cachePrefix; |
||
52 | |||
53 | /** |
||
54 | * Nested arrays containing table => column => cost. |
||
55 | * |
||
56 | * @var float[][] |
||
57 | */ |
||
58 | private $alteredCosts = []; |
||
59 | |||
60 | /** |
||
61 | * Array containing table cost. |
||
62 | * |
||
63 | * @var float[] |
||
64 | */ |
||
65 | private $alteredTableCosts = []; |
||
66 | |||
67 | /** |
||
68 | * @param AbstractSchemaManager $schemaManager |
||
69 | * @param Cache|null $cache The Doctrine cache service to use to cache results (optional) |
||
70 | * @param string|null $schemaCacheKey The unique identifier for the schema manager. Compulsory if cache is set. |
||
71 | */ |
||
72 | public function __construct(AbstractSchemaManager $schemaManager, Cache $cache = null, $schemaCacheKey = null) |
||
85 | |||
86 | /** |
||
87 | * Detect all junctions tables in the schema. |
||
88 | * A table is a junction table if:. |
||
89 | * |
||
90 | * - it has exactly 2 foreign keys |
||
91 | * - it has only 2 columns (or 3 columns if the third one is an autoincremented primary key). |
||
92 | * |
||
93 | * If $ignoreReferencedTables is true, junctions table that are pointed to by a foreign key of another |
||
94 | * table are ignored. |
||
95 | * |
||
96 | * @param bool $ignoreReferencedTables |
||
97 | * |
||
98 | * @return Table[] |
||
99 | */ |
||
100 | public function detectJunctionTables($ignoreReferencedTables = false) |
||
113 | |||
114 | /** |
||
115 | * Returns true if $table is a junction table. |
||
116 | * I.e:. |
||
117 | * |
||
118 | * - it must have exactly 2 foreign keys |
||
119 | * - it must have only 2 columns (or 3 columns if the third one is an autoincremented primary key). |
||
120 | * |
||
121 | * If $ignoreReferencedTables is true, junctions table that are pointed to by a foreign key of another |
||
122 | * table are ignored. |
||
123 | * |
||
124 | * @param Table $table |
||
125 | * @param bool $ignoreReferencedTables |
||
126 | * |
||
127 | * @return bool |
||
128 | */ |
||
129 | private function isJunctionTable(Table $table, $ignoreReferencedTables = false) |
||
182 | |||
183 | /** |
||
184 | * Returns true if the table $table is referenced by another table. |
||
185 | * |
||
186 | * @param Table $table |
||
187 | * |
||
188 | * @return bool |
||
189 | */ |
||
190 | private function isTableReferenced(Table $table) |
||
203 | |||
204 | /** |
||
205 | * Get the shortest path between 2 tables. |
||
206 | * |
||
207 | * @param string $fromTable |
||
208 | * @param string $toTable |
||
209 | * |
||
210 | * @return \Doctrine\DBAL\Schema\ForeignKeyConstraint[] |
||
211 | * |
||
212 | * @throws SchemaAnalyzerException |
||
213 | */ |
||
214 | public function getShortestPath($fromTable, $toTable) |
||
220 | |||
221 | /** |
||
222 | * Get the shortest path between 2 tables. |
||
223 | * |
||
224 | * @param string $fromTable |
||
225 | * @param string $toTable |
||
226 | * |
||
227 | * @return \Doctrine\DBAL\Schema\ForeignKeyConstraint[] |
||
228 | * |
||
229 | * @throws SchemaAnalyzerException |
||
230 | */ |
||
231 | private function getShortestPathWithoutCache($fromTable, $toTable) |
||
284 | |||
285 | private function checkTableExists($tableName) |
||
293 | |||
294 | private function buildSchemaGraph() |
||
343 | |||
344 | /** |
||
345 | * Remove duplicate foreign keys (assumes that all foreign yes are from the same local table). |
||
346 | * |
||
347 | * @param ForeignKeyConstraint[] $foreignKeys |
||
348 | * @return ForeignKeyConstraint[] |
||
349 | */ |
||
350 | private function removeDuplicates(array $foreignKeys) |
||
359 | |||
360 | /** |
||
361 | * Returns the schema (from the schema manager or the cache if needed). |
||
362 | * |
||
363 | * @return Schema |
||
364 | */ |
||
365 | private function getSchema() |
||
378 | |||
379 | /** |
||
380 | * Returns the full exception message when an ambiguity arises. |
||
381 | * |
||
382 | * @param Base[][] $paths |
||
383 | * @param Vertex $startVertex |
||
384 | */ |
||
385 | private function getAmbiguityExceptionMessage(array $paths, Vertex $startVertex, Vertex $endVertex) |
||
401 | |||
402 | /** |
||
403 | * Returns the textual representation of the path. |
||
404 | * |
||
405 | * @param Base[] $path |
||
406 | * @param Vertex $startVertex |
||
407 | */ |
||
408 | private function getTextualPath(array $path, Vertex $startVertex) |
||
452 | |||
453 | /** |
||
454 | * Sets the cost of a foreign key. |
||
455 | * |
||
456 | * @param string $tableName |
||
457 | * @param string $columnName |
||
458 | * @param float $cost |
||
459 | * |
||
460 | * @return $this |
||
461 | */ |
||
462 | public function setForeignKeyCost($tableName, $columnName, $cost) |
||
466 | |||
467 | /** |
||
468 | * Sets the cost modifier of a table. |
||
469 | * |
||
470 | * @param string $tableName |
||
471 | * @param float $cost |
||
472 | * |
||
473 | * @return $this |
||
474 | */ |
||
475 | public function setTableCostModifier($tableName, $cost) |
||
479 | |||
480 | /** |
||
481 | * Sets the cost modifier of all tables at once. |
||
482 | * |
||
483 | * @param array<string, float> $tableCosts The key is the table name, the value is the cost modifier. |
||
484 | */ |
||
485 | public function setTableCostModifiers(array $tableCosts) |
||
489 | |||
490 | /** |
||
491 | * Sets the cost of all foreign keys at once. |
||
492 | * |
||
493 | * @param array<string, array<string, float>> $fkCosts First key is the table name, second key is the column name, the value is the cost. |
||
494 | */ |
||
495 | public function setForeignKeyCosts(array $fkCosts) |
||
499 | |||
500 | /** |
||
501 | * Returns true if this foreign key represents an inheritance relationship, |
||
502 | * i.e. if this foreign key is based on a primary key. |
||
503 | * |
||
504 | * @param ForeignKeyConstraint $fk |
||
505 | * |
||
506 | * @return true |
||
507 | */ |
||
508 | private function isInheritanceRelationship(ForeignKeyConstraint $fk) |
||
521 | |||
522 | /** |
||
523 | * If this table is pointing to a parent table (if its primary key is a foreign key pointing on another table), |
||
524 | * this function will return the pointed table. |
||
525 | * This function will return null if there is no parent table. |
||
526 | * |
||
527 | * @param string $tableName |
||
528 | * |
||
529 | * @return ForeignKeyConstraint|null |
||
530 | */ |
||
531 | public function getParentRelationship($tableName) |
||
537 | |||
538 | /** |
||
539 | * If this table is pointing to a parent table (if its primary key is a foreign key pointing on another table), |
||
540 | * this function will return the pointed table. |
||
541 | * This function will return null if there is no parent table. |
||
542 | * |
||
543 | * @param string $tableName |
||
544 | * |
||
545 | * @return ForeignKeyConstraint|null |
||
546 | */ |
||
547 | private function getParentRelationshipWithoutCache($tableName) |
||
558 | |||
559 | /** |
||
560 | * If this table is pointed by children tables (if other child tables have a primary key that is also a |
||
561 | * foreign key to this table), this function will return the list of child tables. |
||
562 | * This function will return an empty array if there are no children tables. |
||
563 | * |
||
564 | * @param string $tableName |
||
565 | * |
||
566 | * @return ForeignKeyConstraint[] |
||
567 | */ |
||
568 | public function getChildrenRelationships($tableName) |
||
574 | |||
575 | /** |
||
576 | * If this table is pointed by children tables (if other child tables have a primary key that is also a |
||
577 | * foreign key to this table), this function will return the list of child tables. |
||
578 | * This function will return an empty array if there are no children tables. |
||
579 | * |
||
580 | * @param string $tableName |
||
581 | * |
||
582 | * @return ForeignKeyConstraint[] |
||
583 | */ |
||
584 | private function getChildrenRelationshipsWithoutCache($tableName) |
||
602 | |||
603 | /** |
||
604 | * Returns an item from cache or computes it using $closure and puts it in cache. |
||
605 | * |
||
606 | * @param string $key |
||
607 | * @param callable $closure |
||
608 | * |
||
609 | * @return mixed |
||
610 | */ |
||
611 | private function fromCache($key, callable $closure) |
||
621 | } |
||
622 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.