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 Manager 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 Manager, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
41 | class Manager |
||
42 | { |
||
43 | /** |
||
44 | * @var \Phinx\Config\ConfigInterface |
||
45 | */ |
||
46 | protected $config; |
||
47 | |||
48 | /** |
||
49 | * @var \Symfony\Component\Console\Input\InputInterface |
||
50 | */ |
||
51 | protected $input; |
||
52 | |||
53 | /** |
||
54 | * @var \Symfony\Component\Console\Output\OutputInterface |
||
55 | */ |
||
56 | protected $output; |
||
57 | |||
58 | /** |
||
59 | * @var array |
||
60 | */ |
||
61 | protected $environments; |
||
62 | |||
63 | /** |
||
64 | * @var array |
||
65 | */ |
||
66 | protected $migrations; |
||
67 | |||
68 | /** |
||
69 | * @var array |
||
70 | */ |
||
71 | protected $seeds; |
||
72 | |||
73 | /** |
||
74 | * @var integer |
||
75 | */ |
||
76 | const EXIT_STATUS_DOWN = 1; |
||
77 | |||
78 | /** |
||
79 | * @var integer |
||
80 | */ |
||
81 | const EXIT_STATUS_MISSING = 2; |
||
82 | |||
83 | /** |
||
84 | * Class Constructor. |
||
85 | * |
||
86 | * @param \Phinx\Config\ConfigInterface $config Configuration Object |
||
87 | * @param \Symfony\Component\Console\Input\InputInterface $input Console Input |
||
88 | * @param \Symfony\Component\Console\Output\OutputInterface $output Console Output |
||
89 | 432 | */ |
|
90 | public function __construct(ConfigInterface $config, InputInterface $input, OutputInterface $output) |
||
91 | 432 | { |
|
92 | 432 | $this->setConfig($config); |
|
93 | 432 | $this->setInput($input); |
|
94 | 432 | $this->setOutput($output); |
|
95 | } |
||
96 | |||
97 | /** |
||
98 | * Prints the specified environment's migration status. |
||
99 | * |
||
100 | * @param string $environment |
||
101 | * @param null $format |
||
102 | * @return int 0 if all migrations are up, or an error code |
||
103 | 22 | */ |
|
104 | public function printStatus($environment, $format = null) |
||
105 | 22 | { |
|
106 | 22 | $output = $this->getOutput(); |
|
107 | 22 | $migrations = []; |
|
|
|||
108 | 22 | $hasDownMigration = false; |
|
109 | 22 | $hasMissingMigration = false; |
|
110 | 22 | $migrations = $this->getMigrations(); |
|
111 | if (count($migrations)) { |
||
112 | // TODO - rewrite using Symfony Table Helper as we already have this library |
||
113 | 21 | // included and it will fix formatting issues (e.g drawing the lines) |
|
114 | $output->writeln(''); |
||
115 | 21 | ||
116 | 21 | switch ($this->getConfig()->getVersionOrder()) { |
|
117 | 19 | case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME: |
|
118 | 19 | $migrationIdAndStartedHeader = "<info>[Migration ID]</info> Started "; |
|
119 | 2 | break; |
|
120 | 1 | case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME: |
|
121 | 1 | $migrationIdAndStartedHeader = "Migration ID <info>[Started ]</info>"; |
|
122 | 1 | break; |
|
123 | 1 | default: |
|
124 | 21 | throw new \RuntimeException('Invalid version_order configuration option'); |
|
125 | } |
||
126 | 20 | ||
127 | 20 | $output->writeln(" Status $migrationIdAndStartedHeader Finished Migration Name "); |
|
128 | $output->writeln('----------------------------------------------------------------------------------'); |
||
129 | 20 | ||
130 | 20 | $env = $this->getEnvironment($environment); |
|
131 | $versions = $env->getVersionLog(); |
||
132 | |||
133 | 17 | $maxNameLength = $versions ? max(array_map(function ($version) { |
|
134 | 20 | return strlen($version['migration_name']); |
|
135 | }, $versions)) : 0; |
||
136 | 20 | ||
137 | $missingVersions = array_diff_key($versions, $migrations); |
||
138 | 20 | ||
139 | $hasMissingMigration = !empty($missingVersions); |
||
140 | |||
141 | 20 | // get the migrations sorted in the same way as the versions |
|
142 | $sortedMigrations = []; |
||
143 | 20 | ||
144 | 17 | foreach ($versions as $versionCreationTime => $version) { |
|
145 | 13 | if (isset($migrations[$versionCreationTime])) { |
|
146 | 13 | array_push($sortedMigrations, $migrations[$versionCreationTime]); |
|
147 | 13 | unset($migrations[$versionCreationTime]); |
|
148 | 20 | } |
|
149 | } |
||
150 | 20 | ||
151 | if (empty($sortedMigrations) && !empty($missingVersions)) { |
||
152 | // this means we have no up migrations, so we write all the missing versions already so they show up |
||
153 | 4 | // before any possible down migration |
|
154 | 4 | foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { |
|
155 | $this->printMissingVersion($missingVersion, $maxNameLength); |
||
156 | 4 | ||
157 | 4 | unset($missingVersions[$missingVersionCreationTime]); |
|
158 | 4 | } |
|
159 | } |
||
160 | |||
161 | // any migration left in the migrations (ie. not unset when sorting the migrations by the version order) is |
||
162 | 20 | // a migration that is down, so we add them to the end of the sorted migrations list |
|
163 | 13 | if (!empty($migrations)) { |
|
164 | 13 | $sortedMigrations = array_merge($sortedMigrations, $migrations); |
|
165 | } |
||
166 | 20 | ||
167 | 20 | foreach ($sortedMigrations as $migration) { |
|
168 | 20 | $version = array_key_exists($migration->getVersion(), $versions) ? $versions[$migration->getVersion()] : false; |
|
169 | if ($version) { |
||
170 | 13 | // check if there are missing versions before this version |
|
171 | 6 | foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { |
|
172 | 6 | if ($this->getConfig()->isVersionOrderCreationTime()) { |
|
173 | 4 | if ($missingVersion['version'] > $version['version']) { |
|
174 | break; |
||
175 | 3 | } |
|
176 | } else { |
||
177 | if ($missingVersion['start_time'] > $version['start_time']) { |
||
178 | break; |
||
179 | } elseif ($missingVersion['start_time'] == $version['start_time'] && |
||
180 | $missingVersion['version'] > $version['version']) { |
||
181 | break; |
||
182 | } |
||
183 | } |
||
184 | 3 | ||
185 | $this->printMissingVersion($missingVersion, $maxNameLength); |
||
186 | 3 | ||
187 | 13 | unset($missingVersions[$missingVersionCreationTime]); |
|
188 | } |
||
189 | 13 | ||
190 | 13 | $status = ' <info>up</info> '; |
|
191 | 13 | } else { |
|
192 | 13 | $hasDownMigration = true; |
|
193 | $status = ' <error>down</error> '; |
||
194 | 20 | } |
|
195 | $maxNameLength = max($maxNameLength, strlen($migration->getName())); |
||
196 | 20 | ||
197 | 20 | $output->writeln(sprintf( |
|
198 | 20 | '%s %14.0f %19s %19s <comment>%s</comment>', |
|
199 | 20 | $status, |
|
200 | 20 | $migration->getVersion(), |
|
201 | 20 | $version['start_time'], |
|
202 | 20 | $version['end_time'], |
|
203 | 20 | $migration->getName() |
|
204 | )); |
||
205 | 20 | ||
206 | 1 | if ($version && $version['breakpoint']) { |
|
207 | 1 | $output->writeln(' <error>BREAKPOINT SET</error>'); |
|
208 | } |
||
209 | 20 | ||
210 | 20 | $migrations[] = ['migration_status' => trim(strip_tags($status)), 'migration_id' => sprintf('%14.0f', $migration->getVersion()), 'migration_name' => $migration->getName()]; |
|
211 | 20 | unset($versions[$migration->getVersion()]); |
|
212 | } |
||
213 | |||
214 | 20 | // and finally add any possibly-remaining missing migrations |
|
215 | 4 | foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { |
|
216 | $this->printMissingVersion($missingVersion, $maxNameLength); |
||
217 | 4 | ||
218 | 20 | unset($missingVersions[$missingVersionCreationTime]); |
|
219 | 20 | } |
|
220 | } else { |
||
221 | 1 | // there are no migrations |
|
222 | 1 | $output->writeln(''); |
|
223 | $output->writeln('There are no available migrations. Try creating one using the <info>create</info> command.'); |
||
224 | } |
||
225 | |||
226 | 21 | // write an empty line |
|
227 | 21 | $output->writeln(''); |
|
228 | if ($format !== null) { |
||
229 | switch ($format) { |
||
230 | case 'json': |
||
231 | $output->writeln(json_encode( |
||
232 | [ |
||
233 | 'pending_count' => count($this->getMigrations()), |
||
234 | 'migrations' => $migrations |
||
235 | ] |
||
236 | )); |
||
237 | break; |
||
238 | default: |
||
239 | $output->writeln('<info>Unsupported format: ' . $format . '</info>'); |
||
240 | } |
||
241 | } |
||
242 | 21 | ||
243 | 10 | if ($hasMissingMigration) { |
|
244 | 11 | return self::EXIT_STATUS_MISSING; |
|
245 | 6 | } elseif ($hasDownMigration) { |
|
246 | return self::EXIT_STATUS_DOWN; |
||
247 | 5 | } else { |
|
248 | return 0; |
||
249 | } |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Print Missing Version |
||
254 | * |
||
255 | * @param array $version The missing version to print (in the format returned by Environment.getVersionLog). |
||
256 | * @param int $maxNameLength The maximum migration name length. |
||
257 | 10 | */ |
|
258 | private function printMissingVersion($version, $maxNameLength) |
||
259 | 10 | { |
|
260 | 10 | $this->getOutput()->writeln(sprintf( |
|
261 | 10 | ' <error>up</error> %14.0f %19s %19s <comment>%s</comment> <error>** MISSING **</error>', |
|
262 | 10 | $version['version'], |
|
263 | 10 | $version['start_time'], |
|
264 | 10 | $version['end_time'], |
|
265 | 10 | str_pad($version['migration_name'], $maxNameLength, ' ') |
|
266 | )); |
||
267 | 10 | ||
268 | 1 | if ($version && $version['breakpoint']) { |
|
269 | 1 | $this->getOutput()->writeln(' <error>BREAKPOINT SET</error>'); |
|
270 | 10 | } |
|
271 | } |
||
272 | |||
273 | /** |
||
274 | * Migrate to the version of the database on a given date. |
||
275 | * |
||
276 | * @param string $environment Environment |
||
277 | * @param \DateTime $dateTime Date to migrate to |
||
278 | * |
||
279 | * @return void |
||
280 | 4 | */ |
|
281 | public function migrateToDateTime($environment, \DateTime $dateTime) |
||
282 | 4 | { |
|
283 | 4 | $versions = array_keys($this->getMigrations()); |
|
284 | $dateString = $dateTime->format('YmdHis'); |
||
285 | |||
286 | 4 | $outstandingMigrations = array_filter($versions, function ($version) use ($dateString) { |
|
287 | 4 | return $version <= $dateString; |
|
288 | }); |
||
289 | 4 | ||
290 | 3 | if (count($outstandingMigrations) > 0) { |
|
291 | 3 | $migration = max($outstandingMigrations); |
|
292 | 3 | $this->getOutput()->writeln('Migrating to version ' . $migration); |
|
293 | 3 | $this->migrate($environment, $migration); |
|
294 | 4 | } |
|
295 | } |
||
296 | |||
297 | /** |
||
298 | * Migrate an environment to the specified version. |
||
299 | * |
||
300 | * @param string $environment Environment |
||
301 | * @param int $version |
||
302 | * @return void |
||
303 | 8 | */ |
|
304 | public function migrate($environment, $version = null) |
||
356 | |||
357 | /** |
||
358 | * Execute a migration against the specified environment. |
||
359 | * |
||
360 | * @param string $name Environment Name |
||
361 | * @param \Phinx\Migration\MigrationInterface $migration Migration |
||
362 | * @param string $direction Direction |
||
363 | 119 | * @return void |
|
364 | */ |
||
365 | 119 | public function executeMigration($name, MigrationInterface $migration, $direction = MigrationInterface::UP) |
|
386 | |||
387 | /** |
||
388 | * Execute a seeder against the specified environment. |
||
389 | * |
||
390 | * @param string $name Environment Name |
||
391 | * @param \Phinx\Seed\SeedInterface $seed Seed |
||
392 | 6 | * @return void |
|
393 | */ |
||
394 | 6 | public function executeSeed($name, SeedInterface $seed) |
|
415 | |||
416 | /** |
||
417 | * Rollback an environment to the specified version. |
||
418 | * |
||
419 | * @param string $environment Environment |
||
420 | * @param int|string $target |
||
421 | * @param bool $force |
||
422 | * @param bool $targetMustMatchVersion |
||
423 | 349 | * @return void |
|
424 | */ |
||
425 | public function rollback($environment, $target = null, $force = false, $targetMustMatchVersion = true) |
||
525 | |||
526 | /** |
||
527 | * Run database seeders against an environment. |
||
528 | 9 | * |
|
529 | * @param string $environment Environment |
||
530 | 9 | * @param string $seed Seeder |
|
531 | * @return void |
||
532 | 9 | */ |
|
533 | public function seed($environment, $seed = null) |
||
553 | |||
554 | /** |
||
555 | 381 | * Sets the environments. |
|
556 | * |
||
557 | 381 | * @param array $environments Environments |
|
558 | 381 | * @return \Phinx\Migration\Manager |
|
559 | */ |
||
560 | public function setEnvironments($environments = []) |
||
566 | |||
567 | /** |
||
568 | 382 | * Gets the manager class for the given environment. |
|
569 | * |
||
570 | 382 | * @param string $name Environment Name |
|
571 | 380 | * @throws \InvalidArgumentException |
|
572 | * @return \Phinx\Migration\Manager\Environment |
||
573 | */ |
||
574 | public function getEnvironment($name) |
||
599 | |||
600 | 400 | /** |
|
601 | * Sets the console input. |
||
602 | 400 | * |
|
603 | 400 | * @param \Symfony\Component\Console\Input\InputInterface $input Input |
|
604 | * @return \Phinx\Migration\Manager |
||
605 | */ |
||
606 | public function setInput(InputInterface $input) |
||
607 | { |
||
608 | $this->input = $input; |
||
609 | |||
610 | return $this; |
||
611 | 393 | } |
|
612 | |||
613 | 393 | /** |
|
614 | * Gets the console input. |
||
615 | * |
||
616 | * @return \Symfony\Component\Console\Input\InputInterface |
||
617 | */ |
||
618 | public function getInput() |
||
619 | { |
||
620 | return $this->input; |
||
621 | } |
||
622 | 400 | ||
623 | /** |
||
624 | 400 | * Sets the console output. |
|
625 | 400 | * |
|
626 | * @param \Symfony\Component\Console\Output\OutputInterface $output Output |
||
627 | * @return \Phinx\Migration\Manager |
||
628 | */ |
||
629 | public function setOutput(OutputInterface $output) |
||
635 | 395 | ||
636 | /** |
||
637 | * Gets the console output. |
||
638 | * |
||
639 | * @return \Symfony\Component\Console\Output\OutputInterface |
||
640 | */ |
||
641 | public function getOutput() |
||
645 | |||
646 | 379 | /** |
|
647 | 379 | * Sets the database migrations. |
|
648 | * |
||
649 | * @param array $migrations Migrations |
||
650 | * @return \Phinx\Migration\Manager |
||
651 | */ |
||
652 | public function setMigrations(array $migrations) |
||
658 | |||
659 | 388 | /** |
|
660 | 388 | * Gets an array of the database migrations, indexed by migration name (aka creation time) and sorted in ascending |
|
661 | * order |
||
662 | * |
||
663 | 388 | * @throws \InvalidArgumentException |
|
664 | * @return \Phinx\Migration\AbstractMigration[] |
||
665 | 388 | */ |
|
666 | public function getMigrations() |
||
732 | 388 | ||
733 | 388 | /** |
|
734 | * Returns a list of migration files found in the provided migration paths. |
||
735 | 388 | * |
|
736 | 388 | * @return string[] |
|
737 | 388 | */ |
|
738 | 388 | View Code Duplication | protected function getMigrationFiles() |
739 | 388 | { |
|
740 | 388 | $config = $this->getConfig(); |
|
741 | $paths = $config->getMigrationPaths(); |
||
742 | 388 | $files = []; |
|
743 | |||
744 | foreach ($paths as $path) { |
||
745 | $files = array_merge( |
||
746 | $files, |
||
747 | Util::glob($path . DIRECTORY_SEPARATOR . '*.php') |
||
748 | ); |
||
749 | } |
||
750 | // glob() can return the same file multiple times |
||
751 | 11 | // This will cause the migration to fail with a |
|
752 | // false assumption of duplicate migrations |
||
753 | 11 | // http://php.net/manual/en/function.glob.php#110340 |
|
754 | 11 | $files = array_unique($files); |
|
755 | |||
756 | return $files; |
||
757 | } |
||
758 | |||
759 | /** |
||
760 | * Sets the database seeders. |
||
761 | * |
||
762 | * @param array $seeds Seeders |
||
763 | 11 | * @return \Phinx\Migration\Manager |
|
764 | */ |
||
765 | 11 | public function setSeeds(array $seeds) |
|
771 | 11 | ||
772 | /** |
||
773 | 11 | * Get seed dependencies instances from seed dependency array |
|
774 | 11 | * |
|
775 | 11 | * @param OrderedSeedInterface $seed Seed |
|
776 | 11 | * |
|
777 | * @return AbstractSeed[] |
||
778 | */ |
||
779 | 11 | protected function getSeedDependenciesInstances(OrderedSeedInterface $seed) |
|
797 | |||
798 | /** |
||
799 | * Order seeds by dependencies |
||
800 | * |
||
801 | * @param AbstractSeed[] $seeds Seeds |
||
802 | * |
||
803 | * @return AbstractSeed[] |
||
804 | 11 | */ |
|
805 | 11 | protected function orderSeedsByDependencies(array $seeds) |
|
823 | 11 | ||
824 | 11 | /** |
|
825 | * Gets an array of database seeders. |
||
826 | 11 | * |
|
827 | 11 | * @throws \InvalidArgumentException |
|
828 | 11 | * @return \Phinx\Seed\AbstractSeed[] |
|
829 | 11 | */ |
|
830 | 11 | public function getSeeds() |
|
882 | 1 | ||
883 | 1 | /** |
|
884 | * Returns a list of seed files found in the provided seed paths. |
||
885 | 1 | * |
|
886 | 1 | * @return string[] |
|
887 | */ |
||
888 | View Code Duplication | protected function getSeedFiles() |
|
889 | 1 | { |
|
890 | $config = $this->getConfig(); |
||
891 | 1 | $paths = $config->getSeedPaths(); |
|
892 | $files = []; |
||
893 | 1 | ||
894 | 1 | foreach ($paths as $path) { |
|
895 | 1 | $files = array_merge( |
|
896 | 1 | $files, |
|
897 | 1 | Util::glob($path . DIRECTORY_SEPARATOR . '*.php') |
|
898 | 1 | ); |
|
899 | } |
||
900 | // glob() can return the same file multiple times |
||
901 | // This will cause the migration to fail with a |
||
902 | // false assumption of duplicate migrations |
||
903 | // http://php.net/manual/en/function.glob.php#110340 |
||
904 | $files = array_unique($files); |
||
905 | |||
906 | 1 | return $files; |
|
907 | } |
||
908 | 1 | ||
909 | 1 | /** |
|
910 | 1 | * Sets the config. |
|
911 | 1 | * |
|
912 | 1 | * @param \Phinx\Config\ConfigInterface $config Configuration Object |
|
913 | * @return \Phinx\Migration\Manager |
||
914 | */ |
||
915 | public function setConfig(ConfigInterface $config) |
||
921 | |||
922 | /** |
||
923 | * Gets the config. |
||
924 | * |
||
925 | * @return \Phinx\Config\ConfigInterface |
||
926 | */ |
||
927 | public function getConfig() |
||
931 | |||
932 | /** |
||
933 | * Toggles the breakpoint for a specific version. |
||
934 | * |
||
935 | * @param string $environment |
||
936 | * @param int $version |
||
937 | * @return void |
||
938 | */ |
||
939 | public function toggleBreakpoint($environment, $version) |
||
940 | { |
||
941 | $migrations = $this->getMigrations(); |
||
942 | $this->getMigrations(); |
||
943 | $env = $this->getEnvironment($environment); |
||
944 | $versions = $env->getVersionLog(); |
||
945 | |||
946 | if (empty($versions) || empty($migrations)) { |
||
947 | return; |
||
948 | } |
||
949 | |||
950 | if ($version === null) { |
||
951 | $lastVersion = end($versions); |
||
952 | $version = $lastVersion['version']; |
||
953 | } |
||
954 | |||
955 | View Code Duplication | if (0 != $version && !isset($migrations[$version])) { |
|
956 | $this->output->writeln(sprintf( |
||
957 | '<comment>warning</comment> %s is not a valid version', |
||
958 | $version |
||
959 | )); |
||
960 | |||
961 | return; |
||
962 | } |
||
963 | |||
964 | $env->getAdapter()->toggleBreakpoint($migrations[$version]); |
||
965 | |||
966 | $versions = $env->getVersionLog(); |
||
967 | |||
968 | $this->getOutput()->writeln( |
||
969 | ' Breakpoint ' . ($versions[$version]['breakpoint'] ? 'set' : 'cleared') . |
||
970 | ' for <info>' . $version . '</info>' . |
||
971 | ' <comment>' . $migrations[$version]->getName() . '</comment>' |
||
972 | ); |
||
973 | } |
||
974 | |||
975 | /** |
||
976 | * Remove all breakpoints |
||
977 | * |
||
978 | * @param string $environment |
||
979 | * @return void |
||
980 | */ |
||
981 | public function removeBreakpoints($environment) |
||
982 | { |
||
983 | $this->getOutput()->writeln(sprintf( |
||
984 | ' %d breakpoints cleared.', |
||
985 | $this->getEnvironment($environment)->getAdapter()->resetAllBreakpoints() |
||
986 | )); |
||
987 | } |
||
988 | } |
||
989 |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVar
assignment in line 1 and the$higher
assignment in line 2 are dead. The first because$myVar
is never used and the second because$higher
is always overwritten for every possible time line.