From 41d887e57ae1b501d6bbe60c74754e7f5dcfdc97 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Fri, 9 Jan 2026 15:45:48 +0100 Subject: [PATCH 1/3] make use of entropy command --- phpstan.neon | 5 + src/Command/AliceYamlFixturesToPhpCommand.php | 40 ++++---- src/Command/CheckCommentedCodeCommand.php | 65 +++++-------- src/Command/CheckConflictsCommand.php | 11 +-- src/Command/DumpEditorconfigCommand.php | 24 +++-- src/Command/FinalizeClassesCommand.php | 92 +++++++------------ src/Command/FindMultiClassesCommand.php | 11 +-- .../GenerateSymfonyConfigBuildersCommand.php | 11 +-- src/Command/NamespaceToPSR4Command.php | 9 +- src/Command/PrettyJsonCommand.php | 53 +++++------ src/Command/PrivatizeConstantsCommand.php | 61 ++++++------ src/Command/SearchRegexCommand.php | 50 +++++----- .../SplitSymfonyConfigToPerPackageCommand.php | 45 +++++---- src/Command/SpotLazyTraitsCommand.php | 56 +++++------ .../Command/DetectUnitTestsCommand.php | 42 ++++----- 15 files changed, 252 insertions(+), 323 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index e3e6eb43f7..4b134b53ca 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -37,3 +37,8 @@ parameters: - identifier: argument.unresolvableType path: src/Command/GenerateSymfonyConfigBuildersCommand.php + + # magic contract + - + identifier: public.method.unused + message: '#Public method "Rector\\SwissKnife\\(.*?)Command\:\:run\(\)" is never used#' diff --git a/src/Command/AliceYamlFixturesToPhpCommand.php b/src/Command/AliceYamlFixturesToPhpCommand.php index 4349a8a1be..7767517423 100644 --- a/src/Command/AliceYamlFixturesToPhpCommand.php +++ b/src/Command/AliceYamlFixturesToPhpCommand.php @@ -4,45 +4,31 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use PhpParser\BuilderHelpers; use PhpParser\Node\Stmt\Return_; use PhpParser\PrettyPrinter\Standard; use Rector\SwissKnife\Finder\FilesFinder; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Yaml\Yaml; /** * @see https://github.com/nelmio/alice/blob/v2.3.0/doc/complete-reference.md#php */ -final class AliceYamlFixturesToPhpCommand extends Command +final class AliceYamlFixturesToPhpCommand implements \Entropy\Console\Contract\CommandInterface { public function __construct( private readonly SymfonyStyle $symfonyStyle, ) { - parent::__construct(); } - protected function configure(): void - { - $this->setName('alice-yaml-fixtures-to-php'); - - $this->addArgument( - 'sources', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'One or more paths to check' - ); - - $this->setDescription('Converts Alice YAML fixtures to PHP format, so Rector and PHPStan can understand it'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int + /** + * @param string[] $sources One or more paths to check + * @return ExitCode::* + */ + public function run(array $sources): int { - $sources = (array) $input->getArgument('sources'); $yamlFileInfos = FilesFinder::findYamlFiles($sources); $standard = new Standard(); @@ -75,7 +61,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int sprintf('Successfully converted %d Alice YAML fixtures to PHP', count($yamlFileInfos)) ); - return self::SUCCESS; + return \Entropy\Console\Enum\ExitCode::SUCCESS; + } + + public function getName(): string + { + return 'alice-yaml-fixtures-to-php'; + } + + public function getDescription(): string + { + return 'Converts Alice YAML fixtures to PHP format, so Rector and PHPStan can understand it'; } /** diff --git a/src/Command/CheckCommentedCodeCommand.php b/src/Command/CheckCommentedCodeCommand.php index 6cdfc8b6e5..d3f2f30d22 100644 --- a/src/Command/CheckCommentedCodeCommand.php +++ b/src/Command/CheckCommentedCodeCommand.php @@ -4,16 +4,13 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; +use Entropy\Console\Enum\ExitCode; use Rector\SwissKnife\Comments\CommentedCodeAnalyzer; use Rector\SwissKnife\Finder\PhpFilesFinder; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -final class CheckCommentedCodeCommand extends Command +final class CheckCommentedCodeCommand implements CommandInterface { private const int DEFAULT_LINE_LIMIT = 5; @@ -21,47 +18,22 @@ public function __construct( private readonly CommentedCodeAnalyzer $commentedCodeAnalyzer, private readonly SymfonyStyle $symfonyStyle, ) { - parent::__construct(); } - protected function configure(): void + /** + * @param string[] $sources One or more paths to check + * @param string[] $skipFiles File paths to skip + * @param int $lineLimit Maximum number of comment lines in a row allowed + * + * @return ExitCode::* + */ + public function run(array $sources, array $skipFiles = [], int $lineLimit = self::DEFAULT_LINE_LIMIT): int { - $this->setName('check-commented-code'); - - $this->addArgument( - 'sources', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'One or more paths to check' - ); - $this->addOption( - 'skip-file', - null, - InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Skip file path' - ); - $this->setDescription('Checks code for commented snippets'); - - $this->addOption( - 'line-limit', - null, - InputOption::VALUE_REQUIRED | InputOption::VALUE_OPTIONAL, - 'Amount of allowed comment lines in a row', - self::DEFAULT_LINE_LIMIT - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $sources = (array) $input->getArgument('sources'); - $skipFiles = (array) $input->getOption('skip-file'); - $phpFileInfos = PhpFilesFinder::find($sources, $skipFiles); $message = sprintf('Analysing %d *.php files', count($phpFileInfos)); $this->symfonyStyle->note($message); - $lineLimit = (int) $input->getOption('line-limit'); - $commentedLinesByFilePaths = []; foreach ($phpFileInfos as $phpFileInfo) { $commentedLines = $this->commentedCodeAnalyzer->process($phpFileInfo->getRealPath(), $lineLimit); @@ -75,7 +47,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($commentedLinesByFilePaths === []) { $this->symfonyStyle->success('No commented code found'); - return self::SUCCESS; + return ExitCode::SUCCESS; } foreach ($commentedLinesByFilePaths as $filePath => $commentedLines) { @@ -86,6 +58,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $this->symfonyStyle->error('Errors found'); - return self::FAILURE; + + return ExitCode::ERROR; + } + + public function getName(): string + { + return 'check-commented-code'; + } + + public function getDescription(): string + { + return 'Checks code for commented snippets'; } } diff --git a/src/Command/CheckConflictsCommand.php b/src/Command/CheckConflictsCommand.php index 866a90da31..988caa4feb 100644 --- a/src/Command/CheckConflictsCommand.php +++ b/src/Command/CheckConflictsCommand.php @@ -6,13 +6,12 @@ use Rector\SwissKnife\Finder\FilesFinder; use Rector\SwissKnife\Git\ConflictResolver; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -final class CheckConflictsCommand extends Command +final class CheckConflictsCommand implements \Entropy\Console\Contract\CommandInterface { public function __construct( private readonly ConflictResolver $conflictResolver, @@ -21,7 +20,7 @@ public function __construct( parent::__construct(); } - protected function configure(): void + private function configure(): void { $this->setName('check-conflicts'); @@ -29,7 +28,7 @@ protected function configure(): void $this->addArgument('sources', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Path to project'); } - protected function execute(InputInterface $input, OutputInterface $output): int + private function execute(InputInterface $input, OutputInterface $output): int { /** @var string[] $sources */ $sources = (array) $input->getArgument('sources'); @@ -45,7 +44,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $message = sprintf('No conflicts found in %d files', count($fileInfos)); $this->symfonyStyle->success($message); - return self::SUCCESS; + return \Entropy\Console\Enum\ExitCode::SUCCESS; } foreach ($conflictsCountByFilePath as $file => $conflictCount) { @@ -53,6 +52,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->error($message); } - return self::FAILURE; + return \Entropy\Console\Enum\ExitCode::ERROR; } } diff --git a/src/Command/DumpEditorconfigCommand.php b/src/Command/DumpEditorconfigCommand.php index 7ff3938e13..557930e7c5 100644 --- a/src/Command/DumpEditorconfigCommand.php +++ b/src/Command/DumpEditorconfigCommand.php @@ -4,37 +4,43 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -final class DumpEditorconfigCommand extends Command +final class DumpEditorconfigCommand implements CommandInterface { public function __construct( private readonly SymfonyStyle $symfonyStyle, ) { - parent::__construct(); } - protected function configure(): void + public function getName(): string { - $this->setName('dump-editorconfig'); - $this->setDescription('Dump .editorconfig file to project root'); + return 'dump-editorconfig'; } - protected function execute(InputInterface $input, OutputInterface $output): int + public function getDescription(): string + { + return 'Dump .editorconfig file to project root'; + } + + private function execute(InputInterface $input, OutputInterface $output): int { $projectEditorconfigFilePath = getcwd() . '/.editorconfig'; if (file_exists($projectEditorconfigFilePath)) { $this->symfonyStyle->error('.editorconfig file already exists'); - return self::FAILURE; + + return ExitCode::ERROR; } FileSystem::copy(__DIR__ . '/../../templates/.editorconfig', $projectEditorconfigFilePath); + $this->symfonyStyle->success('.editorconfig file was created'); - return self::SUCCESS; + return ExitCode::SUCCESS; } } diff --git a/src/Command/FinalizeClassesCommand.php b/src/Command/FinalizeClassesCommand.php index 858f17ec43..57075bc1ea 100644 --- a/src/Command/FinalizeClassesCommand.php +++ b/src/Command/FinalizeClassesCommand.php @@ -4,6 +4,8 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use Nette\Utils\Strings; use Rector\SwissKnife\Analyzer\NeedsFinalizeAnalyzer; @@ -13,14 +15,9 @@ use Rector\SwissKnife\MockedClassResolver; use Rector\SwissKnife\ParentClassResolver; use Rector\SwissKnife\PhpParser\CachedPhpParser; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -final class FinalizeClassesCommand extends Command +final class FinalizeClassesCommand implements CommandInterface { /** * @see https://regex101.com/r/Q5Nfbo/1 @@ -34,60 +31,29 @@ public function __construct( private readonly CachedPhpParser $cachedPhpParser, private readonly MockedClassResolver $mockedClassResolver, ) { - parent::__construct(); - } - - protected function configure(): void - { - $this->setName('finalize-classes'); - $this->setAliases(['finalise', 'finalise-classes']); - - $this->setDescription('Finalize classes without children'); - - $this->addArgument('paths', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Directories to finalize'); - - $this->addOption( - 'skip-mocked', - null, - InputOption::VALUE_NONE, - 'Skip mocked classes as well (use only if unable to run bypass-finals package)' - ); - - $this->addOption( - 'skip-file', - null, - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Skip file or files by path' - ); - - $this->addOption( - 'dry-run', - null, - InputOption::VALUE_NONE, - 'Do no change anything, only list classes about to be finalized. If there are classes to finalize, it will exit with code 1. Useful for CI.' - ); - - $this->addOption('no-progress', null, InputOption::VALUE_NONE, 'Do not show progress bar, only results'); } /** - * @return self::FAILURE|self::SUCCESS + * @param string[] $paths Directories to finalize + * @param bool $dryRun Do no change anything, only list classes about to be finalized. If there are classes to finalize, it will exit with code 1. Useful for CI. + * @param bool $skipMocked Skip mocked classes as well (use only if unable to run bypass-finals package) + * @param string[] $skipFiles Skip file or files by path + * @param bool $noProgress Do not show progress bar, only results */ - protected function execute(InputInterface $input, OutputInterface $output): int - { - $paths = (array) $input->getArgument('paths'); - $isDryRun = (bool) $input->getOption('dry-run'); - $areMockedSkipped = (bool) $input->getOption('skip-mocked'); - + public function run( + array $paths, + bool $dryRun = false, + bool $skipMocked = false, + array $skipFiles = [], + bool $noProgress = false + ): int { $this->symfonyStyle->title('1. Detecting parent and entity classes'); - $skippedFiles = $input->getOption('skip-file'); - $phpFileInfos = PhpFilesFinder::find($paths, $skippedFiles); + $phpFileInfos = PhpFilesFinder::find($paths, $skipFiles); - $noProgress = (bool) $input->getOption('no-progress'); if (! $noProgress) { // double to count for both parent and entity resolver - $stepRatio = $areMockedSkipped ? 3 : 2; + $stepRatio = $skipMocked ? 3 : 2; $this->symfonyStyle->progressStart($stepRatio * count($phpFileInfos)); } @@ -103,7 +69,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $parentClassNames = $this->parentClassResolver->resolve($phpFileInfos, $progressClosure); $entityClassNames = $this->entityClassResolver->resolve($paths, $progressClosure); - $mockedClassNames = $areMockedSkipped ? $this->mockedClassResolver->resolve($paths, $progressClosure) : []; + $mockedClassNames = $skipMocked ? $this->mockedClassResolver->resolve($paths, $progressClosure) : []; if (! $noProgress) { $this->symfonyStyle->progressFinish(); @@ -115,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int count($entityClassNames) )); - if ($areMockedSkipped) { + if ($skipMocked) { $this->symfonyStyle->writeln(sprintf('Also %d mocked classes', count($mockedClassNames))); } @@ -142,14 +108,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $finalizedFilePaths[] = PathHelper::relativeToCwd($phpFileInfo->getRealPath()); - if ($isDryRun === false) { + if ($dryRun === false) { FileSystem::write($phpFileInfo->getRealPath(), $finalizedContents, null); } } if ($finalizedFilePaths === []) { $this->symfonyStyle->success('Nothing to finalize'); - return self::SUCCESS; + return ExitCode::SUCCESS; } $this->symfonyStyle->listing($finalizedFilePaths); @@ -158,18 +124,28 @@ protected function execute(InputInterface $input, OutputInterface $output): int $pluralClassText = $countFinalizedClasses === 1 ? 'class' : 'classes'; // to make it fail in CI - if ($isDryRun) { + if ($dryRun) { $this->symfonyStyle->error(sprintf( '%d %s can be finalized', $countFinalizedClasses, $pluralClassText, )); - return self::FAILURE; + return ExitCode::ERROR; } $this->symfonyStyle->success(sprintf('%d %s finalized', $countFinalizedClasses, $pluralClassText)); - return self::SUCCESS; + return ExitCode::SUCCESS; + } + + public function getName(): string + { + return 'finalize-classes'; + } + + public function getDescription(): string + { + return 'Finalize classes without children'; } } diff --git a/src/Command/FindMultiClassesCommand.php b/src/Command/FindMultiClassesCommand.php index bab77c1e86..c8520c5ed8 100644 --- a/src/Command/FindMultiClassesCommand.php +++ b/src/Command/FindMultiClassesCommand.php @@ -7,14 +7,13 @@ use Rector\SwissKnife\FileSystem\PathHelper; use Rector\SwissKnife\Finder\MultipleClassInOneFileFinder; use Rector\SwissKnife\Finder\PhpFilesFinder; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -final class FindMultiClassesCommand extends Command +final class FindMultiClassesCommand implements \Entropy\Console\Contract\CommandInterface { public function __construct( private readonly MultipleClassInOneFileFinder $multipleClassInOneFileFinder, @@ -23,7 +22,7 @@ public function __construct( parent::__construct(); } - protected function configure(): void + private function configure(): void { $this->setName('find-multi-classes'); @@ -43,7 +42,7 @@ protected function configure(): void ); } - protected function execute(InputInterface $input, OutputInterface $output): int + private function execute(InputInterface $input, OutputInterface $output): int { /** @var string[] $source */ $source = $input->getArgument('sources'); @@ -56,7 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($multipleClassesByFile === []) { $this->symfonyStyle->success(sprintf('No file with 2+ classes found in %d files', count($phpFileInfos))); - return self::SUCCESS; + return \Entropy\Console\Enum\ExitCode::SUCCESS; } foreach ($multipleClassesByFile as $filePath => $classes) { @@ -68,6 +67,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->listing($classes); } - return self::FAILURE; + return \Entropy\Console\Enum\ExitCode::ERROR; } } diff --git a/src/Command/GenerateSymfonyConfigBuildersCommand.php b/src/Command/GenerateSymfonyConfigBuildersCommand.php index 4e6ddd8c0f..47ad8e2102 100644 --- a/src/Command/GenerateSymfonyConfigBuildersCommand.php +++ b/src/Command/GenerateSymfonyConfigBuildersCommand.php @@ -8,7 +8,6 @@ use ReflectionClass; use Symfony\Component\Config\Builder\ConfigBuilderGenerator; use Symfony\Component\Config\Definition\ConfigurationInterface; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -18,7 +17,7 @@ /** * @see https://github.com/nelmio/alice/blob/v2.3.0/doc/complete-reference.md#php */ -final class GenerateSymfonyConfigBuildersCommand extends Command +final class GenerateSymfonyConfigBuildersCommand implements \Entropy\Console\Contract\CommandInterface { /** * @var string[] @@ -40,7 +39,7 @@ public function __construct( parent::__construct(); } - protected function configure(): void + private function configure(): void { $this->setName('generate-symfony-config-builders'); @@ -49,7 +48,7 @@ protected function configure(): void ); } - protected function execute(InputInterface $input, OutputInterface $output): int + private function execute(InputInterface $input, OutputInterface $output): int { // make sure the classes exist if (! class_exists(ConfigBuilderGenerator::class) || ! class_exists(ContainerBuilder::class)) { @@ -57,7 +56,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'This command requires symfony/config and symfony/dependency-injection 5.3+ to run. Update your dependencies or install them first.' ); - return self::FAILURE; + return \Entropy\Console\Enum\ExitCode::ERROR; } $configBuilderGenerator = new ConfigBuilderGenerator(getcwd() . '/var/cache'); @@ -82,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->success('Done'); - return self::SUCCESS; + return \Entropy\Console\Enum\ExitCode::SUCCESS; } /** diff --git a/src/Command/NamespaceToPSR4Command.php b/src/Command/NamespaceToPSR4Command.php index fc050774a1..3eeeafc646 100644 --- a/src/Command/NamespaceToPSR4Command.php +++ b/src/Command/NamespaceToPSR4Command.php @@ -6,7 +6,6 @@ use Nette\Utils\FileSystem; use Nette\Utils\Strings; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -15,7 +14,7 @@ use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; -final class NamespaceToPSR4Command extends Command +final class NamespaceToPSR4Command implements \Entropy\Console\Contract\CommandInterface { public function __construct( private readonly SymfonyStyle $symfonyStyle, @@ -23,7 +22,7 @@ public function __construct( parent::__construct(); } - protected function configure(): void + private function configure(): void { $this->setName('namespace-to-psr-4'); @@ -46,7 +45,7 @@ protected function configure(): void /** * @return self::* */ - protected function execute(InputInterface $input, OutputInterface $output): int + private function execute(InputInterface $input, OutputInterface $output): int { $path = (string) $input->getArgument('path'); $namespaceRoot = rtrim((string) $input->getOption('namespace-root'), '\\'); @@ -93,7 +92,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->success(sprintf('Fixed %d files', $changedFilesCount)); } - return self::SUCCESS; + return \Entropy\Console\Enum\ExitCode::SUCCESS; } /** diff --git a/src/Command/PrettyJsonCommand.php b/src/Command/PrettyJsonCommand.php index 59b335ad0f..f80aad9d21 100644 --- a/src/Command/PrettyJsonCommand.php +++ b/src/Command/PrettyJsonCommand.php @@ -4,56 +4,39 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use Nette\Utils\Json; use Rector\SwissKnife\FileSystem\JsonAnalyzer; use Rector\SwissKnife\Finder\FilesFinder; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -final class PrettyJsonCommand extends Command +final class PrettyJsonCommand implements \Entropy\Console\Contract\CommandInterface { public function __construct( private readonly SymfonyStyle $symfonyStyle, private readonly JsonAnalyzer $jsonAnalyzer, ) { - parent::__construct(); } - protected function configure(): void + /** + * @param string[] $sources JSON file or directory with JSON files to prettify + * @param bool $dryRun Dry run - no changes will be made + * + * @return ExitCode::* + */ + public function run(array $sources, bool $dryRun = false): int { - $this->setName('pretty-json'); - - $this->setDescription('Turns JSON files from 1-line to pretty print format'); - - $this->addArgument( - 'sources', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'JSON file or directory with JSON files to prettify' - ); - - $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Dry run - no changes will be made'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $sources = (array) $input->getArgument('sources'); $jsonFileInfos = FilesFinder::findJsonFiles($sources); if ($jsonFileInfos === []) { $this->symfonyStyle->error('No *.json files found'); - return self::FAILURE; + return \Entropy\Console\Enum\ExitCode::ERROR; } $message = sprintf('Analysing %d *.json files', count($jsonFileInfos)); $this->symfonyStyle->note($message); - $isDryRun = (bool) $input->getOption('dry-run'); - $printedFilePaths = []; // convert file infos from uggly json to pretty json @@ -70,7 +53,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $printedFilePaths[] = $jsonFileInfo->getRelativePathname(); // nothing will be changed - if ($isDryRun) { + if ($dryRun) { continue; } @@ -82,12 +65,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int '%d file%s %s', count($printedFilePaths), count($printedFilePaths) === 1 ? '' : 's', - $isDryRun ? 'would be changed' : 'changed' + $dryRun ? 'would be changed' : 'changed' ); $this->symfonyStyle->success($successMessage); $this->symfonyStyle->listing($printedFilePaths); - return self::SUCCESS; + return \Entropy\Console\Enum\ExitCode::SUCCESS; + } + + public function getName(): string + { + return 'pretty-json'; + } + + public function getDescription(): string + { + return 'Turns JSON files from 1-line to pretty print format'; } } diff --git a/src/Command/PrivatizeConstantsCommand.php b/src/Command/PrivatizeConstantsCommand.php index 5449b4b536..67408eb3fc 100644 --- a/src/Command/PrivatizeConstantsCommand.php +++ b/src/Command/PrivatizeConstantsCommand.php @@ -4,6 +4,7 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use Nette\Utils\Strings; use Rector\SwissKnife\Contract\ClassConstantFetchInterface; @@ -15,15 +16,10 @@ use Rector\SwissKnife\ValueObject\ClassConstantFetch\CurrentClassConstantFetch; use Rector\SwissKnife\ValueObject\VisibilityChangeStats; use Rector\SwissKnife\YAML\YamlConfigConstantExtractor; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\SplFileInfo; -final class PrivatizeConstantsCommand extends Command +final class PrivatizeConstantsCommand implements \Entropy\Console\Contract\CommandInterface { public function __construct( private readonly SymfonyStyle $symfonyStyle, @@ -32,45 +28,31 @@ public function __construct( private readonly TwigTemplateConstantExtractor $twigTemplateConstantExtractor, private readonly YamlConfigConstantExtractor $yamlConfigConstantExtractor ) { - parent::__construct(); } - protected function configure(): void + public function getName(): string { - $this->setName('privatize-constants'); - - $this->addArgument( - 'sources', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'One or more paths to check, include tests directory as well' - ); - - $this->addOption( - 'exclude-path', - null, - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Path to exclude' - ); - - $this->addOption('debug', null, InputOption::VALUE_NONE, 'Debug output'); + return 'privatize-constants'; + } - $this->setDescription('Make class constants private if not used outside in PHP, Twig and YAML files'); + public function getDescription(): string + { + return 'Make class constants private if not used outside in PHP, Twig and YAML files'; } /** - * @return Command::* + * @param string[] $sources One or more paths to check, include tests directory as well + * @param string[] $excludedPaths Paths to exclude + * @param bool $isDebug Debug output + * @return ExitCode::* */ - protected function execute(InputInterface $input, OutputInterface $output): int + public function run(array $sources, array $excludedPaths = [], bool $isDebug = false): int { - $sources = (array) $input->getArgument('sources'); - $excludedPaths = (array) $input->getOption('exclude-path'); - $isDebug = (bool) $input->getOption('debug'); - $phpFileInfos = PhpFilesFinder::find($sources, $excludedPaths); if ($phpFileInfos === []) { $this->symfonyStyle->warning('No PHP files found in provided paths'); - return self::SUCCESS; + return ExitCode::SUCCESS; } $this->symfonyStyle->title('Finding class const fetches...'); @@ -107,7 +89,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (! $visibilityChangeStats->hasAnyChange()) { $this->symfonyStyle->warning('No constants were privatized'); - return self::SUCCESS; + + return ExitCode::SUCCESS; } $this->symfonyStyle->newLine(2); @@ -116,7 +99,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int sprintf('Totally %d constants were made private', $visibilityChangeStats->getPrivateCount()) ); - return self::SUCCESS; + return ExitCode::SUCCESS; } /** @@ -175,4 +158,14 @@ private function isClassConstantUsedPublicly(array $classConstantFetches, ClassC return false; } + + public function getName(): string + { + return 'privatize-constants'; + } + + public function getDescription(): string + { + return 'Make class constants private if not used outside in PHP, Twig and YAML files'; + } } diff --git a/src/Command/SearchRegexCommand.php b/src/Command/SearchRegexCommand.php index f2a3ee18d1..0b1cb3d947 100644 --- a/src/Command/SearchRegexCommand.php +++ b/src/Command/SearchRegexCommand.php @@ -4,44 +4,30 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\Strings; use Rector\SwissKnife\Finder\PhpFilesFinder; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Webmozart\Assert\Assert; -final class SearchRegexCommand extends Command +final class SearchRegexCommand implements \Entropy\Console\Contract\CommandInterface { public function __construct( private readonly SymfonyStyle $symfonyStyle, ) { - parent::__construct(); } - protected function configure(): void + /** + * @param string $regex Code snippet to look in PHP files in the whole codebase + * @param string $projectDirectory Project directory + * + * @return ExitCode::* + */ + public function run(string $regex, ?string $projectDirectory = null): int { - $this->setName('search-regex'); - - $this->addArgument( - 'regex', - InputArgument::REQUIRED, - 'Code snippet to look in PHP files in the whole codebase' - ); - - $this->addOption('project-directory', null, InputOption::VALUE_REQUIRED, 'Project directory', getcwd()); - - $this->setDescription('Search for regex in PHP files of the whole codebase'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $regex = (string) $input->getArgument('regex'); - - $projectDirectory = (string) $input->getOption('project-directory'); + if ($projectDirectory === null) { + $projectDirectory = getcwd(); + } Assert::directory($projectDirectory); @@ -82,6 +68,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->newLine(2); $this->symfonyStyle->success(sprintf('Found %d cases in %d files', $foundCasesCount, count($markedFiles))); - return self::SUCCESS; + return \Entropy\Console\Enum\ExitCode::SUCCESS; + } + + public function getName(): string + { + return 'search-regex'; + } + + public function getDescription(): string + { + return 'Search for regex in PHP files of the whole codebase'; } } diff --git a/src/Command/SplitSymfonyConfigToPerPackageCommand.php b/src/Command/SplitSymfonyConfigToPerPackageCommand.php index aadb2a1052..f1f4725538 100644 --- a/src/Command/SplitSymfonyConfigToPerPackageCommand.php +++ b/src/Command/SplitSymfonyConfigToPerPackageCommand.php @@ -4,6 +4,8 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Scalar\String_; @@ -16,15 +18,10 @@ use Rector\SwissKnife\PhpParser\NodeFactory\SplitConfigClosureFactory; use Rector\SwissKnife\PhpParser\NodeVisitor\AddImportConfigMethodCallNodeVisitor; use Rector\SwissKnife\PhpParser\NodeVisitor\ExtractSymfonyExtensionCallNodeVisitor; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Webmozart\Assert\Assert; -final class SplitSymfonyConfigToPerPackageCommand extends Command +final class SplitSymfonyConfigToPerPackageCommand implements CommandInterface { private readonly Standard $printerStandard; @@ -33,26 +30,16 @@ public function __construct( private readonly SplitConfigClosureFactory $splitConfigClosureFactory, ) { $this->printerStandard = new Standard(); - - parent::__construct(); } - protected function configure(): void - { - $this->setName('split-config-per-package'); - $this->setDescription( - 'Split Symfony configs that contains many extension() calls to /packages directory with config per package' - ); - - $this->addArgument('config-path', InputArgument::REQUIRED, 'Path to the config file'); - $this->addOption('output-dir', null, InputOption::VALUE_REQUIRED, 'Directory to save the split config files'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int + /** + * @param string $configPath Path to the config file + * @param string $outputDir Directory to save the split config files + * + * @return ExitCode::* + */ + public function run(string $configPath, string $outputDir): int { - $configPath = $input->getArgument('config-path'); - $outputDir = $input->getOption('output-dir'); - Assert::fileExists($configPath); Assert::notEmpty($outputDir); @@ -63,7 +50,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($symfonyExtensionMethodCalls === []) { $this->symfonyStyle->warning('No extension() method calls found'); - return self::SUCCESS; + return \Entropy\Console\Enum\ExitCode::SUCCESS; } foreach ($symfonyExtensionMethodCalls as $symfonyExtensionMethodCall) { @@ -93,6 +80,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 0; } + public function getName(): string + { + return 'split-config-per-package'; + } + + public function getDescription(): string + { + return 'Split Symfony configs that contains many extension() calls to /packages directory with config per package'; + } + /** * @return Stmt[] */ diff --git a/src/Command/SpotLazyTraitsCommand.php b/src/Command/SpotLazyTraitsCommand.php index 31c3e950cf..dcb4e59ee7 100644 --- a/src/Command/SpotLazyTraitsCommand.php +++ b/src/Command/SpotLazyTraitsCommand.php @@ -4,51 +4,33 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; +use Entropy\Console\Enum\ExitCode; use Rector\SwissKnife\Traits\TraitSpotter; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -final class SpotLazyTraitsCommand extends Command +final class SpotLazyTraitsCommand implements CommandInterface { public function __construct( private readonly SymfonyStyle $symfonyStyle, private readonly TraitSpotter $traitSpotter, ) { - parent::__construct(); } - protected function configure(): void + /** + * @param string[] $sources Paths to scan for traits + * @param int $maxUsed Maximum number of times a trait is used to be considered lazy + * @return ExitCode::* + */ + public function run(array $sources, int $maxUsed = 2): int { - $this->setName('spot-lazy-traits'); - - $this->addArgument( - 'sources', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'One or more paths to check' - ); - - $this->addOption('max-used', null, InputOption::VALUE_REQUIRED, 'Maximum count the trait is used', 2); - - $this->setDescription( - 'Spot traits that are use only once, to potentially inline them and make code more robust and readable' - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $sources = (array) $input->getArgument('sources'); - $maxUsedCount = (int) $input->getOption('max-used'); - $this->symfonyStyle->title('Looking for trait definitions'); $traitSpottingResult = $this->traitSpotter->analyse($sources); if ($traitSpottingResult->getTraitCount() === 0) { $this->symfonyStyle->success('No traits were found in your project, nothing to worry about'); - return self::SUCCESS; + + return ExitCode::SUCCESS; } $this->symfonyStyle->writeln( @@ -62,9 +44,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->newLine(); - $this->symfonyStyle->title(sprintf('Looking for traits used less than %d-times', $maxUsedCount)); + $this->symfonyStyle->title(sprintf('Looking for traits used less than %d-times', $maxUsed)); - $leastUsedTraitsMetadatas = $traitSpottingResult->getTraitMaximumUsedTimes($maxUsedCount); + $leastUsedTraitsMetadatas = $traitSpottingResult->getTraitMaximumUsedTimes($maxUsed); foreach ($leastUsedTraitsMetadatas as $leastUsedTraitMetadata) { $this->symfonyStyle->writeln(sprintf( @@ -84,6 +66,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int PHP_EOL )); - return self::SUCCESS; + return ExitCode::SUCCESS; + } + + public function getName(): string + { + return 'spot-lazy-traits'; + } + + public function getDescription(): string + { + return 'Spot traits that are use only once, to potentially inline them and make code more robust and readable'; } } diff --git a/src/Testing/Command/DetectUnitTestsCommand.php b/src/Testing/Command/DetectUnitTestsCommand.php index b29a3ba8f6..35b118b55b 100644 --- a/src/Testing/Command/DetectUnitTestsCommand.php +++ b/src/Testing/Command/DetectUnitTestsCommand.php @@ -4,17 +4,14 @@ namespace Rector\SwissKnife\Testing\Command; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use Rector\SwissKnife\Testing\Printer\PHPUnitXmlPrinter; use Rector\SwissKnife\Testing\UnitTestFilePathsFinder; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Webmozart\Assert\Assert; -final class DetectUnitTestsCommand extends Command +final class DetectUnitTestsCommand implements \Entropy\Console\Contract\CommandInterface { private const string OUTPUT_FILENAME = 'phpunit-unit-files.xml'; @@ -23,32 +20,21 @@ public function __construct( private readonly SymfonyStyle $symfonyStyle, private readonly UnitTestFilePathsFinder $unitTestFilePathsFinder, ) { - parent::__construct(); } - protected function configure(): void + /** + * @param string[] $sources Path to directory with tests + */ + public function run(array $sources): int { - $this->setName('detect-unit-tests'); - - $this->setDescription('Get list of tests in specific directory, that are considered "unit"'); - - $this->addArgument( - 'sources', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'Path to directory with tests' - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $sources = (array) $input->getArgument('sources'); Assert::allString($sources); $unitTestCasesClassesToFilePaths = $this->unitTestFilePathsFinder->findInDirectories($sources); if ($unitTestCasesClassesToFilePaths === []) { $this->symfonyStyle->note('No unit tests found in provided paths'); - return self::SUCCESS; + + return ExitCode::SUCCESS; } $filesPHPUnitXmlContents = $this->phpunitXmlPrinter->printFiles($unitTestCasesClassesToFilePaths); @@ -63,6 +49,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->success($successMessage); - return self::SUCCESS; + return ExitCode::SUCCESS; + } + + public function getName(): string + { + return 'detect-unit-tests'; + } + + public function getDescription(): string + { + return 'Get list of tests in specific directory, that are considered "unit"'; } } From f30f1447061a3bfa223cb30ce474929d4a2c3299 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Fri, 9 Jan 2026 16:15:12 +0100 Subject: [PATCH 2/3] remove deprectaed GenerateSymfonyConfigBuildersCommand --- README.md | 16 +-- bin/swiss-knife.php | 8 +- phpstan.neon | 5 - src/Command/CheckConflictsCommand.php | 33 +++--- src/Command/DumpEditorconfigCommand.php | 4 +- src/Command/FindMultiClassesCommand.php | 56 ++++------ .../GenerateSymfonyConfigBuildersCommand.php | 100 ------------------ src/Command/NamespaceToPSR4Command.php | 51 ++++----- src/Command/PrivatizeConstantsCommand.php | 10 -- src/DependencyInjection/ContainerFactory.php | 26 ----- src/Enum/SymfonyExtensionClass.php | 24 ----- 11 files changed, 65 insertions(+), 268 deletions(-) delete mode 100644 src/Command/GenerateSymfonyConfigBuildersCommand.php delete mode 100644 src/Enum/SymfonyExtensionClass.php diff --git a/README.md b/README.md index 07c0d5d9a0..80ca4d93f0 100644 --- a/README.md +++ b/README.md @@ -241,19 +241,7 @@ That's it!
-## 9. Generate Symfony 5.3 configs builders - -Symfony 5.3 introduced amazing [config builders](https://symfony.com/blog/new-in-symfony-5-3-config-builder-classes), but those classes are not available for IDE until used. To make it easier, we added a command that generates all config builder classes you project can use, in `/var/cache/Symfony`. - -```bash -vendor/bin/swiss-knife generate-symfony-config-builders -``` - -That way IDE, PHPStan after adding those paths and Rector can understand your config files better. - -
- -## 10. Spots Fake Traits +## 9. Spots Fake Traits What is trait has 5 lines and used in single service? We know it's better to be inlined, to empower IDE, Rector and PHPStan. But don't have time to worry about these details. @@ -275,7 +263,7 @@ That's it! Run this command once upon a time or run it in CI to eliminate traits
-## 11. Split huge Symfony config to per-package in directory +## 10. Split huge Symfony config to per-package in directory Do you have a huge Symfony config file that is hard to navigate? Do you want to split it to per-package files? diff --git a/bin/swiss-knife.php b/bin/swiss-knife.php index bc8cc9b1ab..f12534588e 100755 --- a/bin/swiss-knife.php +++ b/bin/swiss-knife.php @@ -2,9 +2,7 @@ declare(strict_types=1); -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Output\ConsoleOutput; +use Entropy\Console\ConsoleApplication; use Rector\SwissKnife\DependencyInjection\ContainerFactory; $scoperAutoloadFilepath = __DIR__ . '/../vendor/scoper-autoload.php'; @@ -32,7 +30,7 @@ $containerFactory = new ContainerFactory(); $container = $containerFactory->create(); -$application = $container->make(Application::class); +$consoleApplication = $container->make(ConsoleApplication::class); -$exitCode = $application->run(new ArgvInput(), new ConsoleOutput()); +$exitCode = $consoleApplication->run($argv); exit($exitCode); diff --git a/phpstan.neon b/phpstan.neon index 4b134b53ca..239e7a34d5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -33,11 +33,6 @@ parameters: identifier: argument.type path: src/Testing/MockWire.php - # optional command, depends on present of classes - - - identifier: argument.unresolvableType - path: src/Command/GenerateSymfonyConfigBuildersCommand.php - # magic contract - identifier: public.method.unused diff --git a/src/Command/CheckConflictsCommand.php b/src/Command/CheckConflictsCommand.php index 988caa4feb..2b74da0a5b 100644 --- a/src/Command/CheckConflictsCommand.php +++ b/src/Command/CheckConflictsCommand.php @@ -4,11 +4,9 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Enum\ExitCode; use Rector\SwissKnife\Finder\FilesFinder; use Rector\SwissKnife\Git\ConflictResolver; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; final class CheckConflictsCommand implements \Entropy\Console\Contract\CommandInterface @@ -17,23 +15,16 @@ public function __construct( private readonly ConflictResolver $conflictResolver, private readonly SymfonyStyle $symfonyStyle, ) { - parent::__construct(); } - private function configure(): void + /** + * @param string[] $sources One or more path to project + * @return ExitCode::* + */ + public function run(array $sources): int { - $this->setName('check-conflicts'); - - $this->setDescription('Check files for missed git conflicts'); - $this->addArgument('sources', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Path to project'); - } - - private function execute(InputInterface $input, OutputInterface $output): int - { - /** @var string[] $sources */ - $sources = (array) $input->getArgument('sources'); - $fileInfos = FilesFinder::find($sources); + $filePaths = []; foreach ($fileInfos as $fileInfo) { $filePaths[] = $fileInfo->getRealPath(); @@ -54,4 +45,14 @@ private function execute(InputInterface $input, OutputInterface $output): int return \Entropy\Console\Enum\ExitCode::ERROR; } + + public function getName(): string + { + return 'check-conflicts'; + } + + public function getDescription(): string + { + return 'Check files for missed git conflicts'; + } } diff --git a/src/Command/DumpEditorconfigCommand.php b/src/Command/DumpEditorconfigCommand.php index 557930e7c5..d8441a0175 100644 --- a/src/Command/DumpEditorconfigCommand.php +++ b/src/Command/DumpEditorconfigCommand.php @@ -7,8 +7,6 @@ use Entropy\Console\Contract\CommandInterface; use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; final class DumpEditorconfigCommand implements CommandInterface @@ -28,7 +26,7 @@ public function getDescription(): string return 'Dump .editorconfig file to project root'; } - private function execute(InputInterface $input, OutputInterface $output): int + public function run(): int { $projectEditorconfigFilePath = getcwd() . '/.editorconfig'; if (file_exists($projectEditorconfigFilePath)) { diff --git a/src/Command/FindMultiClassesCommand.php b/src/Command/FindMultiClassesCommand.php index c8520c5ed8..85c3703dc7 100644 --- a/src/Command/FindMultiClassesCommand.php +++ b/src/Command/FindMultiClassesCommand.php @@ -4,54 +4,32 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; +use Entropy\Console\Enum\ExitCode; use Rector\SwissKnife\FileSystem\PathHelper; use Rector\SwissKnife\Finder\MultipleClassInOneFileFinder; use Rector\SwissKnife\Finder\PhpFilesFinder; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -final class FindMultiClassesCommand implements \Entropy\Console\Contract\CommandInterface +final class FindMultiClassesCommand implements CommandInterface { public function __construct( private readonly MultipleClassInOneFileFinder $multipleClassInOneFileFinder, private readonly SymfonyStyle $symfonyStyle, ) { - parent::__construct(); } - private function configure(): void + /** + * @param string[] $sources Path to source to analyse + * @param string[] $excludePaths Paths to exclude + * + * @return ExitCode::* + */ + public function run(array $sources, array $excludePaths): int { - $this->setName('find-multi-classes'); + $phpFileInfos = PhpFilesFinder::find($sources, $excludePaths); - $this->setDescription('Find multiple classes in one file'); - - $this->addArgument( - 'sources', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'Path to source to analyse' - ); - - $this->addOption( - 'exclude-path', - null, - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Path to exclude' - ); - } - - private function execute(InputInterface $input, OutputInterface $output): int - { - /** @var string[] $source */ - $source = $input->getArgument('sources'); - - $excludedPaths = (array) $input->getOption('exclude-path'); - - $phpFileInfos = PhpFilesFinder::find($source, $excludedPaths); - - $multipleClassesByFile = $this->multipleClassInOneFileFinder->findInDirectories($source, $excludedPaths); + $multipleClassesByFile = $this->multipleClassInOneFileFinder->findInDirectories($sources, $excludePaths); if ($multipleClassesByFile === []) { $this->symfonyStyle->success(sprintf('No file with 2+ classes found in %d files', count($phpFileInfos))); @@ -69,4 +47,14 @@ private function execute(InputInterface $input, OutputInterface $output): int return \Entropy\Console\Enum\ExitCode::ERROR; } + + public function getName(): string + { + return 'find-multi-classes'; + } + + public function getDescription(): string + { + return 'Find multiple classes in one file'; + } } diff --git a/src/Command/GenerateSymfonyConfigBuildersCommand.php b/src/Command/GenerateSymfonyConfigBuildersCommand.php deleted file mode 100644 index 47ad8e2102..0000000000 --- a/src/Command/GenerateSymfonyConfigBuildersCommand.php +++ /dev/null @@ -1,100 +0,0 @@ -setName('generate-symfony-config-builders'); - - $this->setDescription( - 'Generate Symfony config classes to /var/cache/Symfony directory, see https://symfony.com/blog/new-in-symfony-5-3-config-builder-classes' - ); - } - - private function execute(InputInterface $input, OutputInterface $output): int - { - // make sure the classes exist - if (! class_exists(ConfigBuilderGenerator::class) || ! class_exists(ContainerBuilder::class)) { - $this->symfonyStyle->error( - 'This command requires symfony/config and symfony/dependency-injection 5.3+ to run. Update your dependencies or install them first.' - ); - - return \Entropy\Console\Enum\ExitCode::ERROR; - } - - $configBuilderGenerator = new ConfigBuilderGenerator(getcwd() . '/var/cache'); - $this->symfonyStyle->newLine(); - - foreach (self::EXTENSION_CLASSES as $extensionClass) { - // skip for non-existing classes - if (! class_exists($extensionClass)) { - continue; - } - - $configuration = $this->createExtensionConfiguration($extensionClass); - if (! $configuration instanceof ConfigurationInterface) { - continue; - } - - $extensionShortClass = (new ReflectionClass($extensionClass))->getShortName(); - $configBuilderGenerator->build($configuration); - - $this->symfonyStyle->writeln(sprintf('Generated "%s" class in /var/cache/Symfony', $extensionShortClass)); - } - - $this->symfonyStyle->success('Done'); - - return \Entropy\Console\Enum\ExitCode::SUCCESS; - } - - /** - * @param class-string $extensionClass - */ - private function createExtensionConfiguration(string $extensionClass): ?ConfigurationInterface - { - $containerBuilder = new ContainerBuilder(); - $containerBuilder->setParameter('kernel.debug', false); - - /** @var Extension $extension */ - $extension = new $extensionClass(); - - return $extension->getConfiguration([], $containerBuilder); - } -} diff --git a/src/Command/NamespaceToPSR4Command.php b/src/Command/NamespaceToPSR4Command.php index 3eeeafc646..4179d936bc 100644 --- a/src/Command/NamespaceToPSR4Command.php +++ b/src/Command/NamespaceToPSR4Command.php @@ -4,51 +4,30 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; +use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use Nette\Utils\Strings; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; -final class NamespaceToPSR4Command implements \Entropy\Console\Contract\CommandInterface +final class NamespaceToPSR4Command implements CommandInterface { public function __construct( private readonly SymfonyStyle $symfonyStyle, ) { - parent::__construct(); - } - - private function configure(): void - { - $this->setName('namespace-to-psr-4'); - - $this->setDescription('Change namespace in your PHP files to match PSR-4 root'); - - $this->addArgument( - 'path', - InputArgument::REQUIRED, - 'Single directory path to ensure namespace matches, e.g. "tests"' - ); - - $this->addOption( - 'namespace-root', - null, - InputOption::VALUE_REQUIRED, - 'Namespace root for files in provided path, e.g. "App\\Tests"' - ); } /** - * @return self::* + * @param string $path Single directory path to ensure namespace matches, e.g. "tests" + * @param string $namespaceRoot Namespace root for files in provided path, e.g. "App\\Tests" + * + * @return ExitCode::* */ - private function execute(InputInterface $input, OutputInterface $output): int + public function run(string $path, string $namespaceRoot): int { - $path = (string) $input->getArgument('path'); - $namespaceRoot = rtrim((string) $input->getOption('namespace-root'), '\\'); + $namespaceRoot = rtrim($namespaceRoot, '\\'); $namespaceRoot = str_replace('\\\\', '\\', $namespaceRoot); $fileInfos = $this->findFilesInPath($path); @@ -92,7 +71,17 @@ private function execute(InputInterface $input, OutputInterface $output): int $this->symfonyStyle->success(sprintf('Fixed %d files', $changedFilesCount)); } - return \Entropy\Console\Enum\ExitCode::SUCCESS; + return ExitCode::SUCCESS; + } + + public function getName(): string + { + return 'namespace-to-psr-4'; + } + + public function getDescription(): string + { + return 'Change namespace in your PHP files to match PSR-4 root'; } /** diff --git a/src/Command/PrivatizeConstantsCommand.php b/src/Command/PrivatizeConstantsCommand.php index 67408eb3fc..a08d685cbb 100644 --- a/src/Command/PrivatizeConstantsCommand.php +++ b/src/Command/PrivatizeConstantsCommand.php @@ -158,14 +158,4 @@ private function isClassConstantUsedPublicly(array $classConstantFetches, ClassC return false; } - - public function getName(): string - { - return 'privatize-constants'; - } - - public function getDescription(): string - { - return 'Make class constants private if not used outside in PHP, Twig and YAML files'; - } } diff --git a/src/DependencyInjection/ContainerFactory.php b/src/DependencyInjection/ContainerFactory.php index 32c3da2a4c..b7088c67b5 100644 --- a/src/DependencyInjection/ContainerFactory.php +++ b/src/DependencyInjection/ContainerFactory.php @@ -7,8 +7,6 @@ use Entropy\Container\Container; use PhpParser\Parser; use PhpParser\ParserFactory; -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Style\SymfonyStyle; @@ -24,20 +22,6 @@ public function create(): Container $container->autodiscover(__DIR__ . '/../Command'); - // console - $container->service(Application::class, function (Container $container): Application { - $application = new Application('Rector Swiss Knife'); - - $commands = $container->findByContract(Command::class); - $application->addCommands($commands); - - // remove basic command to make output clear - $this->hideDefaultCommands($application); - - return $application; - }); - - // parser $container->service(Parser::class, static function (): Parser { $phpParserFactory = new ParserFactory(); return $phpParserFactory->createForNewestSupportedVersion(); @@ -50,14 +34,4 @@ public function create(): Container return $container; } - - public function hideDefaultCommands(Application $application): void - { - $application->get('list') - ->setHidden(true); - $application->get('completion') - ->setHidden(true); - $application->get('help') - ->setHidden(true); - } } diff --git a/src/Enum/SymfonyExtensionClass.php b/src/Enum/SymfonyExtensionClass.php deleted file mode 100644 index 31bb615478..0000000000 --- a/src/Enum/SymfonyExtensionClass.php +++ /dev/null @@ -1,24 +0,0 @@ - Date: Fri, 9 Jan 2026 16:31:16 +0100 Subject: [PATCH 3/3] rerun rector --- .github/workflows/code_analysis.yaml | 4 +- README.md | 16 ++- composer.json | 2 +- phpstan.neon | 5 + rector.php | 12 ++- src/Command/AliceYamlFixturesToPhpCommand.php | 7 +- src/Command/CheckCommentedCodeCommand.php | 6 +- src/Command/CheckConflictsCommand.php | 11 +- src/Command/DumpEditorconfigCommand.php | 4 +- src/Command/FinalizeClassesCommand.php | 12 +-- src/Command/FindMultiClassesCommand.php | 10 +- .../GenerateSymfonyConfigBuildersCommand.php | 100 ++++++++++++++++++ src/Command/NamespaceToPSR4Command.php | 4 +- src/Command/PrettyJsonCommand.php | 11 +- src/Command/PrivatizeConstantsCommand.php | 13 +-- src/Command/SearchRegexCommand.php | 7 +- .../SplitSymfonyConfigToPerPackageCommand.php | 10 +- src/Command/SpotLazyTraitsCommand.php | 6 +- src/Enum/SymfonyExtensionClass.php | 24 +++++ src/RobotLoader/PhpClassLoader.php | 2 + .../Command/DetectUnitTestsCommand.php | 9 +- 21 files changed, 213 insertions(+), 62 deletions(-) create mode 100644 src/Command/GenerateSymfonyConfigBuildersCommand.php create mode 100644 src/Enum/SymfonyExtensionClass.php diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index e427c16a2a..7926b339f1 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -34,11 +34,11 @@ jobs: - name: 'Check Commented Code' - run: bin/swiss-knife check-commented-code src tests --ansi + run: bin/swiss-knife check-commented-code src tests - name: 'Check Active Classes' - run: vendor/bin/class-leak check bin src --ansi + run: vendor/bin/class-leak check bin src --skip-type="\Entropy\Console\Contract\CommandInterface" - name: 'Unusued check' diff --git a/README.md b/README.md index 80ca4d93f0..07c0d5d9a0 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,19 @@ That's it!
-## 9. Spots Fake Traits +## 9. Generate Symfony 5.3 configs builders + +Symfony 5.3 introduced amazing [config builders](https://symfony.com/blog/new-in-symfony-5-3-config-builder-classes), but those classes are not available for IDE until used. To make it easier, we added a command that generates all config builder classes you project can use, in `/var/cache/Symfony`. + +```bash +vendor/bin/swiss-knife generate-symfony-config-builders +``` + +That way IDE, PHPStan after adding those paths and Rector can understand your config files better. + +
+ +## 10. Spots Fake Traits What is trait has 5 lines and used in single service? We know it's better to be inlined, to empower IDE, Rector and PHPStan. But don't have time to worry about these details. @@ -263,7 +275,7 @@ That's it! Run this command once upon a time or run it in CI to eliminate traits
-## 10. Split huge Symfony config to per-package in directory +## 11. Split huge Symfony config to per-package in directory Do you have a huge Symfony config file that is hard to navigate? Do you want to split it to per-package files? diff --git a/composer.json b/composer.json index 6effa64101..0077e5ef17 100644 --- a/composer.json +++ b/composer.json @@ -21,10 +21,10 @@ "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^12.5", + "symfony/config": "^6.4", "rector/jack": "^0.5.1", "rector/rector": "^2.3", "shipmonk/composer-dependency-analyser": "^1.8", - "symfony/config": "^6.4", "symfony/dependency-injection": "^6.4", "symplify/phpstan-extensions": "^12.0", "tomasvotruba/class-leak": "^2.1", diff --git a/phpstan.neon b/phpstan.neon index 239e7a34d5..55574fd35b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -37,3 +37,8 @@ parameters: - identifier: public.method.unused message: '#Public method "Rector\\SwissKnife\\(.*?)Command\:\:run\(\)" is never used#' + + # depends on symfony extenison class presence + - + message: '#Parameter \#1 \$objectOrClass of class ReflectionClass constructor contains unresolvable type#' + path: src/Command/GenerateSymfonyConfigBuildersCommand.php diff --git a/rector.php b/rector.php index ec6fe64927..2870b9d88e 100644 --- a/rector.php +++ b/rector.php @@ -5,18 +5,20 @@ use Rector\Config\RectorConfig; return RectorConfig::configure() - ->withPaths([__DIR__ . '/src', __DIR__ . '/tests']) + ->withPaths([__DIR__ . '/bin', __DIR__ . '/src', __DIR__ . '/tests']) ->withPhpSets() + ->withRootFiles() ->withPreparedSets( - codeQuality: true, deadCode: true, + codeQuality: true, + codingStyle: true, typeDeclarations: true, typeDeclarationDocblocks: true, privatization: true, - earlyReturn: true, - codingStyle: true, + naming: true, instanceOf: true, - naming: true + earlyReturn: true, + rectorPreset: true, ) ->withImportNames(removeUnusedImports: true) ->withSkip(['*/scoper.php', '*/Source/*', '*/Fixture/*']); diff --git a/src/Command/AliceYamlFixturesToPhpCommand.php b/src/Command/AliceYamlFixturesToPhpCommand.php index 7767517423..5197d042ab 100644 --- a/src/Command/AliceYamlFixturesToPhpCommand.php +++ b/src/Command/AliceYamlFixturesToPhpCommand.php @@ -4,6 +4,7 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use PhpParser\BuilderHelpers; @@ -16,10 +17,10 @@ /** * @see https://github.com/nelmio/alice/blob/v2.3.0/doc/complete-reference.md#php */ -final class AliceYamlFixturesToPhpCommand implements \Entropy\Console\Contract\CommandInterface +final readonly class AliceYamlFixturesToPhpCommand implements CommandInterface { public function __construct( - private readonly SymfonyStyle $symfonyStyle, + private SymfonyStyle $symfonyStyle, ) { } @@ -61,7 +62,7 @@ public function run(array $sources): int sprintf('Successfully converted %d Alice YAML fixtures to PHP', count($yamlFileInfos)) ); - return \Entropy\Console\Enum\ExitCode::SUCCESS; + return ExitCode::SUCCESS; } public function getName(): string diff --git a/src/Command/CheckCommentedCodeCommand.php b/src/Command/CheckCommentedCodeCommand.php index d3f2f30d22..166951fa63 100644 --- a/src/Command/CheckCommentedCodeCommand.php +++ b/src/Command/CheckCommentedCodeCommand.php @@ -10,13 +10,13 @@ use Rector\SwissKnife\Finder\PhpFilesFinder; use Symfony\Component\Console\Style\SymfonyStyle; -final class CheckCommentedCodeCommand implements CommandInterface +final readonly class CheckCommentedCodeCommand implements CommandInterface { private const int DEFAULT_LINE_LIMIT = 5; public function __construct( - private readonly CommentedCodeAnalyzer $commentedCodeAnalyzer, - private readonly SymfonyStyle $symfonyStyle, + private CommentedCodeAnalyzer $commentedCodeAnalyzer, + private SymfonyStyle $symfonyStyle, ) { } diff --git a/src/Command/CheckConflictsCommand.php b/src/Command/CheckConflictsCommand.php index 2b74da0a5b..75121b7491 100644 --- a/src/Command/CheckConflictsCommand.php +++ b/src/Command/CheckConflictsCommand.php @@ -4,16 +4,17 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; use Entropy\Console\Enum\ExitCode; use Rector\SwissKnife\Finder\FilesFinder; use Rector\SwissKnife\Git\ConflictResolver; use Symfony\Component\Console\Style\SymfonyStyle; -final class CheckConflictsCommand implements \Entropy\Console\Contract\CommandInterface +final readonly class CheckConflictsCommand implements CommandInterface { public function __construct( - private readonly ConflictResolver $conflictResolver, - private readonly SymfonyStyle $symfonyStyle, + private ConflictResolver $conflictResolver, + private SymfonyStyle $symfonyStyle, ) { } @@ -35,7 +36,7 @@ public function run(array $sources): int $message = sprintf('No conflicts found in %d files', count($fileInfos)); $this->symfonyStyle->success($message); - return \Entropy\Console\Enum\ExitCode::SUCCESS; + return ExitCode::SUCCESS; } foreach ($conflictsCountByFilePath as $file => $conflictCount) { @@ -43,7 +44,7 @@ public function run(array $sources): int $this->symfonyStyle->error($message); } - return \Entropy\Console\Enum\ExitCode::ERROR; + return ExitCode::ERROR; } public function getName(): string diff --git a/src/Command/DumpEditorconfigCommand.php b/src/Command/DumpEditorconfigCommand.php index d8441a0175..3919429ecc 100644 --- a/src/Command/DumpEditorconfigCommand.php +++ b/src/Command/DumpEditorconfigCommand.php @@ -9,10 +9,10 @@ use Nette\Utils\FileSystem; use Symfony\Component\Console\Style\SymfonyStyle; -final class DumpEditorconfigCommand implements CommandInterface +final readonly class DumpEditorconfigCommand implements CommandInterface { public function __construct( - private readonly SymfonyStyle $symfonyStyle, + private SymfonyStyle $symfonyStyle, ) { } diff --git a/src/Command/FinalizeClassesCommand.php b/src/Command/FinalizeClassesCommand.php index 57075bc1ea..207872273c 100644 --- a/src/Command/FinalizeClassesCommand.php +++ b/src/Command/FinalizeClassesCommand.php @@ -17,7 +17,7 @@ use Rector\SwissKnife\PhpParser\CachedPhpParser; use Symfony\Component\Console\Style\SymfonyStyle; -final class FinalizeClassesCommand implements CommandInterface +final readonly class FinalizeClassesCommand implements CommandInterface { /** * @see https://regex101.com/r/Q5Nfbo/1 @@ -25,11 +25,11 @@ final class FinalizeClassesCommand implements CommandInterface private const string NEWLINE_CLASS_START_REGEX = '#^(readonly )?class\s#m'; public function __construct( - private readonly SymfonyStyle $symfonyStyle, - private readonly ParentClassResolver $parentClassResolver, - private readonly EntityClassResolver $entityClassResolver, - private readonly CachedPhpParser $cachedPhpParser, - private readonly MockedClassResolver $mockedClassResolver, + private SymfonyStyle $symfonyStyle, + private ParentClassResolver $parentClassResolver, + private EntityClassResolver $entityClassResolver, + private CachedPhpParser $cachedPhpParser, + private MockedClassResolver $mockedClassResolver, ) { } diff --git a/src/Command/FindMultiClassesCommand.php b/src/Command/FindMultiClassesCommand.php index 85c3703dc7..3a607b65a4 100644 --- a/src/Command/FindMultiClassesCommand.php +++ b/src/Command/FindMultiClassesCommand.php @@ -11,11 +11,11 @@ use Rector\SwissKnife\Finder\PhpFilesFinder; use Symfony\Component\Console\Style\SymfonyStyle; -final class FindMultiClassesCommand implements CommandInterface +final readonly class FindMultiClassesCommand implements CommandInterface { public function __construct( - private readonly MultipleClassInOneFileFinder $multipleClassInOneFileFinder, - private readonly SymfonyStyle $symfonyStyle, + private MultipleClassInOneFileFinder $multipleClassInOneFileFinder, + private SymfonyStyle $symfonyStyle, ) { } @@ -33,7 +33,7 @@ public function run(array $sources, array $excludePaths): int if ($multipleClassesByFile === []) { $this->symfonyStyle->success(sprintf('No file with 2+ classes found in %d files', count($phpFileInfos))); - return \Entropy\Console\Enum\ExitCode::SUCCESS; + return ExitCode::SUCCESS; } foreach ($multipleClassesByFile as $filePath => $classes) { @@ -45,7 +45,7 @@ public function run(array $sources, array $excludePaths): int $this->symfonyStyle->listing($classes); } - return \Entropy\Console\Enum\ExitCode::ERROR; + return ExitCode::ERROR; } public function getName(): string diff --git a/src/Command/GenerateSymfonyConfigBuildersCommand.php b/src/Command/GenerateSymfonyConfigBuildersCommand.php new file mode 100644 index 0000000000..66ebc03035 --- /dev/null +++ b/src/Command/GenerateSymfonyConfigBuildersCommand.php @@ -0,0 +1,100 @@ +symfonyStyle->error( + 'This command requires symfony/config and symfony/dependency-injection 5.3+ to run. Update your dependencies or install them first.' + ); + + return ExitCode::ERROR; + } + + $configBuilderGenerator = new ConfigBuilderGenerator(getcwd() . '/var/cache'); + $this->symfonyStyle->newLine(); + + foreach (self::EXTENSION_CLASSES as $extensionClass) { + // skip for non-existing classes + if (! class_exists($extensionClass)) { + continue; + } + + $configuration = $this->createExtensionConfiguration($extensionClass); + if (! $configuration instanceof ConfigurationInterface) { + continue; + } + + $extensionShortClass = (new ReflectionClass($extensionClass))->getShortName(); + $configBuilderGenerator->build($configuration); + + $this->symfonyStyle->writeln(sprintf('Generated "%s" class in /var/cache/Symfony', $extensionShortClass)); + } + + $this->symfonyStyle->success('Done'); + + return ExitCode::SUCCESS; + } + + /** + * @param class-string $extensionClass + */ + private function createExtensionConfiguration(string $extensionClass): ?ConfigurationInterface + { + $containerBuilder = new ContainerBuilder(); + $containerBuilder->setParameter('kernel.debug', false); + + /** @var Extension $extension */ + $extension = new $extensionClass(); + + return $extension->getConfiguration([], $containerBuilder); + } +} diff --git a/src/Command/NamespaceToPSR4Command.php b/src/Command/NamespaceToPSR4Command.php index 4179d936bc..b36ed03cdc 100644 --- a/src/Command/NamespaceToPSR4Command.php +++ b/src/Command/NamespaceToPSR4Command.php @@ -12,10 +12,10 @@ use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; -final class NamespaceToPSR4Command implements CommandInterface +final readonly class NamespaceToPSR4Command implements CommandInterface { public function __construct( - private readonly SymfonyStyle $symfonyStyle, + private SymfonyStyle $symfonyStyle, ) { } diff --git a/src/Command/PrettyJsonCommand.php b/src/Command/PrettyJsonCommand.php index f80aad9d21..9273af135b 100644 --- a/src/Command/PrettyJsonCommand.php +++ b/src/Command/PrettyJsonCommand.php @@ -4,6 +4,7 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use Nette\Utils\Json; @@ -11,11 +12,11 @@ use Rector\SwissKnife\Finder\FilesFinder; use Symfony\Component\Console\Style\SymfonyStyle; -final class PrettyJsonCommand implements \Entropy\Console\Contract\CommandInterface +final readonly class PrettyJsonCommand implements CommandInterface { public function __construct( - private readonly SymfonyStyle $symfonyStyle, - private readonly JsonAnalyzer $jsonAnalyzer, + private SymfonyStyle $symfonyStyle, + private JsonAnalyzer $jsonAnalyzer, ) { } @@ -31,7 +32,7 @@ public function run(array $sources, bool $dryRun = false): int if ($jsonFileInfos === []) { $this->symfonyStyle->error('No *.json files found'); - return \Entropy\Console\Enum\ExitCode::ERROR; + return ExitCode::ERROR; } $message = sprintf('Analysing %d *.json files', count($jsonFileInfos)); @@ -71,7 +72,7 @@ public function run(array $sources, bool $dryRun = false): int $this->symfonyStyle->success($successMessage); $this->symfonyStyle->listing($printedFilePaths); - return \Entropy\Console\Enum\ExitCode::SUCCESS; + return ExitCode::SUCCESS; } public function getName(): string diff --git a/src/Command/PrivatizeConstantsCommand.php b/src/Command/PrivatizeConstantsCommand.php index a08d685cbb..2afb7d8c67 100644 --- a/src/Command/PrivatizeConstantsCommand.php +++ b/src/Command/PrivatizeConstantsCommand.php @@ -4,6 +4,7 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; use Entropy\Console\Enum\ExitCode; use Nette\Utils\FileSystem; use Nette\Utils\Strings; @@ -19,14 +20,14 @@ use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\SplFileInfo; -final class PrivatizeConstantsCommand implements \Entropy\Console\Contract\CommandInterface +final readonly class PrivatizeConstantsCommand implements CommandInterface { public function __construct( - private readonly SymfonyStyle $symfonyStyle, - private readonly ClassConstantFetchFinder $classConstantFetchFinder, - private readonly ClassConstFinder $classConstFinder, - private readonly TwigTemplateConstantExtractor $twigTemplateConstantExtractor, - private readonly YamlConfigConstantExtractor $yamlConfigConstantExtractor + private SymfonyStyle $symfonyStyle, + private ClassConstantFetchFinder $classConstantFetchFinder, + private ClassConstFinder $classConstFinder, + private TwigTemplateConstantExtractor $twigTemplateConstantExtractor, + private YamlConfigConstantExtractor $yamlConfigConstantExtractor ) { } diff --git a/src/Command/SearchRegexCommand.php b/src/Command/SearchRegexCommand.php index 0b1cb3d947..f31017867a 100644 --- a/src/Command/SearchRegexCommand.php +++ b/src/Command/SearchRegexCommand.php @@ -4,16 +4,17 @@ namespace Rector\SwissKnife\Command; +use Entropy\Console\Contract\CommandInterface; use Entropy\Console\Enum\ExitCode; use Nette\Utils\Strings; use Rector\SwissKnife\Finder\PhpFilesFinder; use Symfony\Component\Console\Style\SymfonyStyle; use Webmozart\Assert\Assert; -final class SearchRegexCommand implements \Entropy\Console\Contract\CommandInterface +final readonly class SearchRegexCommand implements CommandInterface { public function __construct( - private readonly SymfonyStyle $symfonyStyle, + private SymfonyStyle $symfonyStyle, ) { } @@ -68,7 +69,7 @@ public function run(string $regex, ?string $projectDirectory = null): int $this->symfonyStyle->newLine(2); $this->symfonyStyle->success(sprintf('Found %d cases in %d files', $foundCasesCount, count($markedFiles))); - return \Entropy\Console\Enum\ExitCode::SUCCESS; + return ExitCode::SUCCESS; } public function getName(): string diff --git a/src/Command/SplitSymfonyConfigToPerPackageCommand.php b/src/Command/SplitSymfonyConfigToPerPackageCommand.php index f1f4725538..ac8da390d4 100644 --- a/src/Command/SplitSymfonyConfigToPerPackageCommand.php +++ b/src/Command/SplitSymfonyConfigToPerPackageCommand.php @@ -21,13 +21,13 @@ use Symfony\Component\Console\Style\SymfonyStyle; use Webmozart\Assert\Assert; -final class SplitSymfonyConfigToPerPackageCommand implements CommandInterface +final readonly class SplitSymfonyConfigToPerPackageCommand implements CommandInterface { - private readonly Standard $printerStandard; + private Standard $printerStandard; public function __construct( - private readonly SymfonyStyle $symfonyStyle, - private readonly SplitConfigClosureFactory $splitConfigClosureFactory, + private SymfonyStyle $symfonyStyle, + private SplitConfigClosureFactory $splitConfigClosureFactory, ) { $this->printerStandard = new Standard(); } @@ -50,7 +50,7 @@ public function run(string $configPath, string $outputDir): int if ($symfonyExtensionMethodCalls === []) { $this->symfonyStyle->warning('No extension() method calls found'); - return \Entropy\Console\Enum\ExitCode::SUCCESS; + return ExitCode::SUCCESS; } foreach ($symfonyExtensionMethodCalls as $symfonyExtensionMethodCall) { diff --git a/src/Command/SpotLazyTraitsCommand.php b/src/Command/SpotLazyTraitsCommand.php index dcb4e59ee7..b404fccbe6 100644 --- a/src/Command/SpotLazyTraitsCommand.php +++ b/src/Command/SpotLazyTraitsCommand.php @@ -9,11 +9,11 @@ use Rector\SwissKnife\Traits\TraitSpotter; use Symfony\Component\Console\Style\SymfonyStyle; -final class SpotLazyTraitsCommand implements CommandInterface +final readonly class SpotLazyTraitsCommand implements CommandInterface { public function __construct( - private readonly SymfonyStyle $symfonyStyle, - private readonly TraitSpotter $traitSpotter, + private SymfonyStyle $symfonyStyle, + private TraitSpotter $traitSpotter, ) { } diff --git a/src/Enum/SymfonyExtensionClass.php b/src/Enum/SymfonyExtensionClass.php new file mode 100644 index 0000000000..31bb615478 --- /dev/null +++ b/src/Enum/SymfonyExtensionClass.php @@ -0,0 +1,24 @@ +