Composer Based Sets
Projects like Symfony, Doctrine, Twig or Laravel have lots of versions. Instead of adding dozens of sets for each of those, you can make use of composer-based set resolution:
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withComposerBased(twig: true, doctrine: true, phpunit: true, symfony: true);
- Rector should look into installed
composer.jsonversion oftwig/twig,doctrine/*,phpunit/phpunitandsymfony/* - then it picks all sets that are relevant to your specific installed versions
- and run those
If you upgrade to Doctrine 4, Twig 4, or Symfony 10 later, Rector will pick up sets for you.
Want to take it step by step? Use named arguments to limit the run to specific groups. These are all the available keys - twig, doctrine, phpunit, symfony, laravel and drupal:
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withComposerBased(
twig: true,
doctrine: true,
phpunit: true,
symfony: true,
laravel: true,
drupal: true,
);
How it works
Rector reads the installed version from vendor/composer/installed.json, and falls back to the require and require-dev sections of your composer.json. There are 3 levels where the version decides what runs:
1. Set level - a whole set file is loaded only if a package version is installed.
2. Rule level - a single rule declares the package version it needs. The rule is skipped everywhere else.
3. Rule configuration level - a rule is registered with a configuration valid only since (or until) a specific package version. The very same rule can be registered multiple times with different configuration for different version ranges.
PHPUnit and Symfony sets already use levels 2 and 3, so a single set file covers every supported version of those packages. Other packages are being migrated to this approach.
Show what runs: the composer-based command
Since Rector 2.6, you can see exactly which composer-bound rules are loaded and which of them are active on your project:
vendor/bin/rector composer-based
Composer package bound rules
============================
------------------------------------------------- ----------------- ---------- ----------- --------
Rule Package Requires Installed Active
------------------------------------------------- ----------------- ---------- ----------- --------
AnnotationWithValueToAttributeRector phpunit/phpunit >=10.0 13.2.6.0 yes
BareCreateMockAssignToDirectUseRector phpunit/phpunit >=11.0 13.2.6.0 yes
CreateStubOverCreateMockArgRector phpunit/phpunit >=11.0 13.2.6.0 yes
RemoveOverrideFinalConstructTestCaseRector phpunit/phpunit >=12.0.3 13.2.6.0 yes
------------------------------------------------- ----------------- ---------- ----------- --------
The rules registered with a version-bound configuration are listed in a second table, with the configuration printed below each row:
Composer package bound rule configuration
=========================================
-------------------------------------- ----------------- -------------- ----------- --------
Rule Package Requires Installed Active
-------------------------------------- ----------------- -------------- ----------- --------
AnnotationToAttributeRector phpunit/phpunit >=10.0 <13.0 13.2.6.0 no
AnnotationToAttribute(runClassInSeparateProcess,
PHPUnit\Framework\Attributes\RunClassInSeparateProcess, [], false)
-------------------------------------- ----------------- -------------- ----------- --------
RenameMethodRector phpunit/phpunit >=8.3 13.2.6.0 yes
MethodCallRename(PHPUnit\Framework\MockObject\MockBuilder, setMethods, onlyMethods)
-------------------------------------- ----------------- -------------- ----------- --------
! [NOTE] 32 of 33 composer package bound items are active
Inactive items are listed too, so you can see why a rule does not change your code. In the example above, runClassInSeparateProcess is not converted to an attribute, because that attribute was removed in PHPUnit 13 and the project runs PHPUnit 13.2.
Run only composer-based rules
Upgraded a package and want to see just what that upgrade brings? Narrow the run to composer-bound rules only:
vendor/bin/rector process --composer-based
It keeps only the rules that declare a composer package constraint themselves, or that were registered with a version-bound configuration. Everything else - your levels, prepared sets and custom rules - is skipped for that run, the same way --only narrows it to a single rule.
Handy right after a composer update:
composer update phpunit/phpunit
vendor/bin/rector process --composer-based --dry-run
Create your own composer-based set
Maintain a package with many versions? You can bind your own rules and their configuration to package versions, so a single set file covers every version - and your users' rector.php stays a one-liner even a few majors later.