Skip to content

[Sets] Move every per-version rule into the composer-based set - #758

Merged
TomasVotruba merged 2 commits into
mainfrom
migrate-version-sets-to-composer-based
Aug 5, 2026
Merged

[Sets] Move every per-version rule into the composer-based set#758
TomasVotruba merged 2 commits into
mainfrom
migrate-version-sets-to-composer-based

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

Follow-up to #757, which removed the three already-covered sets. This moves the remaining eight — PHPUnit 4.0 through 11.0 — into composer-based.php, so no ComposerTriggeredSet is left in this package.

Each rule now declares the PHPUnit version its target API is available from:

final class GetMockRector extends AbstractRector implements ComposerPackageConstraintInterface
{
    /**
     * createMock() was added in PHPUnit 5.4
     */
    public function provideComposerPackageConstraint(): ComposerPackageConstraint
    {
        return new ComposerPackageConstraint('phpunit/phpunit', '>=5.4');
    }

That is what makes one set enough. A project upgrading from PHPUnit 6 straight to 12 previously needed every version set in between; now a single set applies exactly the rules its installed version supports.

Version bonds

Grounded in the API that the rule produces:

rule bond why
GetMockRector, GetMockBuilderGetMockToCreateMockRector >=5.4 createMock()
ExceptionAnnotationRector, DelegateExceptionArgumentsRector >=5.2 expectException(), expectExceptionMessage()
SpecificAssertInternalTypeRector, SpecificAssertContainsRector, AssertEqualsParameterToSpecificMethodsTypeRector >=7.5 assertIsArray(), assertStringContainsString(), assertEqualsWithDelta()
AssertRegExpRector >=9.1 assertMatchesRegularExpression()
ExplicitPhpErrorApiRector >=8.4 <10.0 the expectDeprecation() family, removed in 10.0
TestListenerToHooksRector >=9.0 <10.0 the hook interfaces, removed in 10.0

The remaining rules keep the version of the set they came from, marked as such in their docblock (inherited from the PHPUnit 10.0 set). That never applies a rule below where its set already claimed it worked, and the bond can be tightened to an exact minor later.

Upper bounds and their tests

ExplicitPhpErrorApiRector and TestListenerToHooksRector target APIs PHPUnit 10 removed. Left open-ended they would rewrite code on PHPUnit 12 into methods that no longer exist, so both carry <10.0 — which then disables them under the PHPUnit 13 installed here. Their tests read the version from a stub instead:

protected function provideComposerJsonFilePath(): string
{
    return __DIR__ . '/config/composer.json';
}

Two things deliberately left out

PropertyExistsWithoutAssertRector is not registered in composer-based. It matches assertObjectHasProperty() — the exact method the >=10.1 rename already in this set produces — so the two rewrite each other's output. The conflict is not new (both sat in the phpunit100 set together), but I did not want to encode its outcome as expected test output. Worth a separate look; the rule keeps its bond and its set file registration.

Duplicate configuration was dropped rather than moved. The phpunit90 and phpunit100 renames of assertRegExp(), assertObjectHasAttribute(), setMethods() and the file/directory asserts are already in composer-based against PHPUnit\Framework\Assert, which TestCase extends. Same for phpunit80's MockObject class rename, already covered by the >=6.0 map.

Fixtures

Two composer-based fixtures gained an assertTrue(true). AddDoesNotPerformAssertionToNonAssertingTestRector is now part of the set and was adding #[DoesNotPerformAssertions] to their empty test methods, which had nothing to do with what those fixtures test.

Green locally: 652 tests, PHPStan, ECS and Rector clean.

… composer-based

Every rule these three sets registered is already in composer-based.php, bound to the exact phpunit/phpunit version it needs:

- 12.0: RemoveOverrideFinalConstructTestCaseRector (>=12.0.3), plus the mock-to-stub set and composer-based set it imports
- 12.5: the three AllowMockObjects rules (>=12.5.2)
- 13.0: the expectExceptionMessage() to expectExceptionMessageIsOrContains() rename (>=13.2)

The bond is also more accurate than the set trigger was: a 13.0 or 13.1 project no longer gets the rename of a method that only exists from 13.2, and the same for 12.0 versus 12.0.3 and 12.5 versus 12.5.2.

The set files stay reachable through the PHPUnitSetList constants.
Each rule now declares the phpunit/phpunit version its target API is available from, so it can be registered once in composer-based.php instead of in a set that only fires on one major version. A project upgrading from PHPUnit 6 to 12 gets all of them in a single run.

21 rules gained a ComposerPackageConstraintInterface bond and the configured rules moved over with ruleWithConfigurationComposerVersionBound. With nothing left to trigger, PHPUnitSetProvider registers the composer-based set as a plain set, and no ComposerTriggeredSet remains.

Two rules target APIs that PHPUnit 10 removed, so they carry an upper bound and their tests read the version from a stub composer.json.

The per-version set files stay reachable through the PHPUnitSetList constants.
@TomasVotruba
TomasVotruba merged commit 057a0ff into main Aug 5, 2026
7 checks passed
@TomasVotruba
TomasVotruba deleted the migrate-version-sets-to-composer-based branch August 5, 2026 11:48
TomasVotruba added a commit to rectorphp/rector-src 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.

1 participant