diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index 8569aef678f..97411cb1c03 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -62,6 +62,17 @@ final class RectorConfigBuilder 'withCodingStyleLevel' => [SetList::CODING_STYLE, 'coding style'], ]; + /** + * The composer-based set of the extensions that rector-src does not require, so their set list class cannot be + * imported here. Resolved at run-time; an extension that ships no such set falls back to its set group. + * + * @var array + */ + private const array EXTENSION_COMPOSER_BASED_SET_LISTS = [ + SetGroup::LARAVEL => 'RectorLaravel\\Set\\LaravelSetList::COMPOSER_BASED', + SetGroup::DRUPAL => 'DrupalRector\\Set\\DrupalSetList::COMPOSER_BASED', + ]; + /** * @var string[] */ @@ -741,10 +752,24 @@ public function withComposerBased( SetGroup::DRUPAL => $drupal, ]; - foreach ($setMap as $setPath => $isEnabled) { - if ($isEnabled) { - $this->setGroups[] = $setPath; + foreach ($setMap as $setGroup => $isEnabled) { + if (! $isEnabled) { + continue; + } + + $setListConstant = self::EXTENSION_COMPOSER_BASED_SET_LISTS[$setGroup]; + if (defined($setListConstant)) { + $setFilePath = constant($setListConstant); + Assert::string($setFilePath); + + // single set, as every rule inside is bound to the installed package version on its own + $this->sets[] = $setFilePath; + continue; } + + // @deprecated fallback for extensions that still describe their sets as objects, + // instead of bonding the rules themselves + $this->setGroups[] = $setGroup; } if ($phpunit) { diff --git a/tests/Configuration/ExtensionComposerBasedSetTest.php b/tests/Configuration/ExtensionComposerBasedSetTest.php new file mode 100644 index 00000000000..de5873a81cc --- /dev/null +++ b/tests/Configuration/ExtensionComposerBasedSetTest.php @@ -0,0 +1,73 @@ +provideExtensionComposerBasedSetLists(); + + self::assertArrayHasKey(SetGroup::LARAVEL, $extensionSetLists); + self::assertArrayHasKey(SetGroup::DRUPAL, $extensionSetLists); + + foreach ($extensionSetLists as $setListConstant) { + self::assertMatchesRegularExpression('#^\w+(\\\\\w+)+::\w+$#', $setListConstant); + } + } + + /** + * The extension packages are not required by rector-src, so their constant is undefined here and the + * deprecated set group has to keep working. + */ + public function testFallsBackToTheSetGroupWhenTheExtensionIsNotInstalled(): void + { + foreach ($this->provideExtensionComposerBasedSetLists() as $setListConstant) { + self::assertFalse(defined($setListConstant), $setListConstant); + } + + $rectorConfigBuilder = new RectorConfigBuilder() + ->withComposerBased(laravel: true, drupal: true); + + self::assertSame([SetGroup::LARAVEL, SetGroup::DRUPAL], $this->readPrivateArray($rectorConfigBuilder, 'setGroups')); + self::assertSame([], $this->readPrivateArray($rectorConfigBuilder, 'sets')); + } + + /** + * @return array + */ + private function provideExtensionComposerBasedSetLists(): array + { + $extensionSetLists = new ReflectionClass(RectorConfigBuilder::class) + ->getConstant('EXTENSION_COMPOSER_BASED_SET_LISTS'); + + self::assertIsArray($extensionSetLists); + + return $extensionSetLists; + } + + /** + * @return mixed[] + */ + private function readPrivateArray(RectorConfigBuilder $rectorConfigBuilder, string $propertyName): array + { + $value = new ReflectionClass($rectorConfigBuilder) + ->getProperty($propertyName) + ->getValue($rectorConfigBuilder); + + self::assertIsArray($value); + + return $value; + } +}