rector/rector is held at >=2.6.4 <2.6.5 in the root composer.json because Rector 2.6.5 removed both routes that rector.php can use to load the Drupal deprecation sets. Lift the constraint once upstream restores one of them.
Why the hold exists
rector.php follows the configuration documented in Use Rector on your Drupal site, easier than ever, with new composer-based sets:
->withSetProviders(DrupalSetProvider::class)
->withComposerBased(twig: TRUE, phpunit: TRUE, symfony: TRUE, drupal: TRUE)
Rector 2.6.5 breaks this in two independent ways.
1. withSetProviders() is disabled
rectorphp/rector#8392 turned the method into a no-op that emits a deprecation:
Deprecated: The withSetProviders() method is deprecated and no longer applied.
Set providers are now loaded internally - use "withComposerBased()" instead.
Rector\Bridge\SetProviderCollector is now dead code - nothing in the Rector source calls it. DrupalSetProvider is therefore never consulted, and no Drupal sets are registered.
This fails silently. Rector exits 0 and reports [OK] Rector is done!, so CI stays green while no Drupal rule runs at all.
2. withComposerBased(drupal: TRUE) cannot resolve DrupalSetList
The replacement route needs DrupalRector\Set\DrupalSetList::COMPOSER_BASED. Two problems:
The constant is unreleased. It does not exist in palantirnet/drupal-rector 1.1.2 or on main. It is added by palantirnet/drupal-rector#419, which is open and whose description states "do not merge yet".
Rector 2.6.5 mis-scopes the class name. In src/Configuration/RectorConfigBuilder.php, the Drupal import is prefixed by php-scoper while the equivalent Laravel import is not:
use RectorPrefix202608\DrupalRector\Set\DrupalSetList; // line 7
use RectorLaravel\Set\LaravelSetList; // line 35
The guard on line 492 tests the unprefixed name and passes, then the static reference on line 494 resolves to the prefixed one:
if ($drupal && class_exists('DrupalRector\Set\DrupalSetList') && constant('DrupalRector\Set\DrupalSetList::COMPOSER_BASED')) {
$this->sets[] = DrupalSetList::COMPOSER_BASED;
}
Rector 2.6.4 read the same constant from a string (EXTENSION_COMPOSER_BASED_SET_LISTS), which php-scoper leaves untouched, so it was unaffected.
Consequence: merging #419 alone does not fix this. Installing that branch against Rector 2.6.5 turns the silent no-op into a hard failure:
[ERROR] Class "RectorPrefix202608\DrupalRector\Set\DrupalSetList" not found
Verification
A Drupal 11.4 site was built from the template and a probe file containing a Drupal 11.2 deprecation (drupal_requirements_severity()) was run through each combination:
rector/rector |
palantirnet/drupal-rector |
withComposerBased(drupal:) alone |
Provider + withComposerBased() |
| 2.6.4 |
1.1.2 |
no DrupalSetList |
rewrites the deprecation |
| 2.6.4 |
#419 branch |
rewrites the deprecation |
rewrites the deprecation |
| 2.6.5 |
1.1.2 |
silently no rules |
silently no rules |
| 2.6.5 |
#419 branch |
fatal |
fatal |
Exit criteria
Lift the constraint back to ^2.6.x when both hold:
Then drop ->withSetProviders(DrupalSetProvider::class) from rector.php and keep ->withComposerBased(...) on its own, which is the form #419 documents. Remove the php-rector hold from renovate.json at the same time.
No upstream issue exists yet for the scoping bug - a search of rectorphp/rector returns nothing for DrupalSetList or withComposerBased drupal. It should be reported.
rector/rectoris held at>=2.6.4 <2.6.5in the rootcomposer.jsonbecause Rector 2.6.5 removed both routes thatrector.phpcan use to load the Drupal deprecation sets. Lift the constraint once upstream restores one of them.Why the hold exists
rector.phpfollows the configuration documented in Use Rector on your Drupal site, easier than ever, with new composer-based sets:Rector 2.6.5 breaks this in two independent ways.
1.
withSetProviders()is disabledrectorphp/rector#8392 turned the method into a no-op that emits a deprecation:
Rector\Bridge\SetProviderCollectoris now dead code - nothing in the Rector source calls it.DrupalSetProvideris therefore never consulted, and no Drupal sets are registered.This fails silently. Rector exits
0and reports[OK] Rector is done!, so CI stays green while no Drupal rule runs at all.2.
withComposerBased(drupal: TRUE)cannot resolveDrupalSetListThe replacement route needs
DrupalRector\Set\DrupalSetList::COMPOSER_BASED. Two problems:The constant is unreleased. It does not exist in
palantirnet/drupal-rector1.1.2 or onmain. It is added by palantirnet/drupal-rector#419, which is open and whose description states "do not merge yet".Rector 2.6.5 mis-scopes the class name. In
src/Configuration/RectorConfigBuilder.php, the Drupal import is prefixed by php-scoper while the equivalent Laravel import is not:The guard on line 492 tests the unprefixed name and passes, then the static reference on line 494 resolves to the prefixed one:
Rector 2.6.4 read the same constant from a string (
EXTENSION_COMPOSER_BASED_SET_LISTS), which php-scoper leaves untouched, so it was unaffected.Consequence: merging #419 alone does not fix this. Installing that branch against Rector 2.6.5 turns the silent no-op into a hard failure:
Verification
A Drupal 11.4 site was built from the template and a probe file containing a Drupal 11.2 deprecation (
drupal_requirements_severity()) was run through each combination:rector/rectorpalantirnet/drupal-rectorwithComposerBased(drupal:)alonewithComposerBased()DrupalSetList#419branch#419branchExit criteria
Lift the constraint back to
^2.6.xwhen both hold:palantirnet/drupal-rectortags a release containingDrupalSetList::COMPOSER_BASED(Port of 8.x changes to 7.x. #419 or its successor).DrupalRector\Set\DrupalSetListinRectorConfigBuilder, matching howRectorLaravel\Set\LaravelSetListis handled.Then drop
->withSetProviders(DrupalSetProvider::class)fromrector.phpand keep->withComposerBased(...)on its own, which is the form #419 documents. Remove thephp-rectorhold fromrenovate.jsonat the same time.No upstream issue exists yet for the scoping bug - a search of
rectorphp/rectorreturns nothing forDrupalSetListorwithComposerBased drupal. It should be reported.