Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions src/Config/RectorConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ final class RectorConfig extends Container
*/
private array $ruleConfigurations = [];

/**
* @var array<class-string<RectorInterface>, true>
*/
private array $registeredRectorClasses = [];

/**
* @var array<string, true>
*/
private array $registeredComposerBoundRuleConfigurations = [];

/**
* @var string[]
*/
Expand Down Expand Up @@ -195,9 +205,6 @@ public function ruleWithConfiguration(string $rectorClass, array $configuration)
$ruleConfiguration = $this->ruleConfigurations[$rectorClass];
$configurableRector->configure($ruleConfiguration);
});

// for cache invalidation in case of sets change
SimpleParameterProvider::addParameter(Option::REGISTERED_RECTOR_RULES, $rectorClass);
}

/**
Expand All @@ -217,16 +224,25 @@ public function ruleWithConfigurationComposerVersionBound(
$packageVersion = $this->resolveInstalledPackageVersion($packageName);
$isActive = $packageVersion !== null && Semver::satisfies($packageVersion, $versionConstraint);

// reported by the "composer-based" command, the inactive ones as well
SimpleParameterProvider::addParameter(Option::COMPOSER_BOUND_RULE_CONFIGURATIONS, [
new ComposerBoundRuleConfiguration(
$rectorClass,
$packageName,
$versionConstraint,
$configuration,
$isActive
),
]);
// the same rule configuration can be registered by multiple sets, report it only once
$configurationKey = $rectorClass . '|' . $packageName . '|' . $versionConstraint . '|' . serialize(
$configuration
);

if (! isset($this->registeredComposerBoundRuleConfigurations[$configurationKey])) {
$this->registeredComposerBoundRuleConfigurations[$configurationKey] = true;

// reported by the "composer-based" command, the inactive ones as well
SimpleParameterProvider::addParameter(Option::COMPOSER_BOUND_RULE_CONFIGURATIONS, [
new ComposerBoundRuleConfiguration(
$rectorClass,
$packageName,
$versionConstraint,
$configuration,
$isActive
),
]);
}

if (! $isActive) {
return;
Expand All @@ -244,10 +260,17 @@ public function rule(string $rectorClass): void
Assert::isAOf($rectorClass, RectorInterface::class);

$this->singleton($rectorClass);
$this->tag($rectorClass, RectorInterface::class);

// for cache invalidation in case of change
SimpleParameterProvider::addParameter(Option::REGISTERED_RECTOR_RULES, $rectorClass);
// the same rule can be registered by multiple sets, tag it only once,
// otherwise it is run twice on every node and listed twice in the reports
if (! isset($this->registeredRectorClasses[$rectorClass])) {
$this->registeredRectorClasses[$rectorClass] = true;

$this->tag($rectorClass, RectorInterface::class);

// for cache invalidation in case of change
SimpleParameterProvider::addParameter(Option::REGISTERED_RECTOR_RULES, $rectorClass);
}

if (is_a($rectorClass, RelatedConfigInterface::class, true)) {
$configFile = $rectorClass::getConfigFile();
Expand Down Expand Up @@ -442,6 +465,8 @@ public function indent(string $character, int $count): void
public function resetRuleConfigurations(): void
{
$this->ruleConfigurations = [];
$this->registeredRectorClasses = [];
$this->registeredComposerBoundRuleConfigurations = [];
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/Configuration/ConfigurationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ public function createFromInput(InputInterface $input): Configuration

$showRulesSummary = (bool) $input->getOption(Option::RULES_SUMMARY);

$isComposerBased = (bool) $input->getOption(Option::COMPOSER_BASED);

// "--composer-based" narrows the run the same way "--only" does
if ($isComposerBased) {
SimpleParameterProvider::setParameter(Option::IS_RUN_NARROWED, true);
}

return new Configuration(
$isDryRun,
$showProgressBar,
Expand All @@ -113,6 +120,7 @@ public function createFromInput(InputInterface $input): Configuration
$onlySuffix,
$levelOverflows,
$showRulesSummary,
$isComposerBased,
);
}

Expand Down
58 changes: 58 additions & 0 deletions src/Configuration/ConfigurationRuleFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

namespace Rector\Configuration;

use Rector\Configuration\Parameter\SimpleParameterProvider;
use Rector\Contract\Rector\RectorInterface;
use Rector\ValueObject\Configuration;
use Rector\VersionBonding\Contract\ComposerPackageConstraintInterface;
use Rector\VersionBonding\ValueObject\ComposerBoundRuleConfiguration;

/**
* Modify available rector rules based on the configuration options
Expand Down Expand Up @@ -34,6 +37,10 @@ public function filter(array $rectors): array
return $this->filterOnlyRule($rectors, $onlyRule);
}

if ($this->configuration->isComposerBased()) {
return $this->filterComposerBased($rectors);
}

return $rectors;
}

Expand All @@ -52,4 +59,55 @@ public function filterOnlyRule(array $rectors, string $onlyRule): array

return $activeRectors;
}

/**
* Keeps rules that declare a composer package constraint themselves, and rules whose configuration
* was registered with a composer package constraint.
*
* @param list<RectorInterface> $rectors
* @return list<RectorInterface>
*/
private function filterComposerBased(array $rectors): array
{
$composerBoundRectorClasses = $this->resolveComposerBoundRectorClasses();

$activeRectors = [];
foreach ($rectors as $rector) {
if ($rector instanceof ComposerPackageConstraintInterface) {
$activeRectors[] = $rector;
continue;
}

if (in_array($rector::class, $composerBoundRectorClasses, true)) {
$activeRectors[] = $rector;
}
}

return $activeRectors;
}

