Skip to content

Bind annotation to attribute conversion to installed PHPUnit version - #745

Merged
TomasVotruba merged 5 commits into
mainfrom
annotations-to-attributes-version-bound
Jul 31, 2026
Merged

Bind annotation to attribute conversion to installed PHPUnit version#745
TomasVotruba merged 5 commits into
mainfrom
annotations-to-attributes-version-bound

Conversation

@TomasVotruba

@TomasVotruba TomasVotruba commented Jul 31, 2026

Copy link
Copy Markdown
Member

Ref rectorphp/rector-src#7877


Binds the annotation to attribute conversion to the PHPUnit version installed in the analysed project, so nothing is converted to an attribute that version does not have.

annotations-to-attributes.php stays untouched.

use Rector\PHPUnit\Set\PHPUnitSetList;

$rectorConfig->sets([PHPUnitSetList::COMPOSER_BASED]);

Rules bound by composer constraint

The 7 annotation to attribute rules of this package now declare the PHPUnit version their attributes were added in:

-final class TicketAnnotationToAttributeRector extends AbstractRector implements MinPhpVersionInterface
+final class TicketAnnotationToAttributeRector extends AbstractRector implements MinPhpVersionInterface, ComposerPackageConstraintInterface
 {
+    public function provideComposerPackageConstraint(): ComposerPackageConstraint
+    {
+        return new ComposerPackageConstraint('phpunit/phpunit', '>=10.0');
+    }
  • TicketAnnotationToAttributeRector - Ticket
  • TestWithAnnotationToAttributeRector - TestWith, TestWithJson
  • DataProviderAnnotationToAttributeRector - DataProvider, DataProviderExternal
  • CoversAnnotationWithValueToAttributeRector - CoversClass, CoversFunction
  • RequiresAnnotationWithValueToAttributeRector - Requires*
  • DependsAnnotationWithValueToAttributeRector - Depends*
  • AnnotationWithValueToAttributeRector - BackupGlobals, BackupStaticProperties, PreserveGlobalState, Group, UsesClass, TestDox

All of these attributes shipped in the 10.0.0 tag of src/Framework/Attributes, so all get >=10.0. They are registered in the new set as well.

Config bound by composer constraint

Uses ruleWithConfigurationComposerVersionBound() from rectorphp/rector-src#8244 for the pairs that go through AnnotationToAttributeRector, which lives in rector-src and cannot declare the constraint itself.

Went through every annotation Metadata\Parser\AnnotationParser handled in PHPUnit 10.5 and compared it to what this package converts. Three were missing:

+        new AnnotationWithValueToAttribute(
+            'backupStaticProperties',
+            'PHPUnit\Framework\Attributes\BackupStaticProperties',
+            ['enabled' => true, 'disabled' => false]
+        ),
+        new AnnotationWithValueToAttribute(
+            'excludeGlobalVariableFromBackup',
+            'PHPUnit\Framework\Attributes\ExcludeGlobalVariableFromBackup'
+        ),
-    /**
-     * @backupStaticProperties enabled
-     */
+    #[\PHPUnit\Framework\Attributes\BackupStaticProperties(true)]
     public function testSomething()

-    /**
-     * @excludeGlobalVariableFromBackup someGlobalVariable
-     */
+    #[\PHPUnit\Framework\Attributes\ExcludeGlobalVariableFromBackup('someGlobalVariable')]
     public function testSomethingElse()

@backupStaticProperties is the PHPUnit 10 spelling of @backupStaticAttributes, which was already handled.

A pair with an upper bound

@runClassInSeparateProcess maps to RunClassInSeparateProcess, which exists in PHPUnit 10.0 through 12.x and was removed in 13.0. Verified against the 10.0.0, 11.5.0, 12.0.0 and 13.0.0 tags:

// the RunClassInSeparateProcess attribute was added in PHPUnit 10.0 and removed in PHPUnit 13.0
$rectorConfig->ruleWithConfigurationComposerVersionBound(AnnotationToAttributeRector::class, [
    new AnnotationToAttribute(
        'runClassInSeparateProcess',
        'PHPUnit\Framework\Attributes\RunClassInSeparateProcess'
    ),
], 'phpunit/phpunit', '>=10.0 <13.0');

This package requires PHPUnit ^13.2, so the pair is inactive while running its own test suite - hence skip_run_class_in_separate_process.php.inc asserts no change. On a PHPUnit 11 or 12 project the annotation is converted.

Still not convertible

  • @coversDefaultClass / @usesDefaultClass - @coversDefaultClass is resolved as a prefix by CoversAnnotationWithValueToAttributeRector; @usesDefaultClass would need the same treatment in a Uses counterpart rule that does not exist yet
  • @codeCoverageIgnore - attribute is deprecated, already commented out, see Remove codeCoverageIgnore to annotation to attribute conversion #304
  • @excludeStaticPropertyFromBackup - takes two values (class and property), AnnotationWithValueToAttribute supports a single value

No annotation maps to an attribute introduced in 11.x or 12.x - those attributes (IgnoreDeprecations, RequiresPhpunitExtension, CoversMethod, DisableReturnValueGenerationForTestDoubles, ...) never had an annotation form.


Supersedes #744, which contained only the rule decoration part.

@TomasVotruba TomasVotruba changed the title Bind annotation to attribute pairs to phpunit/phpunit >=10.0 Add composer-based set for PHPUnit version bound annotation pairs Jul 31, 2026
@TomasVotruba TomasVotruba changed the title Add composer-based set for PHPUnit version bound annotation pairs Bind annotation to attribute conversion to installed PHPUnit version Jul 31, 2026
@TomasVotruba
TomasVotruba merged commit 743ee7b into main Jul 31, 2026
7 checks passed
@TomasVotruba
TomasVotruba deleted the annotations-to-attributes-version-bound branch July 31, 2026 19:05
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