Total Complexity | 78 |
Total Lines | 612 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Command 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 Command, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
59 | class Command |
||
60 | { |
||
61 | /** |
||
62 | * Marks a cli error exit. |
||
63 | */ |
||
64 | const CLI_ERROR = 1742; |
||
65 | |||
66 | /** |
||
67 | * Marks an input error exit. |
||
68 | */ |
||
69 | const INPUT_ERROR = 1743; |
||
70 | |||
71 | /** |
||
72 | * The recieved cli options |
||
73 | * |
||
74 | * @var array<string, mixed> |
||
75 | */ |
||
76 | private $options = array(); |
||
77 | |||
78 | /** |
||
79 | * The directories/files to be analyzed |
||
80 | * |
||
81 | * @var array<int, string> |
||
82 | */ |
||
83 | private $source = array(); |
||
84 | |||
85 | /** |
||
86 | * The used text ui runner. |
||
87 | * |
||
88 | * @var Runner |
||
89 | */ |
||
90 | private $runner = null; |
||
91 | |||
92 | /** |
||
93 | * @var Application |
||
94 | */ |
||
95 | private $application; |
||
96 | |||
97 | /** |
||
98 | * Performs the main cli process and returns the exit code. |
||
99 | * |
||
100 | * @return int |
||
101 | */ |
||
102 | public function run() |
||
103 | { |
||
104 | $this->application = new Application(); |
||
105 | |||
106 | try { |
||
107 | if ($this->parseArguments() === false) { |
||
108 | $this->printHelp(); |
||
109 | return self::CLI_ERROR; |
||
110 | } |
||
111 | } catch (Exception $e) { |
||
112 | echo $e->getMessage(), PHP_EOL, PHP_EOL; |
||
113 | |||
114 | $this->printHelp(); |
||
115 | return self::CLI_ERROR; |
||
116 | } |
||
117 | |||
118 | if (isset($this->options['--help'])) { |
||
119 | $this->printHelp(); |
||
120 | return Runner::SUCCESS_EXIT; |
||
121 | } |
||
122 | if (isset($this->options['--usage'])) { |
||
123 | $this->printUsage(); |
||
124 | return Runner::SUCCESS_EXIT; |
||
125 | } |
||
126 | if (isset($this->options['--version'])) { |
||
127 | $this->printVersion(); |
||
128 | return Runner::SUCCESS_EXIT; |
||
129 | } |
||
130 | |||
131 | $configurationFile = false; |
||
132 | |||
133 | if (isset($this->options['--configuration'])) { |
||
134 | $configurationFile = $this->options['--configuration']; |
||
135 | |||
136 | if (false === file_exists($configurationFile)) { |
||
137 | $configurationFile = getcwd() . '/' . $configurationFile; |
||
138 | } |
||
139 | if (false === file_exists($configurationFile)) { |
||
140 | $configurationFile = $this->options['--configuration']; |
||
141 | } |
||
142 | |||
143 | unset($this->options['--configuration']); |
||
144 | } elseif (file_exists(getcwd() . '/pdepend.xml')) { |
||
145 | $configurationFile = getcwd() . '/pdepend.xml'; |
||
146 | } elseif (file_exists(getcwd() . '/pdepend.xml.dist')) { |
||
147 | $configurationFile = getcwd() . '/pdepend.xml.dist'; |
||
148 | } |
||
149 | |||
150 | if ($configurationFile) { |
||
151 | try { |
||
152 | $this->application->setConfigurationFile($configurationFile); |
||
153 | } catch (Exception $e) { |
||
154 | echo $e->getMessage(), PHP_EOL, PHP_EOL; |
||
155 | |||
156 | $this->printHelp(); |
||
157 | return self::CLI_ERROR; |
||
158 | } |
||
159 | } |
||
160 | |||
161 | // Create a new text ui runner |
||
162 | $this->runner = $this->application->getRunner(); |
||
163 | |||
164 | $this->assignArguments(); |
||
165 | |||
166 | // Get a copy of all options |
||
167 | $options = $this->options; |
||
168 | |||
169 | // Get an array with all available log options |
||
170 | $logOptions = $this->application->getAvailableLoggerOptions(); |
||
171 | |||
172 | // Get an array with all available analyzer options |
||
173 | $analyzerOptions = $this->application->getAvailableAnalyzerOptions(); |
||
174 | |||
175 | foreach ($options as $option => $value) { |
||
176 | if (isset($logOptions[$option])) { |
||
177 | // Reduce received option list |
||
178 | unset($options[$option]); |
||
179 | // Register logger |
||
180 | $this->runner->addReportGenerator(substr($option, 2), $value); |
||
181 | } elseif (isset($analyzerOptions[$option])) { |
||
182 | // Reduce received option list |
||
183 | unset($options[$option]); |
||
184 | |||
185 | if (isset($analyzerOptions[$option]['value']) && is_bool($value)) { |
||
186 | echo 'Option ', $option, ' requires a value.', PHP_EOL; |
||
187 | return self::INPUT_ERROR; |
||
188 | } elseif ($analyzerOptions[$option]['value'] === 'file' |
||
189 | && file_exists($value) === false |
||
190 | ) { |
||
191 | echo 'Specified file ', $option, '=', $value, |
||
192 | ' not exists.', PHP_EOL; |
||
193 | |||
194 | return self::INPUT_ERROR; |
||
195 | } elseif ($analyzerOptions[$option]['value'] === '*[,...]') { |
||
196 | $value = array_map('trim', explode(',', $value)); |
||
197 | } |
||
198 | $this->runner->addOption(substr($option, 2), $value); |
||
199 | } |
||
200 | } |
||
201 | |||
202 | if (isset($options['--without-annotations'])) { |
||
203 | // Disable annotation parsing |
||
204 | $this->runner->setWithoutAnnotations(); |
||
205 | // Remove option |
||
206 | unset($options['--without-annotations']); |
||
207 | } |
||
208 | |||
209 | if (isset($options['--optimization'])) { |
||
210 | // This option is deprecated. |
||
211 | echo 'Option --optimization is ambiguous.', PHP_EOL; |
||
212 | // Remove option |
||
213 | unset($options['--optimization']); |
||
214 | } |
||
215 | |||
216 | if (isset($options['--quiet'])) { |
||
217 | $runSilent = true; |
||
218 | unset($options['--quiet']); |
||
219 | } else { |
||
220 | $runSilent = false; |
||
221 | $this->runner->addProcessListener(new ResultPrinter()); |
||
222 | } |
||
223 | |||
224 | if (isset($options['--notify-me'])) { |
||
225 | $this->runner->addProcessListener( |
||
226 | new DbusResultPrinter() |
||
227 | ); |
||
228 | unset($options['--notify-me']); |
||
229 | } |
||
230 | |||
231 | if (count($options) > 0) { |
||
232 | $this->printHelp(); |
||
233 | echo "Unknown option '", key($options), "' given.", PHP_EOL; |
||
234 | return self::CLI_ERROR; |
||
235 | } |
||
236 | |||
237 | try { |
||
238 | // Output current pdepend version and author |
||
239 | if ($runSilent === false) { |
||
240 | $this->printVersion(); |
||
241 | $this->printWorkarounds(); |
||
242 | } |
||
243 | |||
244 | $startTime = time(); |
||
245 | |||
246 | $result = $this->runner->run(); |
||
247 | |||
248 | if ($this->runner->hasParseErrors() === true) { |
||
249 | $errors = $this->runner->getParseErrors(); |
||
250 | |||
251 | printf( |
||
252 | '%sThe following error%s occurred:%s', |
||
253 | PHP_EOL, |
||
254 | count($errors) > 1 ? 's' : '', |
||
255 | PHP_EOL |
||
256 | ); |
||
257 | |||
258 | foreach ($errors as $error) { |
||
259 | echo $error, PHP_EOL; |
||
260 | } |
||
261 | echo PHP_EOL; |
||
262 | } |
||
263 | if ($runSilent === false) { |
||
264 | $this->printStatistics($startTime); |
||
265 | } |
||
266 | |||
267 | return $result; |
||
268 | } catch (RuntimeException $e) { |
||
269 | echo PHP_EOL, PHP_EOL, |
||
270 | 'Critical error:', PHP_EOL, |
||
271 | '===============', PHP_EOL, |
||
272 | $e->getMessage(), PHP_EOL; |
||
273 | |||
274 | Log::debug($this->getErrorTrace($e)); |
||
275 | |||
276 | for ($previous = $e->getPrevious(); $previous; $previous = $previous->getPrevious()) { |
||
277 | Log::debug(PHP_EOL . 'Caused by:' . PHP_EOL . $this->getErrorTrace($previous)); |
||
278 | } |
||
279 | |||
280 | return $e->getCode(); |
||
281 | } |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Parses the cli arguments. |
||
286 | * |
||
287 | * @return bool |
||
288 | */ |
||
289 | protected function parseArguments() |
||
290 | { |
||
291 | if (!isset($_SERVER['argv'])) { |
||
292 | if (false === (boolean) ini_get('register_argc_argv')) { |
||
293 | // @codeCoverageIgnoreStart |
||
294 | echo 'Please enable register_argc_argv in your php.ini.'; |
||
295 | } else { |
||
296 | // @codeCoverageIgnoreEnd |
||
297 | echo 'Unknown error, no $argv array available.'; |
||
298 | } |
||
299 | echo PHP_EOL, PHP_EOL; |
||
300 | return false; |
||
301 | } |
||
302 | |||
303 | $argv = $_SERVER['argv']; |
||
304 | |||
305 | // Remove the pdepend command line file |
||
306 | array_shift($argv); |
||
307 | |||
308 | if (count($argv) === 0) { |
||
309 | return false; |
||
310 | } |
||
311 | |||
312 | // Last argument must be a list of source directories |
||
313 | if (strpos(end($argv), '--') !== 0) { |
||
314 | $this->source = explode(',', array_pop($argv)); |
||
315 | } |
||
316 | |||
317 | for ($i = 0, $c = count($argv); $i < $c; ++$i) { |
||
318 | // Is it an ini_set option? |
||
319 | $arg = (string)$argv[$i]; |
||
320 | if ($arg === '-d' && isset($argv[$i + 1])) { |
||
321 | $arg = (string)$argv[++$i]; |
||
322 | if (strpos($arg, '=') === false) { |
||
323 | ini_set($arg, 'on'); |
||
324 | } else { |
||
325 | list($key, $value) = explode('=', $arg); |
||
326 | |||
327 | ini_set($key, $value); |
||
328 | } |
||
329 | } elseif (strpos($arg, '=') === false) { |
||
330 | $this->options[$arg] = true; |
||
331 | } else { |
||
332 | list($key, $value) = explode('=', $arg); |
||
333 | |||
334 | $this->options[$key] = $value; |
||
335 | } |
||
336 | } |
||
337 | |||
338 | return true; |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * Assign CLI arguments to current runner instance |
||
343 | * |
||
344 | * @return void |
||
345 | */ |
||
346 | protected function assignArguments() |
||
347 | { |
||
348 | if ($this->source) { |
||
|
|||
349 | $this->runner->setSourceArguments($this->source); |
||
350 | } |
||
351 | |||
352 | // Check for suffix option |
||
353 | if (isset($this->options['--suffix'])) { |
||
354 | // Get file extensions |
||
355 | $extensions = explode(',', $this->options['--suffix']); |
||
356 | // Set allowed file extensions |
||
357 | $this->runner->setFileExtensions($extensions); |
||
358 | |||
359 | unset($this->options['--suffix']); |
||
360 | } |
||
361 | |||
362 | // Check for ignore option |
||
363 | if (isset($this->options['--ignore'])) { |
||
364 | // Get exclude directories |
||
365 | $directories = explode(',', $this->options['--ignore']); |
||
366 | // Set exclude directories |
||
367 | $this->runner->setExcludeDirectories($directories); |
||
368 | |||
369 | unset($this->options['--ignore']); |
||
370 | } |
||
371 | |||
372 | // Check for exclude namespace option |
||
373 | if (isset($this->options['--exclude'])) { |
||
374 | // Get exclude directories |
||
375 | $namespaces = explode(',', $this->options['--exclude']); |
||
376 | // Set exclude namespace |
||
377 | $this->runner->setExcludeNamespaces($namespaces); |
||
378 | |||
379 | unset($this->options['--exclude']); |
||
380 | } |
||
381 | |||
382 | // Check for the bad documentation option |
||
383 | if (isset($this->options['--bad-documentation'])) { |
||
384 | echo "Option --bad-documentation is ambiguous.", PHP_EOL; |
||
385 | |||
386 | unset($this->options['--bad-documentation']); |
||
387 | } |
||
388 | |||
389 | $configuration = $this->application->getConfiguration(); |
||
390 | |||
391 | // Store in config registry |
||
392 | ConfigurationInstance::set($configuration); |
||
393 | |||
394 | if (isset($this->options['--debug'])) { |
||
395 | unset($this->options['--debug']); |
||
396 | |||
397 | Log::setSeverity(Log::DEBUG); |
||
398 | } |
||
399 | } |
||
400 | |||
401 | /** |
||
402 | * Outputs the current PDepend version. |
||
403 | * |
||
404 | * @return void |
||
405 | */ |
||
406 | protected function printVersion() |
||
407 | { |
||
408 | $build = __DIR__ . '/../../../../../build.properties'; |
||
409 | |||
410 | $version = '@package_version@'; |
||
411 | if (file_exists($build)) { |
||
412 | $data = @parse_ini_file($build); |
||
413 | if (is_array($data)) { |
||
414 | $version = $data['project.version']; |
||
415 | } |
||
416 | } |
||
417 | |||
418 | echo 'PDepend ', $version, PHP_EOL, PHP_EOL; |
||
419 | } |
||
420 | |||
421 | /** |
||
422 | * If the current PHP installation requires some workarounds or limitations, |
||
423 | * this method will output a message on STDOUT. |
||
424 | * |
||
425 | * @return void |
||
426 | */ |
||
427 | protected function printWorkarounds() |
||
428 | { |
||
429 | $workarounds = new Workarounds(); |
||
430 | |||
431 | if ($workarounds->isNotRequired()) { |
||
432 | return; |
||
433 | } |
||
434 | |||
435 | echo 'Your PHP version requires some workaround:', PHP_EOL; |
||
436 | foreach ($workarounds->getRequiredWorkarounds() as $workaround) { |
||
437 | echo '- ', $workaround, PHP_EOL; |
||
438 | } |
||
439 | echo PHP_EOL; |
||
440 | } |
||
441 | |||
442 | /** |
||
443 | * Outputs the base usage of PDepend. |
||
444 | * |
||
445 | * @return void |
||
446 | */ |
||
447 | protected function printUsage() |
||
448 | { |
||
449 | $this->printVersion(); |
||
450 | echo 'Usage: pdepend [options] [logger] <dir[,dir[,...]]>', PHP_EOL, PHP_EOL; |
||
451 | } |
||
452 | |||
453 | /** |
||
454 | * Outputs the main help of PDepend. |
||
455 | * |
||
456 | * @return void |
||
457 | */ |
||
458 | protected function printHelp() |
||
459 | { |
||
460 | $this->printUsage(); |
||
461 | |||
462 | $length = $this->printLogOptions(); |
||
463 | $length = $this->printAnalyzerOptions($length); |
||
464 | |||
465 | $this->printOption( |
||
466 | '--configuration=<file>', |
||
467 | 'Optional PDepend configuration file.', |
||
468 | $length |
||
469 | ); |
||
470 | echo PHP_EOL; |
||
471 | |||
472 | $this->printOption( |
||
473 | '--suffix=<ext[,...]>', |
||
474 | 'List of valid PHP file extensions.', |
||
475 | $length |
||
476 | ); |
||
477 | $this->printOption( |
||
478 | '--ignore=<dir[,...]>', |
||
479 | 'List of exclude directories.', |
||
480 | $length |
||
481 | ); |
||
482 | $this->printOption( |
||
483 | '--exclude=<pkg[,...]>', |
||
484 | 'List of exclude namespaces.', |
||
485 | $length |
||
486 | ); |
||
487 | echo PHP_EOL; |
||
488 | |||
489 | $this->printOption( |
||
490 | '--without-annotations', |
||
491 | 'Do not parse doc comment annotations.', |
||
492 | $length |
||
493 | ); |
||
494 | echo PHP_EOL; |
||
495 | |||
496 | $this->printOption('--quiet', 'Prints errors only.', $length); |
||
497 | $this->printOption('--debug', 'Prints debugging information.', $length); |
||
498 | $this->printOption('--help', 'Print this help text.', $length); |
||
499 | $this->printOption('--version', 'Print the current version.', $length); |
||
500 | |||
501 | $this->printDbusOption($length); |
||
502 | |||
503 | $this->printOption('-d key[=value]', 'Sets a php.ini value.', $length); |
||
504 | echo PHP_EOL; |
||
505 | } |
||
506 | |||
507 | /** |
||
508 | * Prints all available log options and returns the length of the longest |
||
509 | * option. |
||
510 | * |
||
511 | * @return int |
||
512 | */ |
||
513 | protected function printLogOptions() |
||
514 | { |
||
515 | $maxLength = 0; |
||
516 | $options = array(); |
||
517 | $logOptions = $this->application->getAvailableLoggerOptions(); |
||
518 | foreach ($logOptions as $option => $info) { |
||
519 | // Build log option identifier |
||
520 | $identifier = sprintf('%s=<%s>', $option, $info['value']); |
||
521 | // Store in options array |
||
522 | $options[$identifier] = $info['message']; |
||
523 | |||
524 | $length = strlen($identifier); |
||
525 | if ($length > $maxLength) { |
||
526 | $maxLength = $length; |
||
527 | } |
||
528 | } |
||
529 | |||
530 | ksort($options); |
||
531 | |||
532 | $last = null; |
||
533 | foreach ($options as $option => $message) { |
||
534 | $pos = strrpos($option, '-'); |
||
535 | $current = substr($option, 0, $pos === false ? null : $pos); |
||
536 | if ($last !== null && $last !== $current) { |
||
537 | echo PHP_EOL; |
||
538 | } |
||
539 | $last = $current; |
||
540 | |||
541 | $this->printOption($option, $message, $maxLength); |
||
542 | } |
||
543 | echo PHP_EOL; |
||
544 | |||
545 | return $maxLength; |
||
546 | } |
||
547 | |||
548 | /** |
||
549 | * Prints the analyzer options. |
||
550 | * |
||
551 | * @param int $length Length of the longest option. |
||
552 | * |
||
553 | * @return int |
||
554 | */ |
||
555 | protected function printAnalyzerOptions($length) |
||
556 | { |
||
557 | $options = $this->application->getAvailableAnalyzerOptions(); |
||
558 | |||
559 | if (count($options) === 0) { |
||
560 | return $length; |
||
561 | } |
||
562 | |||
563 | ksort($options); |
||
564 | |||
565 | foreach ($options as $option => $info) { |
||
566 | if (isset($info['value'])) { |
||
567 | $option .= '=<' . $info['value'] . '>'; |
||
568 | } else { |
||
569 | $option .= '=<value>'; |
||
570 | } |
||
571 | |||
572 | $this->printOption($option, $info['message'], $length); |
||
573 | } |
||
574 | echo PHP_EOL; |
||
575 | |||
576 | return $length; |
||
577 | } |
||
578 | |||
579 | /** |
||
580 | * Prints a single option. |
||
581 | * |
||
582 | * @param string $option The option identifier. |
||
583 | * @param string $message The option help message. |
||
584 | * @param int $length The length of the longest option. |
||
585 | * |
||
586 | * @return void |
||
587 | */ |
||
588 | private function printOption($option, $message, $length) |
||
589 | { |
||
590 | // Ignore the phpunit xml option |
||
591 | if (0 === strpos($option, '--phpunit-xml=')) { |
||
592 | return; |
||
593 | } |
||
594 | |||
595 | // Calculate the max message length |
||
596 | $mlength = 77 - $length; |
||
597 | |||
598 | $option = str_pad($option, $length, ' ', STR_PAD_RIGHT); |
||
599 | echo ' ', $option, ' '; |
||
600 | |||
601 | $lines = explode(PHP_EOL, wordwrap($message, $mlength, PHP_EOL)); |
||
602 | echo array_shift($lines); |
||
603 | |||
604 | while (($line = array_shift($lines)) !== null) { |
||
605 | echo PHP_EOL, str_repeat(' ', $length + 3), $line; |
||
606 | } |
||
607 | echo PHP_EOL; |
||
608 | } |
||
609 | |||
610 | /** |
||
611 | * Optionally outputs the dbus option when the required extension |
||
612 | * is loaded. |
||
613 | * |
||
614 | * @param int $length Padding length for the option. |
||
615 | * |
||
616 | * @return void |
||
617 | */ |
||
618 | private function printDbusOption($length) |
||
628 | } |
||
629 | |||
630 | /** |
||
631 | * Main method that starts the command line runner. |
||
632 | * |
||
633 | * @return int The exit code. |
||
634 | */ |
||
635 | public static function main() |
||
640 | } |
||
641 | |||
642 | /** |
||
643 | * @param int $startTime |
||
644 | * |
||
645 | * @return void |
||
646 | */ |
||
647 | private function printStatistics($startTime) |
||
659 | } |
||
660 | |||
661 | /** |
||
662 | * @param Exception|\Throwable $exception |
||
663 | * |
||
664 | * @return string |
||
665 | */ |
||
666 | private function getErrorTrace($exception) |
||
671 | } |
||
672 | } |
||
673 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.