/**
* @return string[]
*/
private function resolveComposerBoundRectorClasses(): array
{
$composerBoundRuleConfigurations = SimpleParameterProvider::provideArrayParameter(
Option::COMPOSER_BOUND_RULE_CONFIGURATIONS
);

$rectorClasses = [];
foreach ($composerBoundRuleConfigurations as $composerBoundRuleConfiguration) {
if (! $composerBoundRuleConfiguration instanceof ComposerBoundRuleConfiguration) {
continue;
}

if (! $composerBoundRuleConfiguration->isActive()) {
continue;
}

$rectorClasses[] = $composerBoundRuleConfiguration->getRectorClass();
}

return $rectorClasses;
}
}
5 changes: 5 additions & 0 deletions src/Configuration/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,11 @@ final class Option
*/
public const string COMPOSER_BOUND_RULE_CONFIGURATIONS = 'composer_bound_rule_configurations';

/**
* Run only rules bound to an installed composer package version
*/
public const string COMPOSER_BASED = 'composer-based';

/**
* @internal To filter files by specific suffix
*/
Expand Down
7 changes: 7 additions & 0 deletions src/Console/ProcessConfigureDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ public static function decorate(Command $command): void
// filter by rule and path
$command->addOption(Option::ONLY, null, InputOption::VALUE_REQUIRED, 'Fully qualified rule class name');

$command->addOption(
Option::COMPOSER_BASED,
null,
InputOption::VALUE_NONE,
'Run only rules bound to an installed composer package version'
);

$command->addOption(
Option::ONLY_SUFFIX,
null,
Expand Down
4 changes: 4 additions & 0 deletions src/Parallel/Command/WorkerCommandLineFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ public function create(
}
}

if ((bool) $input->getOption(Option::COMPOSER_BASED)) {
$workerCommandArray[] = self::OPTION_DASHES . Option::COMPOSER_BASED;
}

if ($input->getOption(Option::ONLY) !== null) {
$workerCommandArray[] = self::OPTION_DASHES . Option::ONLY;
$workerCommandArray[] = escapeshellarg((string) $input->getOption(Option::ONLY));
Expand Down
6 changes: 6 additions & 0 deletions src/ValueObject/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ public function __construct(
private ?string $onlySuffix = null,
private array $levelOverflows = [],
private bool $showRulesSummary = false,
private bool $isComposerBased = false,
) {
}

public function isComposerBased(): bool
{
return $this->isComposerBased;
}

public function isDryRun(): bool
{
return $this->isDryRun;
Expand Down
Loading