From 1b700191fcd2f9719e40e5271ca9e7715d45492c Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 5 Aug 2026 13:54:40 +0200 Subject: [PATCH 1/2] [Sets] Deprecate ComposerTriggeredSet in favor of ComposerPackageConstraintInterface 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. --- phpstan.neon | 9 +++++++++ src/Set/ValueObject/ComposerTriggeredSet.php | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index 24654e6061d..abc7566acd6 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -317,6 +317,15 @@ parameters: message: '#Fetching (deprecated )?class constant (.*?) of (deprecated )?class (Rector\\Set\\ValueObject\\DowngradeLevelSetList|Rector\\Symfony\\Set\\(.*?))#' path: src/Configuration/RectorConfigBuilder.php + # the deprecated ComposerTriggeredSet is still resolved internally, until every extension bonds its rules + - + message: '#deprecated class Rector\\Set\\ValueObject\\ComposerTriggeredSet#' + paths: + - src/Set/SetManager.php + - src/Bridge/SetProviderCollector.php + - tests/Set/ValueObject/ComposerTriggeredSetTest.php + - tests/Set/SetManager/SetManagerTest.php + # runtime comparison - '#Comparison operation ".*" between int<\d+, \d+> and \d+ is always true#' diff --git a/src/Set/ValueObject/ComposerTriggeredSet.php b/src/Set/ValueObject/ComposerTriggeredSet.php index eac453f6de5..1bac17ff54d 100644 --- a/src/Set/ValueObject/ComposerTriggeredSet.php +++ b/src/Set/ValueObject/ComposerTriggeredSet.php @@ -12,6 +12,13 @@ /** * @api used by extensions + * + * @deprecated Bond the rules themselves instead, by implementing the ComposerPackageConstraintInterface. A set + * triggered on a single major version has to be repeated for every version an upgrade passes through, while a bonded + * rule states the exact package version its target API is available from and applies from there upwards. + * + * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * * @see \Rector\Tests\Set\ValueObject\ComposerTriggeredSetTest */ final readonly class ComposerTriggeredSet implements SetInterface From 70f00ddd785994813e781262afe452bd1a4a4ca9 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 5 Aug 2026 13:57:36 +0200 Subject: [PATCH 2/2] [Sets] Deprecate the set object machinery around SetInterface SetInterface, Set, SetProviderInterface, SetManager, SetProviderCollector and the two core providers exist to describe a set as an object, so it can be matched against the installed packages. A rule bonded with ComposerPackageConstraintInterface needs none of that: it states the package version its target API is available from, and a plain set file registers it. Every note links to this PR, so the reasoning is one click away. Internal resolving keeps working and is ignored in phpstan.neon, as the sets are still served from here. --- phpstan.neon | 7 +++++-- src/Bridge/SetProviderCollector.php | 7 +++++++ src/Set/Contract/SetInterface.php | 8 ++++++++ src/Set/Contract/SetProviderInterface.php | 8 ++++++++ src/Set/SetManager.php | 7 +++++++ src/Set/SetProvider/CoreSetProvider.php | 8 ++++++++ src/Set/SetProvider/PHPSetProvider.php | 8 ++++++++ src/Set/ValueObject/ComposerTriggeredSet.php | 1 + src/Set/ValueObject/Set.php | 7 +++++++ 9 files changed, 59 insertions(+), 2 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index abc7566acd6..04115d03e40 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -317,12 +317,15 @@ parameters: message: '#Fetching (deprecated )?class constant (.*?) of (deprecated )?class (Rector\\Set\\ValueObject\\DowngradeLevelSetList|Rector\\Symfony\\Set\\(.*?))#' path: src/Configuration/RectorConfigBuilder.php - # the deprecated ComposerTriggeredSet is still resolved internally, until every extension bonds its rules + # the deprecated set objects are still resolved internally, until every extension bonds its rules - - message: '#deprecated class Rector\\Set\\ValueObject\\ComposerTriggeredSet#' + message: '#deprecated (class|interface) Rector\\(Set|Bridge)\\#' paths: - src/Set/SetManager.php + - src/Set/SetProvider/CoreSetProvider.php + - src/Set/SetProvider/PHPSetProvider.php - src/Bridge/SetProviderCollector.php + - src/Configuration/RectorConfigBuilder.php - tests/Set/ValueObject/ComposerTriggeredSetTest.php - tests/Set/SetManager/SetManagerTest.php diff --git a/src/Bridge/SetProviderCollector.php b/src/Bridge/SetProviderCollector.php index 2697ec553c4..c83f8309482 100644 --- a/src/Bridge/SetProviderCollector.php +++ b/src/Bridge/SetProviderCollector.php @@ -16,6 +16,13 @@ * @api * * Utils class to ease building bridges by 3rd-party tools + * + * @deprecated Bond the rules themselves instead, by implementing the ComposerPackageConstraintInterface. A set + * described as an object only existed to be matched against the installed packages; a bonded rule states the exact + * package version its target API is available from and applies from there upwards, so a plain set file is enough. + * + * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * @see https://github.com/rectorphp/rector-src/pull/8296 */ final readonly class SetProviderCollector { diff --git a/src/Set/Contract/SetInterface.php b/src/Set/Contract/SetInterface.php index 3f3fb3d0455..f32334a6e36 100644 --- a/src/Set/Contract/SetInterface.php +++ b/src/Set/Contract/SetInterface.php @@ -4,6 +4,14 @@ namespace Rector\Set\Contract; +/** + * @deprecated Bond the rules themselves instead, by implementing the ComposerPackageConstraintInterface. A set + * described as an object only existed to be matched against the installed packages; a bonded rule states the exact + * package version its target API is available from and applies from there upwards, so a plain set file is enough. + * + * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * @see https://github.com/rectorphp/rector-src/pull/8296 + */ interface SetInterface { public function getGroupName(): string; diff --git a/src/Set/Contract/SetProviderInterface.php b/src/Set/Contract/SetProviderInterface.php index 00a37dcc9b1..23bc119ca41 100644 --- a/src/Set/Contract/SetProviderInterface.php +++ b/src/Set/Contract/SetProviderInterface.php @@ -4,6 +4,14 @@ namespace Rector\Set\Contract; +/** + * @deprecated Bond the rules themselves instead, by implementing the ComposerPackageConstraintInterface. A set + * described as an object only existed to be matched against the installed packages; a bonded rule states the exact + * package version its target API is available from and applies from there upwards, so a plain set file is enough. + * + * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * @see https://github.com/rectorphp/rector-src/pull/8296 + */ interface SetProviderInterface { /** diff --git a/src/Set/SetManager.php b/src/Set/SetManager.php index da1960d1e1e..22d6f04fdea 100644 --- a/src/Set/SetManager.php +++ b/src/Set/SetManager.php @@ -11,6 +11,13 @@ /** * @see \Rector\Tests\Set\SetManager\SetManagerTest + * + * @deprecated Bond the rules themselves instead, by implementing the ComposerPackageConstraintInterface. A set + * described as an object only existed to be matched against the installed packages; a bonded rule states the exact + * package version its target API is available from and applies from there upwards, so a plain set file is enough. + * + * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * @see https://github.com/rectorphp/rector-src/pull/8296 */ final readonly class SetManager { diff --git a/src/Set/SetProvider/CoreSetProvider.php b/src/Set/SetProvider/CoreSetProvider.php index 1ce42614582..04b80d582f8 100644 --- a/src/Set/SetProvider/CoreSetProvider.php +++ b/src/Set/SetProvider/CoreSetProvider.php @@ -9,6 +9,14 @@ use Rector\Set\Enum\SetGroup; use Rector\Set\ValueObject\Set; +/** + * @deprecated Bond the rules themselves instead, by implementing the ComposerPackageConstraintInterface. A set + * described as an object only existed to be matched against the installed packages; a bonded rule states the exact + * package version its target API is available from and applies from there upwards, so a plain set file is enough. + * + * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * @see https://github.com/rectorphp/rector-src/pull/8296 + */ final class CoreSetProvider implements SetProviderInterface { /** diff --git a/src/Set/SetProvider/PHPSetProvider.php b/src/Set/SetProvider/PHPSetProvider.php index 513d94855a7..a74fdfa628f 100644 --- a/src/Set/SetProvider/PHPSetProvider.php +++ b/src/Set/SetProvider/PHPSetProvider.php @@ -9,6 +9,14 @@ use Rector\Set\Enum\SetGroup; use Rector\Set\ValueObject\Set; +/** + * @deprecated Bond the rules themselves instead, by implementing the ComposerPackageConstraintInterface. A set + * described as an object only existed to be matched against the installed packages; a bonded rule states the exact + * package version its target API is available from and applies from there upwards, so a plain set file is enough. + * + * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * @see https://github.com/rectorphp/rector-src/pull/8296 + */ final class PHPSetProvider implements SetProviderInterface { /** diff --git a/src/Set/ValueObject/ComposerTriggeredSet.php b/src/Set/ValueObject/ComposerTriggeredSet.php index 1bac17ff54d..1193322c5c7 100644 --- a/src/Set/ValueObject/ComposerTriggeredSet.php +++ b/src/Set/ValueObject/ComposerTriggeredSet.php @@ -18,6 +18,7 @@ * rule states the exact package version its target API is available from and applies from there upwards. * * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * @see https://github.com/rectorphp/rector-src/pull/8296 * * @see \Rector\Tests\Set\ValueObject\ComposerTriggeredSetTest */ diff --git a/src/Set/ValueObject/Set.php b/src/Set/ValueObject/Set.php index 5e3d05bf307..75b5834b1ef 100644 --- a/src/Set/ValueObject/Set.php +++ b/src/Set/ValueObject/Set.php @@ -9,6 +9,13 @@ /** * @api used by extensions + * + * @deprecated Bond the rules themselves instead, by implementing the ComposerPackageConstraintInterface. A set + * described as an object only existed to be matched against the installed packages; a bonded rule states the exact + * package version its target API is available from and applies from there upwards, so a plain set file is enough. + * + * @see \Rector\VersionBonding\Contract\ComposerPackageConstraintInterface + * @see https://github.com/rectorphp/rector-src/pull/8296 */ final readonly class Set implements SetInterface {