Application.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Console;
  13. use PhpCsFixer\Console\Command\CheckCommand;
  14. use PhpCsFixer\Console\Command\DescribeCommand;
  15. use PhpCsFixer\Console\Command\FixCommand;
  16. use PhpCsFixer\Console\Command\HelpCommand;
  17. use PhpCsFixer\Console\Command\ListFilesCommand;
  18. use PhpCsFixer\Console\Command\ListSetsCommand;
  19. use PhpCsFixer\Console\Command\SelfUpdateCommand;
  20. use PhpCsFixer\Console\Command\WorkerCommand;
  21. use PhpCsFixer\Console\SelfUpdate\GithubClient;
  22. use PhpCsFixer\Console\SelfUpdate\NewVersionChecker;
  23. use PhpCsFixer\PharChecker;
  24. use PhpCsFixer\Runner\Parallel\WorkerException;
  25. use PhpCsFixer\ToolInfo;
  26. use PhpCsFixer\Utils;
  27. use Symfony\Component\Console\Application as BaseApplication;
  28. use Symfony\Component\Console\Command\Command;
  29. use Symfony\Component\Console\Command\ListCommand;
  30. use Symfony\Component\Console\Input\InputInterface;
  31. use Symfony\Component\Console\Output\ConsoleOutputInterface;
  32. use Symfony\Component\Console\Output\OutputInterface;
  33. /**
  34. * @author Fabien Potencier <fabien@symfony.com>
  35. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  36. *
  37. * @internal
  38. */
  39. final class Application extends BaseApplication
  40. {
  41. public const NAME = 'PHP CS Fixer';
  42. public const VERSION = '3.58.1';
  43. public const VERSION_CODENAME = '7th Gear';
  44. private ToolInfo $toolInfo;
  45. private ?Command $executedCommand = null;
  46. public function __construct()
  47. {
  48. parent::__construct(self::NAME, self::VERSION);
  49. $this->toolInfo = new ToolInfo();
  50. // in alphabetical order
  51. $this->add(new DescribeCommand());
  52. $this->add(new CheckCommand($this->toolInfo));
  53. $this->add(new FixCommand($this->toolInfo));
  54. $this->add(new ListFilesCommand($this->toolInfo));
  55. $this->add(new ListSetsCommand());
  56. $this->add(new SelfUpdateCommand(
  57. new NewVersionChecker(new GithubClient()),
  58. $this->toolInfo,
  59. new PharChecker()
  60. ));
  61. $this->add(new WorkerCommand($this->toolInfo));
  62. }
  63. public static function getMajorVersion(): int
  64. {
  65. return (int) explode('.', self::VERSION)[0];
  66. }
  67. public function doRun(InputInterface $input, OutputInterface $output): int
  68. {
  69. $stdErr = $output instanceof ConsoleOutputInterface
  70. ? $output->getErrorOutput()
  71. : ($input->hasParameterOption('--format', true) && 'txt' !== $input->getParameterOption('--format', null, true) ? null : $output);
  72. if (null !== $stdErr) {
  73. $warningsDetector = new WarningsDetector($this->toolInfo);
  74. $warningsDetector->detectOldVendor();
  75. $warningsDetector->detectOldMajor();
  76. $warnings = $warningsDetector->getWarnings();
  77. if (\count($warnings) > 0) {
  78. foreach ($warnings as $warning) {
  79. $stdErr->writeln(sprintf($stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s', $warning));
  80. }
  81. $stdErr->writeln('');
  82. }
  83. }
  84. $result = parent::doRun($input, $output);
  85. if (
  86. null !== $stdErr
  87. && $output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE
  88. ) {
  89. $triggeredDeprecations = Utils::getTriggeredDeprecations();
  90. if (\count($triggeredDeprecations) > 0) {
  91. $stdErr->writeln('');
  92. $stdErr->writeln($stdErr->isDecorated() ? '<bg=yellow;fg=black;>Detected deprecations in use:</>' : 'Detected deprecations in use:');
  93. foreach ($triggeredDeprecations as $deprecation) {
  94. $stdErr->writeln(sprintf('- %s', $deprecation));
  95. }
  96. }
  97. }
  98. return $result;
  99. }
  100. /**
  101. * @internal
  102. */
  103. public static function getAbout(bool $decorated = false): string
  104. {
  105. $longVersion = sprintf('%s <info>%s</info>', self::NAME, self::VERSION);
  106. $commit = '@git-commit@';
  107. $versionCommit = '';
  108. if ('@'.'git-commit@' !== $commit) { /** @phpstan-ignore-line as `$commit` is replaced during phar building */
  109. $versionCommit = substr($commit, 0, 7);
  110. }
  111. $about = implode('', [
  112. $longVersion,
  113. $versionCommit ? sprintf(' <info>(%s)</info>', $versionCommit) : '', // @phpstan-ignore-line to avoid `Ternary operator condition is always true|false.`
  114. self::VERSION_CODENAME ? sprintf(' <info>%s</info>', self::VERSION_CODENAME) : '', // @phpstan-ignore-line to avoid `Ternary operator condition is always true|false.`
  115. ' by <comment>Fabien Potencier</comment>, <comment>Dariusz Ruminski</comment> and <comment>contributors</comment>.',
  116. ]);
  117. if (false === $decorated) {
  118. return strip_tags($about);
  119. }
  120. return $about;
  121. }
  122. /**
  123. * @internal
  124. */
  125. public static function getAboutWithRuntime(bool $decorated = false): string
  126. {
  127. $about = self::getAbout(true)."\nPHP runtime: <info>".PHP_VERSION.'</info>';
  128. if (false === $decorated) {
  129. return strip_tags($about);
  130. }
  131. return $about;
  132. }
  133. public function getLongVersion(): string
  134. {
  135. return self::getAboutWithRuntime(true);
  136. }
  137. protected function getDefaultCommands(): array
  138. {
  139. return [new HelpCommand(), new ListCommand()];
  140. }
  141. /**
  142. * @throws \Throwable
  143. */
  144. protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int
  145. {
  146. $this->executedCommand = $command;
  147. return parent::doRunCommand($command, $input, $output);
  148. }
  149. protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void
  150. {
  151. // Since parallel analysis utilises child processes, and they have their own output,
  152. // we need to capture the output of the child process to determine it there was an exception.
  153. // Default render format is not machine-friendly, so we need to override it for `worker` command,
  154. // in order to be able to easily parse exception data for further displaying on main process' side.
  155. if ($this->executedCommand instanceof WorkerCommand) {
  156. $output->writeln(WorkerCommand::ERROR_PREFIX.json_encode(
  157. [
  158. 'class' => \get_class($e),
  159. 'message' => $e->getMessage(),
  160. 'file' => $e->getFile(),
  161. 'line' => $e->getLine(),
  162. 'code' => $e->getCode(),
  163. 'trace' => $e->getTraceAsString(),
  164. ]
  165. ));
  166. return;
  167. }
  168. parent::doRenderThrowable($e, $output);
  169. if ($output->isVeryVerbose() && $e instanceof WorkerException) {
  170. $output->writeln('<comment>Original trace from worker:</comment>');
  171. $output->writeln('');
  172. $output->writeln($e->getOriginalTraceAsString());
  173. $output->writeln('');
  174. }
  175. }
  176. }