Complex classes like HelpController 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 HelpController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
38 | class HelpController extends Controller |
||
39 | { |
||
40 | /** |
||
41 | * Displays available commands or the detailed information |
||
42 | * about a particular command. |
||
43 | * |
||
44 | * @param string $command The name of the command to show help about. |
||
45 | * If not provided, all available commands will be displayed. |
||
46 | * @return int the exit status |
||
47 | * @throws Exception if the command for help is unknown |
||
48 | */ |
||
49 | 3 | public function actionIndex($command = null) |
|
50 | { |
||
51 | 3 | if ($command !== null) { |
|
52 | 2 | $result = Yii::$app->createController($command); |
|
53 | 2 | if ($result === false) { |
|
54 | $name = $this->ansiFormat($command, Console::FG_YELLOW); |
||
55 | throw new Exception("No help for unknown command \"$name\"."); |
||
56 | } |
||
57 | |||
58 | 2 | list($controller, $actionID) = $result; |
|
59 | |||
60 | 2 | $actions = $this->getActions($controller); |
|
61 | 2 | if ($actionID !== '' || count($actions) === 1 && $actions[0] === $controller->defaultAction) { |
|
62 | 2 | $this->getSubCommandHelp($controller, $actionID); |
|
63 | } else { |
||
64 | 2 | $this->getCommandHelp($controller); |
|
65 | } |
||
66 | } else { |
||
67 | 1 | $this->getDefaultHelp(); |
|
68 | } |
||
69 | 3 | } |
|
70 | |||
71 | /** |
||
72 | * List all available controllers and actions in machine readable format. |
||
73 | * This is used for shell completion. |
||
74 | * @since 2.0.11 |
||
75 | */ |
||
76 | 2 | public function actionList() |
|
96 | |||
97 | /** |
||
98 | * List all available options for the $action in machine readable format. |
||
99 | * This is used for shell completion. |
||
100 | * |
||
101 | * @param string $action route to action |
||
102 | * @since 2.0.11 |
||
103 | */ |
||
104 | 1 | public function actionListActionOptions($action) |
|
132 | |||
133 | /** |
||
134 | * Displays usage information for $action. |
||
135 | * |
||
136 | * @param string $action route to action |
||
137 | * @since 2.0.11 |
||
138 | */ |
||
139 | 1 | public function actionUsage($action) |
|
140 | { |
||
141 | 1 | $result = Yii::$app->createController($action); |
|
142 | |||
143 | 1 | if ($result === false || !($result[0] instanceof Controller)) { |
|
144 | return; |
||
145 | } |
||
146 | |||
147 | /** @var Controller $controller */ |
||
148 | 1 | list($controller, $actionID) = $result; |
|
149 | 1 | $action = $controller->createAction($actionID); |
|
150 | 1 | if ($action === null) { |
|
151 | return; |
||
152 | } |
||
153 | |||
154 | 1 | $scriptName = $this->getScriptName(); |
|
155 | 1 | if ($action->id === $controller->defaultAction) { |
|
156 | $this->stdout($scriptName . ' ' . $this->ansiFormat($controller->getUniqueId(), Console::FG_YELLOW)); |
||
157 | } else { |
||
158 | 1 | $this->stdout($scriptName . ' ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW)); |
|
159 | } |
||
160 | |||
161 | 1 | $args = $controller->getActionArgsHelp($action); |
|
162 | 1 | foreach ($args as $name => $arg) { |
|
163 | 1 | if ($arg['required']) { |
|
164 | 1 | $this->stdout(' <' . $name . '>', Console::FG_CYAN); |
|
165 | } else { |
||
166 | 1 | $this->stdout(' [' . $name . ']', Console::FG_CYAN); |
|
167 | } |
||
168 | } |
||
169 | |||
170 | 1 | $this->stdout("\n"); |
|
171 | 1 | } |
|
172 | |||
173 | /** |
||
174 | * Returns all available command names. |
||
175 | * @return array all available command names |
||
176 | */ |
||
177 | 18 | public function getCommands() |
|
183 | |||
184 | /** |
||
185 | * Returns an array of commands an their descriptions. |
||
186 | * @return array all available commands as keys and their description as values. |
||
187 | */ |
||
188 | 3 | protected function getCommandDescriptions() |
|
206 | |||
207 | /** |
||
208 | * Returns all available actions of the specified controller. |
||
209 | * @param Controller $controller the controller instance |
||
210 | * @return array all available action IDs. |
||
211 | */ |
||
212 | 20 | public function getActions($controller) |
|
226 | |||
227 | /** |
||
228 | * Returns available commands of a specified module. |
||
229 | * @param \yii\base\Module $module the module instance |
||
230 | * @return array the available command names |
||
231 | */ |
||
232 | 18 | protected function getModuleCommands($module) |
|
233 | { |
||
234 | 18 | $prefix = $module instanceof Application ? '' : $module->getUniqueId() . '/'; |
|
235 | |||
236 | 18 | $commands = []; |
|
237 | 18 | foreach (array_keys($module->controllerMap) as $id) { |
|
238 | 18 | $commands[] = $prefix . $id; |
|
239 | } |
||
240 | |||
241 | 18 | foreach ($module->getModules() as $id => $child) { |
|
242 | 1 | if (($child = $module->getModule($id)) === null) { |
|
243 | continue; |
||
244 | } |
||
245 | 1 | foreach ($this->getModuleCommands($child) as $command) { |
|
246 | 1 | $commands[] = $command; |
|
247 | } |
||
248 | } |
||
249 | |||
250 | 18 | $controllerPath = $module->getControllerPath(); |
|
251 | 18 | if (is_dir($controllerPath)) { |
|
252 | 1 | $files = scandir($controllerPath); |
|
253 | 1 | foreach ($files as $file) { |
|
254 | 1 | if (!empty($file) && substr_compare($file, 'Controller.php', -14, 14) === 0) { |
|
255 | 1 | $controllerClass = $module->controllerNamespace . '\\' . substr(basename($file), 0, -4); |
|
256 | 1 | if ($this->validateControllerClass($controllerClass)) { |
|
257 | 1 | $commands[] = $prefix . Inflector::camel2id(substr(basename($file), 0, -14), '-', true); |
|
258 | } |
||
259 | } |
||
260 | } |
||
261 | } |
||
262 | |||
263 | 18 | return $commands; |
|
264 | } |
||
265 | |||
266 | /** |
||
267 | * Validates if the given class is a valid console controller class. |
||
268 | * @param string $controllerClass |
||
269 | * @return bool |
||
270 | */ |
||
271 | 1 | protected function validateControllerClass($controllerClass) |
|
272 | { |
||
273 | 1 | if (class_exists($controllerClass)) { |
|
274 | 1 | $class = new \ReflectionClass($controllerClass); |
|
275 | 1 | return !$class->isAbstract() && $class->isSubclassOf('yii\console\Controller'); |
|
276 | } |
||
277 | |||
278 | return false; |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * Displays all available commands. |
||
283 | */ |
||
284 | 1 | protected function getDefaultHelp() |
|
285 | { |
||
286 | 1 | $commands = $this->getCommandDescriptions(); |
|
287 | 1 | $this->stdout($this->getDefaultHelpHeader()); |
|
288 | 1 | if (!empty($commands)) { |
|
289 | 1 | $this->stdout("\nThe following commands are available:\n\n", Console::BOLD); |
|
290 | 1 | $len = 0; |
|
291 | 1 | foreach ($commands as $command => $description) { |
|
292 | 1 | $result = Yii::$app->createController($command); |
|
293 | 1 | if ($result !== false && $result[0] instanceof Controller) { |
|
294 | /** @var $controller Controller */ |
||
295 | 1 | list($controller, $actionID) = $result; |
|
296 | 1 | $actions = $this->getActions($controller); |
|
297 | 1 | if (!empty($actions)) { |
|
298 | 1 | $prefix = $controller->getUniqueId(); |
|
299 | 1 | foreach ($actions as $action) { |
|
300 | 1 | $string = $prefix . '/' . $action; |
|
301 | 1 | if ($action === $controller->defaultAction) { |
|
302 | 1 | $string .= ' (default)'; |
|
303 | } |
||
304 | 1 | if (($l = strlen($string)) > $len) { |
|
305 | 1 | $len = $l; |
|
306 | } |
||
307 | } |
||
308 | } |
||
309 | } elseif (($l = strlen($command)) > $len) { |
||
310 | 1 | $len = $l; |
|
311 | } |
||
312 | } |
||
313 | 1 | foreach ($commands as $command => $description) { |
|
314 | 1 | $this->stdout('- ' . $this->ansiFormat($command, Console::FG_YELLOW)); |
|
315 | 1 | $this->stdout(str_repeat(' ', $len + 4 - strlen($command))); |
|
316 | 1 | $this->stdout(Console::wrapText($description, $len + 4 + 2), Console::BOLD); |
|
317 | 1 | $this->stdout("\n"); |
|
318 | |||
319 | 1 | $result = Yii::$app->createController($command); |
|
320 | 1 | if ($result !== false && $result[0] instanceof Controller) { |
|
321 | 1 | list($controller, $actionID) = $result; |
|
322 | 1 | $actions = $this->getActions($controller); |
|
323 | 1 | if (!empty($actions)) { |
|
324 | 1 | $prefix = $controller->getUniqueId(); |
|
325 | 1 | foreach ($actions as $action) { |
|
326 | 1 | $string = ' ' . $prefix . '/' . $action; |
|
327 | 1 | $this->stdout(' ' . $this->ansiFormat($string, Console::FG_GREEN)); |
|
328 | 1 | if ($action === $controller->defaultAction) { |
|
329 | 1 | $string .= ' (default)'; |
|
330 | 1 | $this->stdout(' (default)', Console::FG_YELLOW); |
|
331 | } |
||
332 | 1 | $summary = $controller->getActionHelpSummary($controller->createAction($action)); |
|
333 | 1 | if ($summary !== '') { |
|
334 | 1 | $this->stdout(str_repeat(' ', $len + 4 - strlen($string))); |
|
335 | 1 | $this->stdout(Console::wrapText($summary, $len + 4 + 2)); |
|
336 | } |
||
337 | 1 | $this->stdout("\n"); |
|
338 | } |
||
339 | } |
||
340 | 1 | $this->stdout("\n"); |
|
341 | } |
||
342 | } |
||
343 | 1 | $scriptName = $this->getScriptName(); |
|
344 | 1 | $this->stdout("\nTo see the help of each command, enter:\n", Console::BOLD); |
|
345 | 1 | $this->stdout("\n $scriptName " . $this->ansiFormat('help', Console::FG_YELLOW) . ' ' |
|
346 | 1 | . $this->ansiFormat('<command-name>', Console::FG_CYAN) . "\n\n"); |
|
347 | } else { |
||
348 | $this->stdout("\nNo commands are found.\n\n", Console::BOLD); |
||
349 | } |
||
350 | 1 | } |
|
351 | |||
352 | /** |
||
353 | * Displays the overall information of the command. |
||
354 | * @param Controller $controller the controller instance |
||
355 | */ |
||
356 | protected function getCommandHelp($controller) |
||
397 | |||
398 | /** |
||
399 | * Displays the detailed information of a command action. |
||
400 | * @param Controller $controller the controller instance |
||
401 | * @param string $actionID action ID |
||
402 | * @throws Exception if the action does not exist |
||
403 | */ |
||
404 | 2 | protected function getSubCommandHelp($controller, $actionID) |
|
471 | |||
472 | /** |
||
473 | * Generates a well-formed string for an argument or option. |
||
474 | * @param string $name the name of the argument or option |
||
475 | * @param bool $required whether the argument is required |
||
476 | * @param string $type the type of the option or argument |
||
477 | * @param mixed $defaultValue the default value of the option or argument |
||
478 | * @param string $comment comment about the option or argument |
||
479 | * @return string the formatted string for the argument or option |
||
480 | */ |
||
481 | 2 | protected function formatOptionHelp($name, $required, $type, $defaultValue, $comment) |
|
517 | |||
518 | /** |
||
519 | * @param Controller $controller the controller instance |
||
520 | * @param string $option the option name |
||
521 | * @return string the formatted string for the alias argument or option |
||
522 | * @since 2.0.8 |
||
523 | */ |
||
524 | 2 | protected function formatOptionAliases($controller, $option) |
|
535 | |||
536 | /** |
||
537 | * @return string the name of the cli script currently running. |
||
538 | */ |
||
539 | 4 | protected function getScriptName() |
|
543 | |||
544 | /** |
||
545 | * Return a default help header. |
||
546 | * @return string default help header. |
||
547 | * @since 2.0.11 |
||
548 | */ |
||
549 | 1 | protected function getDefaultHelpHeader() |
|
553 | } |
||
554 |
This checks looks for assignemnts to variables using the
list(...)
function, where not all assigned variables are subsequently used.Consider the following code example.
Only the variables
$a
and$c
are used. There was no need to assign$b
.Instead, the list call could have been.