Skip to content

Commit

Permalink
[config] Add version-based set loading, use composer semver
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Nov 13, 2024
1 parent 129f93c commit b87e654
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 44 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"webmozart/assert": "^1.11"
},
"require-dev": {
"dg/bypass-finals": "^1.8",
"php-parallel-lint/php-parallel-lint": "^1.4",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan-deprecation-rules": "^1.2",
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,7 @@ parameters:
-
message: '#Fetching (deprecated )?class constant (.*?) of (deprecated )?class (Rector\\Set\\ValueObject\\DowngradeLevelSetList|Rector\\Symfony\\Set\\(.*?))#'
path: src/Configuration/RectorConfigBuilder.php

# false positive on mock
-
message: '#Return type of call to method PHPUnit\\Framework\\TestCase\:\:createMock\(\) contains unresolvable type#'
7 changes: 0 additions & 7 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Rector\CodingStyle\Rector\String_\UseClassKeywordForClassNameResolutionRector;
use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\ConstFetch\RemovePhpVersionIdCheckRector;
use Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;

return RectorConfig::configure()
Expand All @@ -20,8 +19,6 @@
earlyReturn: true,
naming: true,
rectorPreset: true,
// @experimental 2024-06
// twig: true,
phpunitCodeQuality: true
)
->withPhpSets()
Expand All @@ -41,9 +38,7 @@
StringClassNameToClassConstantRector::class,
__DIR__ . '/bin/validate-phpstan-version.php',
// tests
'*/Fixture/*',
'*/Fixture*',
'*/Source/*',
'*/Source*',
'*/Expected/*',

Expand All @@ -55,6 +50,4 @@
__DIR__ . '/src/Configuration/RectorConfigBuilder.php',
__DIR__ . '/src/Console/Notifier.php',
],

RemoveUnusedPrivatePropertyRector::class => [__DIR__ . '/src/Configuration/RectorConfigBuilder.php'],
]);
14 changes: 14 additions & 0 deletions src/Bridge/SetProviderCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Rector\Set\Contract\SetProviderInterface;
use Rector\Set\SetProvider\CoreSetProvider;
use Rector\Set\SetProvider\PHPSetProvider;
use Rector\Set\ValueObject\ComposerTriggeredSet;
use Rector\Symfony\Set\SetProvider\SymfonySetProvider;
use Rector\Symfony\Set\SetProvider\TwigSetProvider;

Expand Down Expand Up @@ -64,4 +65,17 @@ public function provideSets(): array

return $sets;
}

/**
* @return array<ComposerTriggeredSet>
*/
public function provideComposerTriggeredSets(): array
{
return array_filter(
$this->provideSets(),
function (SetInterface $set): bool {
return $set instanceof ComposerTriggeredSet;
}
);
}
}
22 changes: 15 additions & 7 deletions src/Composer/InstalledPackageResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,25 @@ public function resolve(string $projectDirectory): array
$installedPackageFileContents = FileSystem::read($installedPackagesFilePath);
$installedPackagesFilePath = Json::decode($installedPackageFileContents, true);

$installedPackages = $this->createInstalledPackages($installedPackagesFilePath['packages']);

$this->resolvedInstalledPackages[$projectDirectory] = $installedPackages;

return $installedPackages;
}

/**
* @param mixed[] $packages
* @return InstalledPackage[]
*/
private function createInstalledPackages(array $packages): array
{
$installedPackages = [];

foreach ($installedPackagesFilePath['packages'] as $installedPackage) {
$installedPackages[] = new InstalledPackage(
$installedPackage['name'],
$installedPackage['version_normalized']
);
foreach ($packages as $package) {
$installedPackages[] = new InstalledPackage($package['name'], $package['version_normalized']);
}

$this->resolvedInstalledPackages[$projectDirectory] = $installedPackages;

return $installedPackages;
}
}
30 changes: 22 additions & 8 deletions src/Configuration/RectorConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Rector\Bridge\SetProviderCollector;
use Rector\Bridge\SetRectorsResolver;
use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface;
use Rector\Composer\InstalledPackageResolver;
use Rector\Config\Level\CodeQualityLevel;
use Rector\Config\Level\DeadCodeLevel;
use Rector\Config\Level\TypeDeclarationLevel;
Expand Down Expand Up @@ -168,7 +169,7 @@ public function __invoke(RectorConfig $rectorConfig): void
// @experimental 2024-06
if ($this->setGroups !== []) {
$setProviderCollector = $rectorConfig->make(SetProviderCollector::class);
$setManager = new SetManager($setProviderCollector);
$setManager = new SetManager($setProviderCollector, new InstalledPackageResolver());

$this->groupLoadedSets = $setManager->matchBySetGroups($this->setGroups);
}
Expand Down Expand Up @@ -704,8 +705,6 @@ public function withPreparedSets(
bool $doctrineCodeQuality = false,
bool $symfonyCodeQuality = false,
bool $symfonyConfigs = false,
// composer based
bool $twig = false,
bool $phpunit = false,
): self {
Notifier::notifyNotSuitableMethodForPHP74(__METHOD__);
Expand Down Expand Up @@ -770,18 +769,33 @@ public function withPreparedSets(
$this->sets[] = SymfonySetList::CONFIGS;
}

// @experimental 2024-06
if ($twig) {
$this->setGroups[] = SetGroup::TWIG;
}

if ($phpunit) {
$this->setGroups[] = SetGroup::PHPUNIT;
}

return $this;
}

