From 31e1ec8b59f1b8a286fda1d9ab031c3648a85a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Tam=C3=A1s=20Szab=C3=B3?= Date: Wed, 21 Oct 2026 09:34:16 +0200 Subject: [PATCH 1/3] [Configuration] Auto-detect installed frameworks in withComposerBased() called with no argument --- src/Configuration/RectorConfigBuilder.php | 39 ++++++++++ .../ComposerBasedAutoDetectTest.php | 72 +++++++++++++++++++ .../doctrine_project/composer.json | 8 +++ .../vendor/composer/installed.json | 19 +++++ 4 files changed, 138 insertions(+) create mode 100644 tests/Configuration/ComposerBasedAutoDetectTest.php create mode 100644 tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/composer.json create mode 100644 tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/vendor/composer/installed.json diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index 622a72fc529..fc3592e922e 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -10,6 +10,7 @@ use PhpParser\NodeVisitor; use Rector\Bridge\SetRectorsResolver; use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface; +use Rector\Composer\InstalledPackageResolver; use Rector\Config\Level\CodeQualityLevel; use Rector\Config\Level\CodingStyleLevel; use Rector\Config\Level\DeadCodeLevel; @@ -63,6 +64,21 @@ final class RectorConfigBuilder 'withCodingStyleLevel' => [SetList::CODING_STYLE, 'coding style'], ]; + /** + * The package that marks a framework as used by the project. Its own package, not one of its components: + * Laravel requires symfony/console, symfony/process etc., which must not switch the Symfony set on. + * + * @var array withComposerBased() argument name => package name + */ + private const array COMPOSER_BASED_FRAMEWORK_PACKAGES = [ + 'twig' => 'twig/twig', + 'doctrine' => 'doctrine/orm', + 'phpunit' => 'phpunit/phpunit', + 'symfony' => 'symfony/framework-bundle', + 'laravel' => 'laravel/framework', + 'drupal' => 'drupal/core', + ]; + /** * @var string[] */ @@ -704,6 +720,10 @@ public function withPreparedSets( return $this; } + /** + * Call it with no argument to load the sets of every framework installed in the project. + * Any argument switches the detection off, and only the passed sets are loaded. + */ public function withComposerBased( bool $twig = false, bool $doctrine = false, @@ -713,6 +733,10 @@ public function withComposerBased( bool $laravel = false, bool $drupal = false, ): self { + if (func_num_args() === 0) { + return $this->withComposerBased(...$this->resolveInstalledComposerBasedFrameworks()); + } + if ($laravel && class_exists('RectorLaravel\Set\LaravelSetList') && constant('RectorLaravel\Set\LaravelSetList::COMPOSER_BASED')) { $this->sets[] = LaravelSetList::COMPOSER_BASED; } @@ -1219,4 +1243,19 @@ private function addLevelRules( return $this; } + + /** + * @return array withComposerBased() argument name => is the framework installed + */ + private function resolveInstalledComposerBasedFrameworks(): array + { + $installedPackageResolver = new InstalledPackageResolver(); + + $installedFrameworks = []; + foreach (self::COMPOSER_BASED_FRAMEWORK_PACKAGES as $argumentName => $packageName) { + $installedFrameworks[$argumentName] = $installedPackageResolver->resolvePackageVersion($packageName) !== null; + } + + return $installedFrameworks; + } } diff --git a/tests/Configuration/ComposerBasedAutoDetectTest.php b/tests/Configuration/ComposerBasedAutoDetectTest.php new file mode 100644 index 00000000000..d8a32d989d4 --- /dev/null +++ b/tests/Configuration/ComposerBasedAutoDetectTest.php @@ -0,0 +1,72 @@ +originalWorkingDirectory = (string) getcwd(); + chdir(__DIR__ . '/Fixture/ComposerBasedAutoDetect/doctrine_project'); + } + + protected function tearDown(): void + { + chdir($this->originalWorkingDirectory); + } + + /** + * symfony/console is installed as well, but only symfony/framework-bundle marks a Symfony project + */ + public function testNoArgumentLoadsSetsOfInstalledFrameworks(): void + { + $rectorConfigBuilder = new RectorConfigBuilder() + ->withComposerBased(); + + $this->assertSame( + [PHPUnitSetList::COMPOSER_BASED, DoctrineSetList::COMPOSER_BASED], + $this->readSets($rectorConfigBuilder) + ); + } + + public function testExplicitArgumentSkipsDetection(): void + { + $rectorConfigBuilder = new RectorConfigBuilder() + ->withComposerBased(twig: true); + + $this->assertSame([TwigSetList::COMPOSER_BASED], $this->readSets($rectorConfigBuilder)); + } + + public function testExplicitFalseArgumentSkipsDetection(): void + { + $rectorConfigBuilder = new RectorConfigBuilder() + ->withComposerBased(phpunit: false); + + $this->assertSame([], $this->readSets($rectorConfigBuilder)); + } + + /** + * @return string[] + */ + private function readSets(RectorConfigBuilder $rectorConfigBuilder): array + { + $sets = new ReflectionClass($rectorConfigBuilder) + ->getProperty('sets') + ->getValue($rectorConfigBuilder); + + $this->assertIsArray($sets); + + return $sets; + } +} diff --git a/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/composer.json b/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/composer.json new file mode 100644 index 00000000000..f4d0b431ab3 --- /dev/null +++ b/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/composer.json @@ -0,0 +1,8 @@ +{ + "type": "project", + "require": { + "doctrine/orm": "^3.0", + "phpunit/phpunit": "^12.0", + "symfony/console": "^7.0" + } +} diff --git a/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/vendor/composer/installed.json b/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/vendor/composer/installed.json new file mode 100644 index 00000000000..b60b1c245ba --- /dev/null +++ b/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/vendor/composer/installed.json @@ -0,0 +1,19 @@ +{ + "packages": [ + { + "name": "doctrine/orm", + "version": "3.3.0", + "version_normalized": "3.3.0.0" + }, + { + "name": "phpunit/phpunit", + "version": "12.1.0", + "version_normalized": "12.1.0.0" + }, + { + "name": "symfony/console", + "version": "v7.2.0", + "version_normalized": "7.2.0.0" + } + ] +} From 995bbfe960dd34aca765a20168664f92751f6848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Tam=C3=A1s=20Szab=C3=B3?= Date: Wed, 21 Oct 2026 10:32:20 +0200 Subject: [PATCH 2/3] Trigger composer-based sets by any of several packages, use package wording --- src/Configuration/RectorConfigBuilder.php | 42 +++++++++++-------- .../ComposerBasedAutoDetectTest.php | 24 +++++++++-- .../composer.json | 2 +- .../vendor/composer/installed.json | 6 +-- .../no_trigger_packages/composer.json | 6 +++ .../vendor/composer/installed.json | 9 ++++ 6 files changed, 63 insertions(+), 26 deletions(-) rename tests/Configuration/Fixture/ComposerBasedAutoDetect/{doctrine_project => installed_packages}/composer.json (78%) rename tests/Configuration/Fixture/ComposerBasedAutoDetect/{doctrine_project => installed_packages}/vendor/composer/installed.json (74%) create mode 100644 tests/Configuration/Fixture/ComposerBasedAutoDetect/no_trigger_packages/composer.json create mode 100644 tests/Configuration/Fixture/ComposerBasedAutoDetect/no_trigger_packages/vendor/composer/installed.json diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index fc3592e922e..b8d47defca4 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -65,18 +65,17 @@ final class RectorConfigBuilder ]; /** - * The package that marks a framework as used by the project. Its own package, not one of its components: - * Laravel requires symfony/console, symfony/process etc., which must not switch the Symfony set on. + * Any of these installed packages loads the composer-based set; each rule inside is bound to its own package * - * @var array withComposerBased() argument name => package name + * @var array withComposerBased() argument name => package names */ - private const array COMPOSER_BASED_FRAMEWORK_PACKAGES = [ - 'twig' => 'twig/twig', - 'doctrine' => 'doctrine/orm', - 'phpunit' => 'phpunit/phpunit', - 'symfony' => 'symfony/framework-bundle', - 'laravel' => 'laravel/framework', - 'drupal' => 'drupal/core', + private const array COMPOSER_BASED_TRIGGER_PACKAGES = [ + 'twig' => ['twig/twig'], + 'doctrine' => ['doctrine/orm', 'doctrine/dbal', 'doctrine/doctrine-bundle'], + 'phpunit' => ['phpunit/phpunit'], + 'symfony' => ['symfony/framework-bundle', 'symfony/http-kernel', 'symfony/console'], + 'laravel' => ['laravel/framework', 'illuminate/support'], + 'drupal' => ['drupal/core'], ]; /** @@ -721,7 +720,7 @@ public function withPreparedSets( } /** - * Call it with no argument to load the sets of every framework installed in the project. + * Call it with no argument to load the sets of every package installed in the project. * Any argument switches the detection off, and only the passed sets are loaded. */ public function withComposerBased( @@ -734,7 +733,7 @@ public function withComposerBased( bool $drupal = false, ): self { if (func_num_args() === 0) { - return $this->withComposerBased(...$this->resolveInstalledComposerBasedFrameworks()); + return $this->withComposerBased(...$this->resolveInstalledComposerBasedPackages()); } if ($laravel && class_exists('RectorLaravel\Set\LaravelSetList') && constant('RectorLaravel\Set\LaravelSetList::COMPOSER_BASED')) { @@ -1245,17 +1244,24 @@ private function addLevelRules( } /** - * @return array withComposerBased() argument name => is the framework installed + * @return array withComposerBased() argument name => is any of its packages installed */ - private function resolveInstalledComposerBasedFrameworks(): array + private function resolveInstalledComposerBasedPackages(): array { $installedPackageResolver = new InstalledPackageResolver(); - $installedFrameworks = []; - foreach (self::COMPOSER_BASED_FRAMEWORK_PACKAGES as $argumentName => $packageName) { - $installedFrameworks[$argumentName] = $installedPackageResolver->resolvePackageVersion($packageName) !== null; + $installedPackages = []; + foreach (self::COMPOSER_BASED_TRIGGER_PACKAGES as $argumentName => $packageNames) { + $installedPackages[$argumentName] = false; + + foreach ($packageNames as $packageName) { + if ($installedPackageResolver->resolvePackageVersion($packageName) !== null) { + $installedPackages[$argumentName] = true; + break; + } + } } - return $installedFrameworks; + return $installedPackages; } } diff --git a/tests/Configuration/ComposerBasedAutoDetectTest.php b/tests/Configuration/ComposerBasedAutoDetectTest.php index d8a32d989d4..5ad6e6e7efb 100644 --- a/tests/Configuration/ComposerBasedAutoDetectTest.php +++ b/tests/Configuration/ComposerBasedAutoDetectTest.php @@ -8,6 +8,7 @@ use Rector\Configuration\RectorConfigBuilder; use Rector\Doctrine\Set\DoctrineSetList; use Rector\PHPUnit\Set\PHPUnitSetList; +use Rector\Symfony\Set\SymfonySetList; use Rector\Symfony\Set\TwigSetList; use ReflectionClass; @@ -18,7 +19,7 @@ final class ComposerBasedAutoDetectTest extends TestCase protected function setUp(): void { $this->originalWorkingDirectory = (string) getcwd(); - chdir(__DIR__ . '/Fixture/ComposerBasedAutoDetect/doctrine_project'); + $this->changeProjectDirectory('installed_packages'); } protected function tearDown(): void @@ -27,19 +28,29 @@ protected function tearDown(): void } /** - * symfony/console is installed as well, but only symfony/framework-bundle marks a Symfony project + * doctrine/dbal and symfony/console are enough to load their sets, no doctrine/orm or symfony/framework-bundle needed */ - public function testNoArgumentLoadsSetsOfInstalledFrameworks(): void + public function testNoArgumentLoadsSetsOfInstalledPackages(): void { $rectorConfigBuilder = new RectorConfigBuilder() ->withComposerBased(); $this->assertSame( - [PHPUnitSetList::COMPOSER_BASED, DoctrineSetList::COMPOSER_BASED], + [PHPUnitSetList::COMPOSER_BASED, DoctrineSetList::COMPOSER_BASED, SymfonySetList::COMPOSER_BASED], $this->readSets($rectorConfigBuilder) ); } + public function testNoArgumentWithoutTriggerPackagesLoadsNothing(): void + { + $this->changeProjectDirectory('no_trigger_packages'); + + $rectorConfigBuilder = new RectorConfigBuilder() + ->withComposerBased(); + + $this->assertSame([], $this->readSets($rectorConfigBuilder)); + } + public function testExplicitArgumentSkipsDetection(): void { $rectorConfigBuilder = new RectorConfigBuilder() @@ -56,6 +67,11 @@ public function testExplicitFalseArgumentSkipsDetection(): void $this->assertSame([], $this->readSets($rectorConfigBuilder)); } + private function changeProjectDirectory(string $projectName): void + { + chdir(__DIR__ . '/Fixture/ComposerBasedAutoDetect/' . $projectName); + } + /** * @return string[] */ diff --git a/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/composer.json b/tests/Configuration/Fixture/ComposerBasedAutoDetect/installed_packages/composer.json similarity index 78% rename from tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/composer.json rename to tests/Configuration/Fixture/ComposerBasedAutoDetect/installed_packages/composer.json index f4d0b431ab3..4d26cee8ce3 100644 --- a/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/composer.json +++ b/tests/Configuration/Fixture/ComposerBasedAutoDetect/installed_packages/composer.json @@ -1,7 +1,7 @@ { "type": "project", "require": { - "doctrine/orm": "^3.0", + "doctrine/dbal": "^4.0", "phpunit/phpunit": "^12.0", "symfony/console": "^7.0" } diff --git a/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/vendor/composer/installed.json b/tests/Configuration/Fixture/ComposerBasedAutoDetect/installed_packages/vendor/composer/installed.json similarity index 74% rename from tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/vendor/composer/installed.json rename to tests/Configuration/Fixture/ComposerBasedAutoDetect/installed_packages/vendor/composer/installed.json index b60b1c245ba..2f377da8d21 100644 --- a/tests/Configuration/Fixture/ComposerBasedAutoDetect/doctrine_project/vendor/composer/installed.json +++ b/tests/Configuration/Fixture/ComposerBasedAutoDetect/installed_packages/vendor/composer/installed.json @@ -1,9 +1,9 @@ { "packages": [ { - "name": "doctrine/orm", - "version": "3.3.0", - "version_normalized": "3.3.0.0" + "name": "doctrine/dbal", + "version": "4.2.0", + "version_normalized": "4.2.0.0" }, { "name": "phpunit/phpunit", diff --git a/tests/Configuration/Fixture/ComposerBasedAutoDetect/no_trigger_packages/composer.json b/tests/Configuration/Fixture/ComposerBasedAutoDetect/no_trigger_packages/composer.json new file mode 100644 index 00000000000..408b1c1d95e --- /dev/null +++ b/tests/Configuration/Fixture/ComposerBasedAutoDetect/no_trigger_packages/composer.json @@ -0,0 +1,6 @@ +{ + "type": "project", + "require": { + "webmozart/assert": "^1.11" + } +} diff --git a/tests/Configuration/Fixture/ComposerBasedAutoDetect/no_trigger_packages/vendor/composer/installed.json b/tests/Configuration/Fixture/ComposerBasedAutoDetect/no_trigger_packages/vendor/composer/installed.json new file mode 100644 index 00000000000..8d81601582a --- /dev/null +++ b/tests/Configuration/Fixture/ComposerBasedAutoDetect/no_trigger_packages/vendor/composer/installed.json @@ -0,0 +1,9 @@ +{ + "packages": [ + { + "name": "webmozart/assert", + "version": "1.11.0", + "version_normalized": "1.11.0.0" + } + ] +} From aa524febb82cdbaf4200ee06f921cd5e01cde098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Tam=C3=A1s=20Szab=C3=B3?= Date: Wed, 21 Oct 2026 11:23:36 +0200 Subject: [PATCH 3/3] Generate composer-based trigger packages from the packages the sets upgrade --- ...nerate-composer-based-trigger-packages.php | 132 ++++++++++++++++++ .../ComposerBasedTriggerPackages.php | 102 ++++++++++++++ src/Configuration/RectorConfigBuilder.php | 16 +-- .../ComposerBasedAutoDetectTest.php | 16 +++ 4 files changed, 251 insertions(+), 15 deletions(-) create mode 100644 scripts/generate-composer-based-trigger-packages.php create mode 100644 src/Configuration/ComposerBasedTriggerPackages.php diff --git a/scripts/generate-composer-based-trigger-packages.php b/scripts/generate-composer-based-trigger-packages.php new file mode 100644 index 00000000000..986a9f59c25 --- /dev/null +++ b/scripts/generate-composer-based-trigger-packages.php @@ -0,0 +1,132 @@ + TwigSetList::COMPOSER_BASED, + 'doctrine' => DoctrineSetList::COMPOSER_BASED, + 'phpunit' => PHPUnitSetList::COMPOSER_BASED, + 'symfony' => SymfonySetList::COMPOSER_BASED, +]; + +// rector-laravel and drupal-rector are not required here, so their sets cannot be read +$extensionTriggerPackages = [ + 'laravel' => ['laravel/framework', 'illuminate/support'], + 'drupal' => ['drupal/core'], +]; + +$triggerPackages = []; + +foreach ($composerBasedSetFiles as $argumentName => $composerBasedSetFile) { + // the parameter provider is static, start each set from scratch + SimpleParameterProvider::setParameter(Option::COMPOSER_BOUND_RULE_CONFIGURATIONS, []); + + $rectorConfig = new RectorConfig(); + + /** @var callable $configCallable */ + $configCallable = require $composerBasedSetFile; + $configCallable($rectorConfig); + + $rectorClasses = array_merge( + $rectorConfig->getMainRectorClasses(), + array_keys($rectorConfig->getRuleConfigurations()) + ); + + $packageNames = []; + + foreach (array_unique($rectorClasses) as $rectorClass) { + // the package constraint does not depend on constructor dependencies, skip them + $rector = new ReflectionClass($rectorClass)->newInstanceWithoutConstructor(); + if (! $rector instanceof ComposerPackageConstraintInterface) { + continue; + } + + $composerPackageConstraint = $rector->provideComposerPackageConstraint(); + $composerPackageConstraints = is_array($composerPackageConstraint) + ? $composerPackageConstraint + : [$composerPackageConstraint]; + + foreach ($composerPackageConstraints as $composerPackageConstraint) { + $packageNames[] = $composerPackageConstraint->getPackageName(); + } + } + + // rule configurations registered via ruleWithConfigurationComposerVersionBound(), the inactive ones included + $composerBoundRuleConfigurations = SimpleParameterProvider::provideArrayParameter( + Option::COMPOSER_BOUND_RULE_CONFIGURATIONS + ); + + foreach ($composerBoundRuleConfigurations as $composerBoundRuleConfiguration) { + if ($composerBoundRuleConfiguration instanceof ComposerBoundRuleConfiguration) { + $packageNames[] = $composerBoundRuleConfiguration->getPackageName(); + } + } + + $packageNames = array_values(array_unique($packageNames)); + sort($packageNames); + + $triggerPackages[$argumentName] = $packageNames; +} + +$triggerPackages = array_merge($triggerPackages, $extensionTriggerPackages); + +$printedTriggerPackages = ''; +foreach ($triggerPackages as $argumentName => $packageNames) { + $printedTriggerPackages .= sprintf(" '%s' => [\n", $argumentName); + + foreach ($packageNames as $packageName) { + $printedTriggerPackages .= sprintf(" '%s',\n", $packageName); + } + + $printedTriggerPackages .= " ],\n"; +} + +$fileContents = << + */ + public const array PACKAGES_BY_ARGUMENT = [ +{$printedTriggerPackages} ]; +} + +PHP; + +$targetFilePath = __DIR__ . '/../src/Configuration/ComposerBasedTriggerPackages.php'; +file_put_contents($targetFilePath, $fileContents); + +$symfonyStyle = new SymfonyStyle(new ArrayInput([]), new ConsoleOutput()); + +foreach ($triggerPackages as $argumentName => $packageNames) { + $symfonyStyle->writeln(sprintf('%s: %d packages', $argumentName, count($packageNames))); +} + +$symfonyStyle->success(sprintf('File "%s" was generated', realpath($targetFilePath))); diff --git a/src/Configuration/ComposerBasedTriggerPackages.php b/src/Configuration/ComposerBasedTriggerPackages.php new file mode 100644 index 00000000000..ae1c4e57ff7 --- /dev/null +++ b/src/Configuration/ComposerBasedTriggerPackages.php @@ -0,0 +1,102 @@ + + */ + public const array PACKAGES_BY_ARGUMENT = [ + 'twig' => [ + 'twig/twig', + ], + 'doctrine' => [ + 'doctrine/collections', + 'doctrine/common', + 'doctrine/data-fixtures', + 'doctrine/dbal', + 'doctrine/doctrine-bundle', + 'doctrine/mongodb-odm', + 'doctrine/mongodb-odm-bundle', + 'doctrine/orm', + 'gedmo/doctrine-extensions', + ], + 'phpunit' => [ + 'phpunit/phpunit', + ], + 'symfony' => [ + 'jms/serializer', + 'symfony/bridge-swift-mailer', + 'symfony/browser-kit', + 'symfony/cache', + 'symfony/class-loader', + 'symfony/config', + 'symfony/console', + 'symfony/contracts', + 'symfony/debug', + 'symfony/dependency-injection', + 'symfony/doctrine-bridge', + 'symfony/dom-crawler', + 'symfony/error-handler', + 'symfony/event-dispatcher', + 'symfony/expression-language', + 'symfony/filesystem', + 'symfony/finder', + 'symfony/form', + 'symfony/framework-bundle', + 'symfony/http-client', + 'symfony/http-foundation', + 'symfony/http-kernel', + 'symfony/inflector', + 'symfony/intl', + 'symfony/json-streamer', + 'symfony/mail-pace-mailer', + 'symfony/mailer', + 'symfony/messenger', + 'symfony/mime', + 'symfony/monolog-bridge', + 'symfony/notifier', + 'symfony/options-resolver', + 'symfony/process', + 'symfony/property-access', + 'symfony/property-info', + 'symfony/routing', + 'symfony/scheduler', + 'symfony/security', + 'symfony/security-bundle', + 'symfony/security-core', + 'symfony/security-http', + 'symfony/security-mailer', + 'symfony/serializer', + 'symfony/symfony', + 'symfony/templating', + 'symfony/translation', + 'symfony/translation-contracts', + 'symfony/twig-bridge', + 'symfony/twig-bundle', + 'symfony/type-info', + 'symfony/uid', + 'symfony/validator', + 'symfony/var-dumper', + 'symfony/web-link', + 'symfony/workflow', + 'symfony/yaml', + 'twig/twig', + ], + 'laravel' => [ + 'laravel/framework', + 'illuminate/support', + ], + 'drupal' => [ + 'drupal/core', + ], + ]; +} diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index b8d47defca4..6de1e3de3ef 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -64,20 +64,6 @@ final class RectorConfigBuilder 'withCodingStyleLevel' => [SetList::CODING_STYLE, 'coding style'], ]; - /** - * Any of these installed packages loads the composer-based set; each rule inside is bound to its own package - * - * @var array withComposerBased() argument name => package names - */ - private const array COMPOSER_BASED_TRIGGER_PACKAGES = [ - 'twig' => ['twig/twig'], - 'doctrine' => ['doctrine/orm', 'doctrine/dbal', 'doctrine/doctrine-bundle'], - 'phpunit' => ['phpunit/phpunit'], - 'symfony' => ['symfony/framework-bundle', 'symfony/http-kernel', 'symfony/console'], - 'laravel' => ['laravel/framework', 'illuminate/support'], - 'drupal' => ['drupal/core'], - ]; - /** * @var string[] */ @@ -1251,7 +1237,7 @@ private function resolveInstalledComposerBasedPackages(): array $installedPackageResolver = new InstalledPackageResolver(); $installedPackages = []; - foreach (self::COMPOSER_BASED_TRIGGER_PACKAGES as $argumentName => $packageNames) { + foreach (ComposerBasedTriggerPackages::PACKAGES_BY_ARGUMENT as $argumentName => $packageNames) { $installedPackages[$argumentName] = false; foreach ($packageNames as $packageName) { diff --git a/tests/Configuration/ComposerBasedAutoDetectTest.php b/tests/Configuration/ComposerBasedAutoDetectTest.php index 5ad6e6e7efb..5b58a87e762 100644 --- a/tests/Configuration/ComposerBasedAutoDetectTest.php +++ b/tests/Configuration/ComposerBasedAutoDetectTest.php @@ -5,12 +5,15 @@ namespace Rector\Tests\Configuration; use PHPUnit\Framework\TestCase; +use Rector\Configuration\ComposerBasedTriggerPackages; use Rector\Configuration\RectorConfigBuilder; use Rector\Doctrine\Set\DoctrineSetList; use Rector\PHPUnit\Set\PHPUnitSetList; use Rector\Symfony\Set\SymfonySetList; use Rector\Symfony\Set\TwigSetList; use ReflectionClass; +use ReflectionMethod; +use ReflectionParameter; final class ComposerBasedAutoDetectTest extends TestCase { @@ -67,6 +70,19 @@ public function testExplicitFalseArgumentSkipsDetection(): void $this->assertSame([], $this->readSets($rectorConfigBuilder)); } + public function testEveryArgumentHasTriggerPackages(): void + { + $argumentNames = array_map( + static fn (ReflectionParameter $reflectionParameter): string => $reflectionParameter->getName(), + new ReflectionMethod(RectorConfigBuilder::class, 'withComposerBased')->getParameters() + ); + + // deprecated, no longer applied + $argumentNames = array_values(array_diff($argumentNames, ['netteUtils'])); + + $this->assertSame($argumentNames, array_keys(ComposerBasedTriggerPackages::PACKAGES_BY_ARGUMENT)); + } + private function changeProjectDirectory(string $projectName): void { chdir(__DIR__ . '/Fixture/ComposerBasedAutoDetect/' . $projectName);