diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index 6d8c913049..06eeaf1da1 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -27,9 +27,13 @@ jobs: name: 'Tests' run: vendor/bin/phpunit + - + name: 'Main run' + run: php bin/behastan + - 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" --skip-type="Rector\Behastan\Contract\RuleInterface" name: ${{ matrix.actions.name }} runs-on: ubuntu-latest @@ -39,7 +43,7 @@ jobs: # see https://github.com/shivammathur/setup-php - uses: shivammathur/setup-php@v2 with: - php-version: 8.2 + php-version: 8.3 coverage: none # composer install cache - https://github.com/ramsey/composer-install diff --git a/.github/workflows/downgraded_release.yaml b/.github/workflows/downgraded_release.yaml new file mode 100644 index 0000000000..aae39f6e96 --- /dev/null +++ b/.github/workflows/downgraded_release.yaml @@ -0,0 +1,65 @@ +name: Downgraded Release + +on: + push: + tags: + # see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-including-and-excluding-branches + - '*' + +jobs: + downgrade_release: + runs-on: ubuntu-latest + + steps: + - uses: "actions/checkout@v3" + with: + token: ${{ secrets.WORKFLOWS_TOKEN }} + + - + uses: "shivammathur/setup-php@v2" + with: + php-version: 8.3 + coverage: none + + # invoke patches + - run: composer install --ansi + + # but no dev packages + - run: composer update --no-dev --ansi + + # get rector to "rector-local" directory, to avoid downgrading itself in the /vendor + - run: mkdir rector-local + - run: composer require rector/rector --working-dir rector-local --ansi + + # downgrade to PHP 7.4 + - run: rector-local/vendor/bin/rector process bin src vendor --config build/rector-downgrade-php.php --ansi + + # clear the dev files + - run: rm -rf tests ecs.php phpstan.neon phpunit.xml .gitignore .editorconfig + + # prefix and scope + - run: sh prefix-code.sh + + # copy PHP 7.4 composer + workflows + - run: cp -r build/target-repository/. . + + # clear the dev files + - run: rm -rf build prefix-code.sh full-tool-build.sh scoper.php rector.php php-scoper.phar rector-local + + # setup git user + - + run: | + git config user.email "action@github.com" + git config user.name "GitHub Action" + # publish to the same repository with a new tag + # see https://tomasvotruba.com/blog/how-to-release-php-81-and-72-package-in-the-same-repository/ + - + name: "Tag Downgraded Code" + run: | + # separate a "git add" to add untracked (new) files too + git add --all + git commit -m "release PHP 7.2 downgraded" + + # force push tag, so there is only 1 version + git tag "${GITHUB_REF#refs/tags/}" --force + git push origin "${GITHUB_REF#refs/tags/}" --force diff --git a/bin/behastan.php b/bin/behastan.php index dd83dd5ec8..95b95729a8 100755 --- a/bin/behastan.php +++ b/bin/behastan.php @@ -3,9 +3,6 @@ declare(strict_types=1); use Rector\Behastan\DependencyInjection\ContainerFactory; -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Output\ConsoleOutput; $possibleAutoloadPaths = [ // dependency @@ -28,10 +25,8 @@ require_once $scoperAutoloadFilepath; } -$containerFactory = new ContainerFactory(); -$container = $containerFactory->create(); +$container = ContainerFactory::create(); +$consoleApplication = $container->make(\Entropy\Console\ConsoleApplication::class); -$application = $container->make(Application::class); - -$exitCode = $application->run(new ArgvInput(), new ConsoleOutput()); +$exitCode = $consoleApplication->run($argv); exit($exitCode); diff --git a/build/build-scoped.sh b/build/build-scoped.sh new file mode 100755 index 0000000000..d9fde8f448 --- /dev/null +++ b/build/build-scoped.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# inspired from https://github.com/rectorphp/rector/blob/main/build/build-rector-scoped.sh + +# see https://stackoverflow.com/questions/66644233/how-to-propagate-colors-from-bash-script-to-github-action?noredirect=1#comment117811853_66644233 +export TERM=xterm-color + +# show errors +set -e + +# script fails if trying to access to an undefined variable +set -u + + +# functions +note() +{ + MESSAGE=$1; + printf "\n"; + echo "\033[0;33m[NOTE] $MESSAGE\033[0m"; +} + + +# configure here +BUILD_DIRECTORY=$1 +RESULT_DIRECTORY=$2 + +# --------------------------- + +note "Starts" + +# 2. scope it +note "Running scoper with '$RESULT_DIRECTORY' output directory" +wget https://github.com/humbug/php-scoper/releases/download/0.18.18/php-scoper.phar -N --no-verbose + +# create directory +mkdir "$RESULT_DIRECTORY" -p + +# Work around possible PHP memory limits +php -d memory_limit=-1 php-scoper.phar add-prefix bin src stubs vendor composer.json --output-dir "../$RESULT_DIRECTORY" --config scoper.php --force --ansi --working-dir "$BUILD_DIRECTORY" + +note "Show prefixed files in '$RESULT_DIRECTORY'" +ls -l $RESULT_DIRECTORY + +note "Dumping Composer Autoload" +composer dump-autoload --working-dir "$RESULT_DIRECTORY" --ansi --classmap-authoritative --no-dev + +# make bin/behastan runnable without "php" +chmod 777 "$RESULT_DIRECTORY/bin/behastan" +chmod 777 "$RESULT_DIRECTORY/bin/behastan.php" + +note "Finished" diff --git a/build/rector-downgrade-php.php b/build/rector-downgrade-php.php new file mode 100644 index 0000000000..c768643a04 --- /dev/null +++ b/build/rector-downgrade-php.php @@ -0,0 +1,12 @@ +withDowngradeSets(php72: true) + ->withSkip([ + '*/Tests/*', + '*/tests/*', + ]); diff --git a/build/target-repository/.github/FUNDING.yml b/build/target-repository/.github/FUNDING.yml new file mode 100644 index 0000000000..f797866a90 --- /dev/null +++ b/build/target-repository/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms +github: tomasvotruba +custom: https://www.paypal.me/rectorphp diff --git a/build/target-repository/.github/workflows/bare_run.yaml b/build/target-repository/.github/workflows/bare_run.yaml new file mode 100644 index 0000000000..459481c205 --- /dev/null +++ b/build/target-repository/.github/workflows/bare_run.yaml @@ -0,0 +1,23 @@ +name: Bare Run + +on: [pull_request, push] + +jobs: + bare_run: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php_version: ['7.2', '7.4', '8.0', '8.2'] + + steps: + - uses: actions/checkout@v2 + + - + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + coverage: none + + - run: php bin/behastan diff --git a/build/target-repository/composer.json b/build/target-repository/composer.json new file mode 100644 index 0000000000..ffe52dca17 --- /dev/null +++ b/build/target-repository/composer.json @@ -0,0 +1,16 @@ +{ + "name": "rector/behastan", + "description": "Static analysis for Behat definitions and context files", + "license": "MIT", + "require": { + "php": ">=7.2" + }, + "bin": [ + "bin/behastan" + ], + "autoload": { + "psr-4": { + "Rector\\Behastan\\": "src" + } + } +} diff --git a/composer.json b/composer.json index 3ba3994f9e..bc6dd026b7 100644 --- a/composer.json +++ b/composer.json @@ -7,11 +7,10 @@ ], "require": { "php": "^8.2", - "illuminate/container": "^11.41|^12.37", "nette/utils": "^4.0", + "entropy/entropy": "dev-main", "nikic/php-parser": "^5.6", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/finder": "^7.0", "webmozart/assert": "^1.12" }, "require-dev": { @@ -40,7 +39,8 @@ "sort-packages": true, "allow-plugins": { "phpstan/extension-installer": true - } + }, + "platform-check": false }, "scripts": { "check-cs": "vendor/bin/ecs check --ansi", diff --git a/full-tool-build.sh b/full-tool-build.sh new file mode 100644 index 0000000000..94f37107f4 --- /dev/null +++ b/full-tool-build.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# add patches +composer install --ansi + +# but skip dev dependencies +composer update --no-dev --ansi + +# remove tests and useless files, to make downgraded, scoped and deployed codebase as small as possible +rm -rf tests + +# downgrade with rector +mkdir rector-local +composer require rector/rector --working-dir rector-local +rector-local/vendor/bin/rector process bin src vendor --config build/rector-downgrade-php-72.php --ansi + +# prefix +sh prefix-code.sh diff --git a/prefix-code.sh b/prefix-code.sh new file mode 100644 index 0000000000..0fa0668e5e --- /dev/null +++ b/prefix-code.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# inspired from https://github.com/rectorphp/rector/blob/main/build/build-rector-scoped.sh + +# see https://stackoverflow.com/questions/66644233/how-to-propagate-colors-from-bash-script-to-github-action?noredirect=1#comment117811853_66644233 +export TERM=xterm-color + +# show errors +set -e + +# script fails if trying to access to an undefined variable +set -u + + +# functions +note() +{ + MESSAGE=$1; + printf "\n"; + echo "\033[0;33m[NOTE] $MESSAGE\033[0m"; +} + +# --------------------------- + +# 2. scope it +note "Downloading php-scoper 0.18.11" +wget https://github.com/humbug/php-scoper/releases/download/0.18.18/php-scoper.phar -N --no-verbose + + +note "Running php-scoper" + +# Work around possible PHP memory limits +php -d memory_limit=-1 php-scoper.phar add-prefix bin src vendor composer.json --config scoper.php --force --ansi --output-dir scoped-code + +# the output code is in "/scoped-code", lets move it up +# the local directories have to be empty to move easily +rm -r bin src vendor composer.json +mv scoped-code/* . + +note "Dumping Composer Autoload" +composer dump-autoload --ansi --classmap-authoritative --no-dev + +# make bin/ecs runnable without "php" +chmod 777 "bin/behastan" +chmod 777 "bin/behastan.php" + +note "Finished" diff --git a/rector.php b/rector.php index ec52a77e36..3a97a08e75 100644 --- a/rector.php +++ b/rector.php @@ -19,5 +19,11 @@ naming: true, rectorPreset: true, ) + ->withSkip([ + \Rector\Php55\Rector\String_\StringClassNameToClassConstantRector::class => [ + // keep string class names + __DIR__ . '/src/Resolver/ClassMethodMasksResolver.php', + ], + ]) ->withImportNames() ->withSkip(['*/scoper.php', '*/Source/*', '*/Fixture/*']); diff --git a/src/Analyzer/UnusedDefinitionsAnalyzer.php b/src/Analyzer/UnusedDefinitionsAnalyzer.php index 43ad773ef3..a023a8cd75 100644 --- a/src/Analyzer/UnusedDefinitionsAnalyzer.php +++ b/src/Analyzer/UnusedDefinitionsAnalyzer.php @@ -4,6 +4,7 @@ namespace Rector\Behastan\Analyzer; +use Entropy\Console\Output\OutputPrinter; use Nette\Utils\Strings; use Rector\Behastan\DefinitionMasksExtractor; use Rector\Behastan\UsedInstructionResolver; @@ -13,7 +14,6 @@ use Rector\Behastan\ValueObject\Mask\RegexMask; use Rector\Behastan\ValueObject\Mask\SkippedMask; use Rector\Behastan\ValueObject\MaskCollection; -use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\SplFileInfo; use Webmozart\Assert\Assert; @@ -28,7 +28,6 @@ private const MASK_VALUE_REGEX = '#(\:[\W\w]+)#'; public function __construct( - private SymfonyStyle $symfonyStyle, private UsedInstructionResolver $usedInstructionResolver, private DefinitionMasksExtractor $definitionMasksExtractor, ) { @@ -55,11 +54,11 @@ public function analyse(array $contextFiles, array $featureFiles, MaskCollection $maskCollection = $this->definitionMasksExtractor->extract($contextFiles); $featureInstructions = $this->usedInstructionResolver->resolveInstructionsFromFeatureFiles($featureFiles); - $maskProgressBar = $this->symfonyStyle->createProgressBar($maskCollection->count()); + //$maskProgressBar = $this->outputPrinter->createProgressBar($maskCollection->count()); $unusedMasks = []; foreach ($maskCollection->all() as $mask) { - $maskProgressBar->advance(); + // $maskProgressBar->advance(); if ($this->isMaskUsed($mask, $featureInstructions)) { continue; @@ -68,7 +67,7 @@ public function analyse(array $contextFiles, array $featureFiles, MaskCollection $unusedMasks[] = $mask; } - $maskProgressBar->finish(); + // $maskProgressBar->finish(); return $unusedMasks; } diff --git a/src/Command/AnalyzeCommand.php b/src/Command/AnalyzeCommand.php index 53eeaf54b7..330e4064dd 100644 --- a/src/Command/AnalyzeCommand.php +++ b/src/Command/AnalyzeCommand.php @@ -4,144 +4,131 @@ namespace Rector\Behastan\Command; -use Rector\Behastan\Contract\RuleInterface; +use Entropy\Console\Contract\CommandInterface; +use Entropy\Console\Output\OutputPrinter; use Rector\Behastan\DefinitionMasksExtractor; -use Rector\Behastan\Enum\Option; use Rector\Behastan\Finder\BehatMetafilesFinder; use Rector\Behastan\Reporting\MaskCollectionStatsPrinter; +use Rector\Behastan\RulesRegistry; use Rector\Behastan\ValueObject\RuleError; -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 Rector\Console\ExitCode; use Webmozart\Assert\Assert; -final class AnalyzeCommand extends Command +final readonly class AnalyzeCommand implements CommandInterface { - /** - * @param RuleInterface[] $rules - */ public function __construct( - private readonly SymfonyStyle $symfonyStyle, - private readonly DefinitionMasksExtractor $definitionMasksExtractor, - private readonly MaskCollectionStatsPrinter $maskCollectionStatsPrinter, - private readonly array $rules + private DefinitionMasksExtractor $definitionMasksExtractor, + private MaskCollectionStatsPrinter $maskCollectionStatsPrinter, + private OutputPrinter $outputPrinter, + private RulesRegistry $rulesRegistry, ) { - parent::__construct(); - - Assert::allObject($rules); - Assert::allIsInstanceOf($rules, RuleInterface::class); - Assert::notEmpty($rules); - Assert::greaterThan(count($rules), 2); } - protected function configure(): void + /** + * @param string $projectDirectory Project directory (we find *.Context.php definition files and *.feature script files there) + * @param string[] $skip Skip a rule by identifier + * + * @return ExitCode::* + */ + public function run(?string $projectDirectory = null, array $skip = []): int { - $this->setName('analyze'); - $this->setAliases(['analyse']); - - $this->setDescription('Run complete static analysis on Behat definitions and features'); - - $this->addArgument( - Option::PROJECT_DIRECTORY, - InputArgument::OPTIONAL, - 'Project directory (we find *.Context.php definition files and *.feature script files there)', - getcwd() - ); - - $this->addOption( - 'skip', - null, - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Skip a rule by identifier' - ); - } + // fallback to current directory + if ($projectDirectory === null) { + $projectDirectory = getcwd(); + Assert::string($projectDirectory); + } - protected function execute(InputInterface $input, OutputInterface $output): int - { - $testDirectory = $input->getArgument(Option::PROJECT_DIRECTORY); - Assert::directory($testDirectory); + Assert::directory($projectDirectory); - $contextFileInfos = BehatMetafilesFinder::findContextFiles([$testDirectory]); + $contextFileInfos = BehatMetafilesFinder::findContextFiles([$projectDirectory]); if ($contextFileInfos === []) { - $this->symfonyStyle->error(sprintf( + $this->outputPrinter->redBackground(sprintf( 'No *.Context files found in "%s". Please provide correct directory', - $testDirectory + $projectDirectory )); - return self::FAILURE; + + return ExitCode::FAILURE; } - $featureFileInfos = BehatMetafilesFinder::findFeatureFiles([$testDirectory]); + $featureFileInfos = BehatMetafilesFinder::findFeatureFiles([$projectDirectory]); if ($featureFileInfos === []) { - $this->symfonyStyle->error(sprintf( + $this->outputPrinter->redBackground(sprintf( 'No *.feature files found in "%s". Please provide correct directory', - $testDirectory + $projectDirectory )); - return self::FAILURE; - } - $skips = $input->getOption('skip'); + return ExitCode::FAILURE; + } - $this->symfonyStyle->writeln(sprintf( + $this->outputPrinter->writeln(sprintf( 'Found %d Context and %d feature files', count($contextFileInfos), count($featureFileInfos) )); - $this->symfonyStyle->writeln('Extracting definitions masks...'); + $this->outputPrinter->writeln('Extracting definitions masks...'); $maskCollection = $this->definitionMasksExtractor->extract($contextFileInfos); - $this->symfonyStyle->newLine(); + $this->outputPrinter->newLine(); $this->maskCollectionStatsPrinter->print($maskCollection); - $this->symfonyStyle->newLine(); + $this->outputPrinter->newLine(); - $this->symfonyStyle->writeln('Running analysis...'); - $this->symfonyStyle->newLine(); + $this->outputPrinter->writeln('Running analysis...'); + $this->outputPrinter->newLine(); /** @var RuleError[] $allRuleErrors */ $allRuleErrors = []; - foreach ($this->rules as $rule) { - if ($skips !== [] && in_array($rule->getIdentifier(), $skips, true)) { - $this->symfonyStyle->writeln(sprintf('Skipping "%s" rule', $rule->getIdentifier())); - $this->symfonyStyle->newLine(); + foreach ($this->rulesRegistry->all() as $rule) { + if ($skip !== [] && in_array($rule->getIdentifier(), $skip, true)) { + $this->outputPrinter->writeln(sprintf('Skipping "%s" rule', $rule->getIdentifier())); + $this->outputPrinter->newLine(); continue; } - $ruleErrors = $rule->process($contextFileInfos, $featureFileInfos, $maskCollection, $testDirectory); + $ruleErrors = $rule->process($contextFileInfos, $featureFileInfos, $maskCollection, $projectDirectory); $allRuleErrors = array_merge($allRuleErrors, $ruleErrors); } if ($allRuleErrors === []) { - $this->symfonyStyle->newLine(2); - $this->symfonyStyle->success('No errors found. Good job!'); + $this->outputPrinter->newLine(2); + $this->outputPrinter->greenBackground('No errors found. Good job!'); - return self::SUCCESS; + return ExitCode::SUCCESS; } - $this->symfonyStyle->newLine(2); + $this->outputPrinter->newLine(2); $i = 1; foreach ($allRuleErrors as $allRuleError) { - $this->symfonyStyle->writeln(sprintf('%d) %s', $i, $allRuleError->getMessage())); + $this->outputPrinter->writeln(sprintf('%d) %s', $i, $allRuleError->getMessage())); foreach ($allRuleError->getLineFilePaths() as $lineFilePath) { // compared to listing() this allow to make paths clickable in IDE - $this->symfonyStyle->writeln($lineFilePath); + $this->outputPrinter->writeln($lineFilePath); } - $this->symfonyStyle->newLine(2); + $this->outputPrinter->newLine(2); ++$i; } - $this->symfonyStyle->newLine(); - $this->symfonyStyle->error(sprintf( + $this->outputPrinter->newLine(); + + $this->outputPrinter->redBackground(sprintf( 'Found %d error%s', count($allRuleErrors), count($allRuleErrors) > 1 ? 's' : '' )); - return self::FAILURE; + return ExitCode::FAILURE; + } + + public function getName(): string + { + return 'analyze'; + } + + public function getDescription(): string + { + return 'Run complete static analysis on Behat definitions and features'; } } diff --git a/src/Command/DuplicatedDefinitionsCommand.php b/src/Command/DuplicatedDefinitionsCommand.php deleted file mode 100644 index 35bb756be4..0000000000 --- a/src/Command/DuplicatedDefinitionsCommand.php +++ /dev/null @@ -1,48 +0,0 @@ -setName('duplicated-definitions'); - - $this->setDescription( - '[DEPRECATED] Find duplicated definitions in *Context.php, use just one to keep definitions clear and to the point' - ); - - $this->addArgument( - Option::PROJECT_DIRECTORY, - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'Director with *.Context.php definition files' - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $this->symfonyStyle->error( - 'This command was deprecated, to avoid granular rules. Use "analyze" command that runs them all instead' - ); - - return self::FAILURE; - } -} diff --git a/src/Command/UnusedDefinitionsCommand.php b/src/Command/UnusedDefinitionsCommand.php deleted file mode 100644 index 049232b98b..0000000000 --- a/src/Command/UnusedDefinitionsCommand.php +++ /dev/null @@ -1,48 +0,0 @@ -setName('unused-definitions'); - - $this->setDescription( - '[DEPRECATED] Checks Behat definitions in *Context.php files and feature files to spot unused ones' - ); - - $this->addArgument( - Option::PROJECT_DIRECTORY, - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'One or more paths to check or *.Context.php and feature.yml files' - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $this->symfonyStyle->error( - 'This command was deprecated, to avoid granular rules. Use "analyze" command that runs them all instead' - ); - - return self::FAILURE; - } -} diff --git a/src/DependencyInjection/ContainerFactory.php b/src/DependencyInjection/ContainerFactory.php index ed82b6d98b..ad64ef281f 100644 --- a/src/DependencyInjection/ContainerFactory.php +++ b/src/DependencyInjection/ContainerFactory.php @@ -4,100 +4,23 @@ namespace Rector\Behastan\DependencyInjection; -use Illuminate\Container\Container; -use PhpParser\Parser; -use PhpParser\ParserFactory; -use Rector\Behastan\Analyzer\ContextDefinitionsAnalyzer; -use Rector\Behastan\Command\AnalyzeCommand; -use Rector\Behastan\Command\DuplicatedDefinitionsCommand; -use Rector\Behastan\Command\UnusedDefinitionsCommand; +use Entropy\Container\Container; use Rector\Behastan\Contract\RuleInterface; -use Rector\Behastan\Rule\DuplicatedContextDefinitionContentsRule; -use Rector\Behastan\Rule\DuplicatedMaskRule; -use Rector\Behastan\Rule\UnusedContextDefinitionsRule; -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\Console\Style\SymfonyStyle; -use Webmozart\Assert\Assert; +use Rector\Behastan\RulesRegistry; final class ContainerFactory { - /** - * @api used in bin and tests - */ - public function create(): Container + public static function create(): Container { $container = new Container(); - // console - $container->singleton(Application::class, function (Container $container): Application { - $application = new Application('Behastan', '0.4'); + $container->autodiscover(__DIR__ . '/../Rule'); + $container->autodiscover(__DIR__ . '/../Command'); - // register commands - foreach ([ - DuplicatedDefinitionsCommand::class, - UnusedDefinitionsCommand::class, - AnalyzeCommand::class, - ] as $commandClass) { - $command = $container->make($commandClass); - $application->add($command); - } - - // remove basic command to make output clear - $this->hideDefaultCommands($application); - - return $application; + $container->service(RulesRegistry::class, function (Container $container) { + return new RulesRegistry($container->findByContract(RuleInterface::class)); }); - // parser - $container->singleton(Parser::class, static function (): Parser { - $phpParserFactory = new ParserFactory(); - return $phpParserFactory->createForHostVersion(); - }); - - // to re-use - $container->singleton(ContextDefinitionsAnalyzer::class); - - // silence in PHPUnit tests to keep output clear - $consoleOutput = new ConsoleOutput(); - $consoleOutput->setVerbosity( - defined('PHPUNIT_COMPOSER_INSTALL') ? ConsoleOutput::VERBOSITY_QUIET : ConsoleOutput::VERBOSITY_NORMAL - ); - - $container->singleton( - SymfonyStyle::class, - static fn (): SymfonyStyle => new SymfonyStyle(new ArrayInput([]), $consoleOutput) - ); - - $this->registerRule($container, DuplicatedMaskRule::class); - $this->registerRule($container, DuplicatedContextDefinitionContentsRule::class); - $this->registerRule($container, UnusedContextDefinitionsRule::class); - - $container->when(AnalyzeCommand::class) - ->needs('$rules') - ->giveTagged(RuleInterface::class); - return $container; } - - public function hideDefaultCommands(Application $application): void - { - $application->get('list') - ->setHidden(); - - $application->get('completion') - ->setHidden(); - - $application->get('help') - ->setHidden(); - } - - private function registerRule(Container $container, string $ruleClass): void - { - Assert::isAOf($ruleClass, RuleInterface::class); - - $container->singleton($ruleClass); - $container->tag($ruleClass, RuleInterface::class); - } } diff --git a/src/Enum/Option.php b/src/Enum/Option.php deleted file mode 100644 index 6492127cf7..0000000000 --- a/src/Enum/Option.php +++ /dev/null @@ -1,13 +0,0 @@ -phpParser->parse($fileContents); Assert::isArray($stmts); diff --git a/src/Reporting/MaskCollectionStatsPrinter.php b/src/Reporting/MaskCollectionStatsPrinter.php index 2e0a2e6138..49b3ecad29 100644 --- a/src/Reporting/MaskCollectionStatsPrinter.php +++ b/src/Reporting/MaskCollectionStatsPrinter.php @@ -4,26 +4,26 @@ namespace Rector\Behastan\Reporting; +use Entropy\Console\Output\OutputPrinter; use Rector\Behastan\ValueObject\Mask\ExactMask; use Rector\Behastan\ValueObject\Mask\NamedMask; use Rector\Behastan\ValueObject\Mask\RegexMask; use Rector\Behastan\ValueObject\Mask\SkippedMask; use Rector\Behastan\ValueObject\MaskCollection; -use Symfony\Component\Console\Style\SymfonyStyle; final readonly class MaskCollectionStatsPrinter { public function __construct( - private SymfonyStyle $symfonyStyle + private OutputPrinter $outputPrinter ) { } public function print(MaskCollection $maskCollection): void { - $this->symfonyStyle->writeln(sprintf('Found %d masks:', $maskCollection->count())); - $this->symfonyStyle->writeln(sprintf(' * %d exact', $maskCollection->countByType(ExactMask::class))); - $this->symfonyStyle->writeln(sprintf(' * %d /regex/', $maskCollection->countByType(RegexMask::class))); - $this->symfonyStyle->writeln(sprintf(' * %d :named', $maskCollection->countByType(NamedMask::class))); + $this->outputPrinter->writeln(sprintf('Found %d masks:', $maskCollection->count())); + $this->outputPrinter->writeln(sprintf(' * %d exact', $maskCollection->countByType(ExactMask::class))); + $this->outputPrinter->writeln(sprintf(' * %d /regex/', $maskCollection->countByType(RegexMask::class))); + $this->outputPrinter->writeln(sprintf(' * %d :named', $maskCollection->countByType(NamedMask::class))); $this->printSkippedMasks($maskCollection); } @@ -42,7 +42,7 @@ private function printSkippedMasks(MaskCollection $maskCollection): void $skippedMasksString = implode('", "', $skippedMasksValues); - $this->symfonyStyle->writeln(sprintf( + $this->outputPrinter->writeln(sprintf( ' * %d skipped ("%s")', $maskCollection->countByType(SkippedMask::class), $skippedMasksString diff --git a/src/RulesRegistry.php b/src/RulesRegistry.php new file mode 100644 index 0000000000..96e1577e3e --- /dev/null +++ b/src/RulesRegistry.php @@ -0,0 +1,30 @@ +rules; + } +} diff --git a/tests/AbstractTestCase.php b/tests/AbstractTestCase.php index 4fe0e5ceec..948eadf02b 100644 --- a/tests/AbstractTestCase.php +++ b/tests/AbstractTestCase.php @@ -4,7 +4,7 @@ namespace Rector\Behastan\Tests; -use Illuminate\Container\Container; +use Entropy\Container\Container; use PHPUnit\Framework\TestCase; use Rector\Behastan\DependencyInjection\ContainerFactory;