public function withComposerBased(bool $twig = false, bool $doctrine = false, bool $phpunit = false): self
{
$setMap = [
$twig => SetGroup::TWIG,
$doctrine => SetGroup::DOCTRINE,
$phpunit => SetGroup::PHPUNIT,
];

dump($setMap);
die;

foreach ($setMap as $isEnabled => $setPath) {
if ($isEnabled) {
$this->setGroups[] = $setPath;
}
}

return $this;
}

/**
* @param array<class-string<RectorInterface>> $rules
*/
Expand Down
4 changes: 4 additions & 0 deletions src/Set/Enum/SetGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,25 @@ final class SetGroup
public const PHP = 'php';

/**
* Version-based set provider
* @var string
*/
public const TWIG = 'twig';

/**
* Version-based set provider
* @var string
*/
public const PHPUNIT = 'phpunit';

/**
* Version-based set provider
* @var string
*/
public const DOCTRINE = 'doctrine';

/**
* Version-based set provider
* @var string
*/
public const SYMFONY = 'symfony';
Expand Down
19 changes: 6 additions & 13 deletions src/Set/SetManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
final readonly class SetManager
{
public function __construct(
private SetProviderCollector $setProviderCollector
private SetProviderCollector $setProviderCollector,
private InstalledPackageResolver $installedPackageResolver,
) {
}

Expand All @@ -25,13 +26,9 @@ public function matchComposerTriggered(string $groupName): array
{
$matchedSets = [];

foreach ($this->setProviderCollector->provideSets() as $set) {
if (! $set instanceof ComposerTriggeredSet) {
continue;
}

if ($set->getGroupName() === $groupName) {
$matchedSets[] = $set;
foreach ($this->setProviderCollector->provideComposerTriggeredSets() as $composerTriggeredSet) {
if ($composerTriggeredSet->getGroupName() === $groupName) {
$matchedSets[] = $composerTriggeredSet;
}
}

Expand All @@ -44,8 +41,7 @@ public function matchComposerTriggered(string $groupName): array
*/
public function matchBySetGroups(array $setGroups): array
{
$installedPackageResolver = new InstalledPackageResolver();
$installedComposerPackages = $installedPackageResolver->resolve(getcwd());
$installedComposerPackages = $this->installedPackageResolver->resolve(getcwd());

$groupLoadedSets = [];

Expand All @@ -54,9 +50,6 @@ public function matchBySetGroups(array $setGroups): array

foreach ($composerTriggeredSets as $composerTriggeredSet) {
if ($composerTriggeredSet->matchInstalledPackages($installedComposerPackages)) {
// @todo add debug note somewhere
// echo sprintf('Loaded "%s" set as it meets the conditions', $composerTriggeredSet->getSetFilePath());

// it matched composer package + version requirements → load set
$groupLoadedSets[] = $composerTriggeredSet->getSetFilePath();
}
Expand Down
3 changes: 2 additions & 1 deletion src/Set/ValueObject/ComposerTriggeredSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Rector\Set\ValueObject;

use Composer\Semver\Semver;
use Rector\Composer\ValueObject\InstalledPackage;
use Rector\Set\Contract\SetInterface;
use Webmozart\Assert\Assert;
Expand Down Expand Up @@ -49,7 +50,7 @@ public function matchInstalledPackages(array $installedPackages): bool
continue;
}

return version_compare($installedPackage->getVersion(), $this->version) !== -1;
return Semver::satisfies($this->version, '^' . $installedPackage->getVersion());
}

return false;
Expand Down
38 changes: 30 additions & 8 deletions tests/Set/SetManager/SetManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,43 @@

namespace Rector\Tests\Set\SetManager;

use PHPUnit\Framework\TestCase;
use DG\BypassFinals;
use Rector\Bridge\SetProviderCollector;
use Rector\Composer\InstalledPackageResolver;
use Rector\Composer\ValueObject\InstalledPackage;
use Rector\Set\Enum\SetGroup;
use Rector\Set\SetManager;
use Rector\Tests\Set\SetManager\Source\SomeSetProvider;
use Rector\Testing\PHPUnit\AbstractLazyTestCase;

final class SetManagerTest extends TestCase
final class SetManagerTest extends AbstractLazyTestCase
{
public function test(): void
private SetManager $setManager;

protected function setUp(): void
{
$setProviderCollector = new SetProviderCollector([new SomeSetProvider()]);
parent::setUp();

BypassFinals::enable();

$setManager = new SetManager($setProviderCollector);
$setProviderCollector = new SetProviderCollector();

// fake that Twig 2.0 is installed, as data are fetched from currently installed packages
$installedPackageResolverMock = $this->createMock(InstalledPackageResolver::class);
$installedPackageResolverMock->method('resolve')
->willReturn([new InstalledPackage('twig/twig', '2.0.0')]);

$this->setManager = new SetManager($setProviderCollector, $installedPackageResolverMock);
}

$twigComposerTriggeredSet = $setManager->matchComposerTriggered(SetGroup::TWIG);
$this->assertGreaterThan(6, count($twigComposerTriggeredSet));
public function test(): void
{
$twigComposerTriggeredSet = $this->setManager->matchComposerTriggered(SetGroup::TWIG);
$this->assertCount(6, $twigComposerTriggeredSet);
}

public function testByVersion(): void
{
$composerTriggeredSets = $this->setManager->matchBySetGroups([SetGroup::TWIG]);
$this->assertCount(2, $composerTriggeredSets);
}
}

0 comments on commit b87e654

Please sign in to comment.