Skip to content

feat: add ComposerPackageConstraintInterface - #7877

Merged
TomasVotruba merged 1 commit into
rectorphp:mainfrom
calebdw:calebdw/push-ykxnnrtlxmkk
Feb 10, 2026
Merged

feat: add ComposerPackageConstraintInterface#7877
TomasVotruba merged 1 commit into
rectorphp:mainfrom
calebdw:calebdw/push-ykxnnrtlxmkk

Conversation

@calebdw

@calebdw calebdw commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Hello!

This PR introduces a new ComposerPackageConstraintInterface that allows individual Rector rules to specify a composer package constraint. Rules implementing this interface will be automatically skipped if the required package is not installed or does not satisfy the specified constraint.

Motivation

This is analogous to MinPhpVersionInterface (which skips rules based on PHP version) but for composer packages. This is particularly useful for framework-specific rules (e.g., Laravel, Symfony) where certain rules only make sense for specific package versions.

Comparison with ComposerTriggeredSet

Rector already has ComposerTriggeredSet for composer-based filtering, but it serves a different purpose:

ComposerTriggeredSet ComposerPackageConstraintInterface
Operates on Sets (collections of rules) Individual rules
Purpose Auto-load upgrade sets based on installed version Skip rules if package constraint not satisfied
Version matching Caret (^) - targets specific major version Any semver constraint (>=, <, ^, ~, ranges, etc.)
Use case "Load Laravel 10 upgrade set if laravel/framework ^10 is installed" "Skip this rule if laravel/framework doesn't match constraint"
Cumulative No: each set targets a specific version range Depends on constraint used

ComposerTriggeredSet is designed for upgrade paths where you want different sets for different version jumps (9→10, 10→11, etc.). ComposerPackageConstraintInterface is for rules that should only run when a package satisfies a specific constraint (for example, apply to a version and all subsequent version).

Usage

<?php
use Rector\VersionBonding\Contract\ComposerPackageConstraintInterface;
use Rector\VersionBonding\ValueObject\ComposerPackageConstraint;

final class SomeLaravelRector extends AbstractRector implements ComposerPackageConstraintInterface
{
    public function provideComposerPackageConstraint(): ComposerPackageConstraint
    {
        // Min version
        return new ComposerPackageConstraint('laravel/framework', '>=10.0.0');
        
        // Or max version
        return new ComposerPackageConstraint('laravel/framework', '<10.0.0');
        
        // Or specific major
        return new ComposerPackageConstraint('laravel/framework', '^9.0');
        
        // Or range
        return new ComposerPackageConstraint('laravel/framework', '>=9.0 <11.0');
    }
}

Thanks!

This adds the ability to filter out rectors by composer package constraint.
@calebdw
calebdw force-pushed the calebdw/push-ykxnnrtlxmkk branch from 6bc2c5b to ed459b4 Compare February 7, 2026 22:47
@TomasVotruba

Copy link
Copy Markdown
Member

This looks like much lightweight than the cumbersome sets provider approach. Love it 🥰

Let's give it a go and test it. Thanks

@TomasVotruba

Copy link
Copy Markdown
Member

I just noticed we have this feature in Rector... such an underrated power.

I'll have to tell my agents about it to flip all the Symfony/PHPUnit/Rector sets to this and just let Rector load the right rules automatically :)

@calebdw

calebdw commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Yes! In my opinion, this kind of makes the composer sets obsolete. I've been meaning to update rector-laravel but haven't had time yet

@TomasVotruba

Copy link
Copy Markdown
Member

Yeah, I wanted to make this happen, but was affraid of huge cognitive load to solve these.
Now I'll give it a go :) building on top giants - you've done epic work here 👏

My 1st trial to verify this works end-to-end is to upgrade to PHPUnit 11 and let Rector 1-shot the upgrade with 0 deprecations repoted by PHPUnit. (Watch live force pushes here on @mautic mautic/mautic#16891)

Single rule are easy to do now, but I've struggled with configured ones - e.g. some PHP 8 attributes were available on PHPUnit 10, some on PHPUnit 11 etc. So I made new config method to handle package + custom rules + version: #8244

TomasVotruba added a commit that referenced this pull request Aug 5, 2026
…traintInterface

A set triggered on a single major version has to be repeated for every version an upgrade passes through. A rule bonded with ComposerPackageConstraintInterface, added in #7877, states the exact package version its target API is available from and applies from there upwards, so one set covers every upgrade path.

rectorphp/rector-symfony#1010 and rectorphp/rector-phpunit#758 moved both extensions over, leaving Twig as the only remaining user. The internal resolving of the class is kept working and ignored in phpstan.neon until that lands too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants