Complex classes like Deployer 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 Deployer, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 58 | class Deployer extends Container |
||
| 59 | { |
||
| 60 | /** |
||
| 61 | * Global instance of deployer. It's can be accessed only after constructor call. |
||
| 62 | * @var Deployer |
||
| 63 | */ |
||
| 64 | private static $instance; |
||
| 65 | |||
| 66 | /** |
||
| 67 | * @param Application $console |
||
| 68 | */ |
||
| 69 | 27 | public function __construct(Application $console) |
|
| 70 | { |
||
| 71 | 27 | parent::__construct(); |
|
| 72 | |||
| 73 | /****************************** |
||
| 74 | * Console * |
||
| 75 | ******************************/ |
||
| 76 | |||
| 77 | $this['console'] = function () use ($console) { |
||
| 78 | $console->catchIO(function ($input, $output) { |
||
| 79 | 11 | $this['input'] = $input; |
|
| 80 | 11 | $this['output'] = new OutputWatcher($output); |
|
| 81 | 11 | return [$this['input'], $this['output']]; |
|
| 82 | 12 | }); |
|
| 83 | 12 | return $console; |
|
| 84 | }; |
||
| 85 | |||
| 86 | /****************************** |
||
| 87 | * Config * |
||
| 88 | ******************************/ |
||
| 89 | |||
| 90 | $this['config'] = function () { |
||
| 91 | 27 | return new Collection(); |
|
| 92 | }; |
||
| 93 | 27 | $this->config['ssh_multiplexing'] = true; |
|
| 94 | 27 | $this->config['default_stage'] = null; |
|
| 95 | |||
| 96 | /****************************** |
||
| 97 | * Core * |
||
| 98 | ******************************/ |
||
| 99 | |||
| 100 | $this['pop'] = function ($c) { |
||
| 101 | 9 | return new ProcessOutputPrinter($c['output'], $c['logger']); |
|
| 102 | }; |
||
| 103 | $this['sshClient'] = function ($c) { |
||
| 104 | return new Ssh\Client($c['output'], $c['pop'], $c['config']['ssh_multiplexing']); |
||
| 105 | }; |
||
| 106 | $this['rsync'] = function ($c) { |
||
| 107 | return new Rsync($c['pop']); |
||
| 108 | }; |
||
| 109 | $this['processRunner'] = function ($c) { |
||
| 110 | 9 | return new ProcessRunner($c['pop']); |
|
| 111 | }; |
||
| 112 | $this['tasks'] = function () { |
||
| 113 | 16 | return new Task\TaskCollection(); |
|
| 114 | }; |
||
| 115 | $this['hosts'] = function () { |
||
| 116 | 16 | return new Host\HostCollection(); |
|
| 117 | }; |
||
| 118 | $this['scriptManager'] = function ($c) { |
||
| 119 | 13 | return new Task\ScriptManager($c['tasks']); |
|
| 120 | }; |
||
| 121 | $this['hostSelector'] = function ($c) { |
||
| 122 | 11 | $defaultStage = $c['config']['default_stage']; |
|
| 123 | 11 | if (is_object($defaultStage) && ($defaultStage instanceof \Closure)) { |
|
| 124 | $defaultStage = call_user_func($defaultStage); |
||
| 125 | } |
||
| 126 | 11 | return new Host\HostSelector($c['hosts'], $defaultStage); |
|
| 127 | }; |
||
| 128 | $this['fail'] = function () { |
||
| 129 | 8 | return new Collection(); |
|
| 130 | }; |
||
| 131 | $this['informer'] = function ($c) { |
||
| 132 | 11 | return new Informer($c['output']); |
|
| 133 | }; |
||
| 134 | $this['seriesExecutor'] = function ($c) { |
||
| 135 | 11 | return new SeriesExecutor($c['input'], $c['output'], $c['informer']); |
|
| 136 | }; |
||
| 137 | $this['parallelExecutor'] = function ($c) { |
||
| 138 | return new ParallelExecutor($c['input'], $c['output'], $c['informer'], $c['console']); |
||
| 139 | }; |
||
| 140 | |||
| 141 | /****************************** |
||
| 142 | * Logger * |
||
| 143 | ******************************/ |
||
| 144 | |||
| 145 | $this['log_handler'] = function () { |
||
| 146 | 9 | return !empty($this->config['log_file']) |
|
| 147 | ? new FileHandler($this->config['log_file']) |
||
| 148 | 9 | : new NullHandler(); |
|
| 149 | }; |
||
| 150 | $this['logger'] = function () { |
||
| 151 | 9 | return new Logger($this['log_handler']); |
|
| 152 | }; |
||
| 153 | |||
| 154 | /****************************** |
||
| 155 | * Init command * |
||
| 156 | ******************************/ |
||
| 157 | |||
| 158 | $this['init_command'] = function () { |
||
| 159 | 12 | return new InitCommand(); |
|
| 160 | }; |
||
| 161 | |||
| 162 | 27 | self::$instance = $this; |
|
| 163 | 27 | } |
|
| 164 | |||
| 165 | /** |
||
| 166 | * @return Deployer |
||
| 167 | */ |
||
| 168 | 29 | public static function get() |
|
| 172 | |||
| 173 | /** |
||
| 174 | * @param string $name |
||
| 175 | * @param mixed $value |
||
| 176 | */ |
||
| 177 | 11 | public static function setDefault($name, $value) |
|
| 181 | |||
| 182 | /** |
||
| 183 | * @param string $name |
||
| 184 | * @param mixed $default |
||
| 185 | * @return mixed |
||
| 186 | */ |
||
| 187 | 14 | public static function getDefault($name, $default = null) |
|
| 191 | |||
| 192 | /** |
||
| 193 | * @param string $name |
||
| 194 | * @return boolean |
||
| 195 | */ |
||
| 196 | 14 | public static function hasDefault($name) |
|
| 200 | |||
| 201 | /** |
||
| 202 | * @param string $name |
||
| 203 | * @param array $array |
||
| 204 | */ |
||
| 205 | 2 | public static function addDefault($name, $array) |
|
| 217 | |||
| 218 | /** |
||
| 219 | * Init console application |
||
| 220 | */ |
||
| 221 | 12 | public function init() |
|
| 232 | |||
| 233 | /** |
||
| 234 | * Transform tasks to console commands. |
||
| 235 | */ |
||
| 236 | 12 | public function addConsoleCommands() |
|
| 248 | |||
| 249 | /** |
||
| 250 | * @param string $name |
||
| 251 | * @return mixed |
||
| 252 | * @throws \InvalidArgumentException |
||
| 253 | */ |
||
| 254 | 32 | public function __get($name) |
|
| 262 | |||
| 263 | /** |
||
| 264 | * @return Application |
||
| 265 | */ |
||
| 266 | 12 | public function getConsole() |
|
| 270 | |||
| 271 | /** |
||
| 272 | * @return Console\Input\InputInterface |
||
| 273 | */ |
||
| 274 | public function getInput() |
||
| 278 | |||
| 279 | /** |
||
| 280 | * @return Console\Output\OutputInterface |
||
| 281 | */ |
||
| 282 | public function getOutput() |
||
| 286 | |||
| 287 | /** |
||
| 288 | * @param string $name |
||
| 289 | * @return Console\Helper\HelperInterface |
||
| 290 | */ |
||
| 291 | public function getHelper($name) |
||
| 295 | |||
| 296 | /** |
||
| 297 | * Run Deployer |
||
| 298 | * |
||
| 299 | * @param string $version |
||
| 300 | * @param string $deployFile |
||
| 301 | */ |
||
| 302 | public static function run($version, $deployFile) |
||
| 303 | { |
||
| 304 | // Init Deployer |
||
| 305 | $console = new Application('Deployer', $version); |
||
| 306 | $input = new ArgvInput(); |
||
| 307 | $output = new ConsoleOutput(); |
||
| 308 | $deployer = new self($console); |
||
| 309 | |||
| 310 | // Pretty-print uncaught exceptions in symfony-console |
||
| 311 | set_exception_handler(function ($e) use ($input, $output, $deployer) { |
||
| 312 | $io = new SymfonyStyle($input, $output); |
||
| 313 | $io->block($e->getMessage(), get_class($e), 'fg=white;bg=red', ' ', true); |
||
| 314 | $io->block($e->getTraceAsString()); |
||
| 315 | |||
| 316 | $deployer->logger->log('['. get_class($e) .'] '. $e->getMessage()); |
||
| 317 | $deployer->logger->log($e->getTraceAsString()); |
||
| 318 | exit(1); |
||
| 319 | }); |
||
| 320 | |||
| 321 | // Require deploy.php file |
||
| 322 | self::loadRecipe($deployFile); |
||
| 323 | |||
| 324 | // Run Deployer |
||
| 325 | $deployer->init(); |
||
| 326 | $console->run($input, $output); |
||
| 327 | } |
||
| 328 | |||
| 329 | /** |
||
| 330 | * Collect anonymous stats about Deployer usage for improving developer experience. |
||
| 331 | * If you are not comfortable with this, you will always be able to disable this |
||
| 332 | * by setting `allow_anonymous_stats` to false in your deploy.php file. |
||
| 333 | * |
||
| 334 | * @param CommandEvent $commandEvent |
||
| 335 | * @codeCoverageIgnore |
||
| 336 | */ |
||
| 337 | public function collectAnonymousStats(CommandEvent $commandEvent) |
||
| 372 | |||
| 373 | /** |
||
| 374 | * Load recipe file |
||
| 375 | * |
||
| 376 | * @param string $deployFile |
||
| 377 | * |
||
| 378 | * @return void |
||
| 379 | * @codeCoverageIgnore |
||
| 380 | */ |
||
| 381 | public static function loadRecipe($deployFile) |
||
| 399 | |||
| 400 | /** |
||
| 401 | * @return string |
||
| 402 | * @codeCoverageIgnore |
||
| 403 | */ |
||
| 404 | public static function getCalledScript(): string |
||
| 415 | } |
||
| 416 |