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.json version of twig/twig, doctrine/*, phpunit/phpunit and symfony/*
  • 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.

See How to create a custom composer-based set.