44
55use Composer \Semver \Constraint \ConstraintInterface ;
66use Composer \Semver \VersionParser ;
7+ use PharIo \Version \UnsupportedVersionConstraintException ;
8+ use PharIo \Version \Version ;
9+ use PharIo \Version \VersionConstraint ;
10+ use PharIo \Version \VersionConstraintParser ;
711use PhpParser \Node ;
812use PHPStan \Analyser \Scope ;
913use PHPStan \Node \InClassMethodNode ;
1519use function count ;
1620use function is_numeric ;
1721use function method_exists ;
22+ use function preg_match ;
1823use function sprintf ;
1924
2025/**
2126 * @implements Rule<InClassMethodNode>
2227 */
2328class AttributeRequiresPhpVersionRule implements Rule
2429{
30+ private const VERSION_COMPARISON = "/(?P<operator>!=|<|<=|<>|=|==|>|>=)?\s*(?P<version>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]* \r?$/m " ;
2531
26- private ConstraintInterface $ phpstanVersionConstraint ;
32+
33+ private Version $ phpstanPhpVersion ;
2734
2835 private PHPUnitVersion $ PHPUnitVersion ;
2936
@@ -45,8 +52,7 @@ public function __construct(
4552 $ this ->testMethodsHelper = $ testMethodsHelper ;
4653 $ this ->deprecationRulesInstalled = $ deprecationRulesInstalled ;
4754
48- $ parser = new VersionParser ();
49- $ this ->phpstanVersionConstraint = $ parser ->parseConstraints ($ phpVersion ->getVersionString ());
55+ $ this ->phpstanPhpVersion = new Version ($ phpVersion ->getVersionString ());
5056 }
5157
5258 public function getNodeType (): string
@@ -72,7 +78,7 @@ public function processNode(Node $node, Scope $scope): array
7278 }
7379
7480 $ errors = [];
75- $ parser = new VersionParser ();
81+ $ parser = new VersionConstraintParser ();
7682 foreach ($ reflectionMethod ->getAttributes ('PHPUnit\Framework\Attributes\RequiresPhp ' ) as $ attr ) {
7783 $ args = $ attr ->getArguments ();
7884 if (count ($ args ) !== 1 ) {
@@ -82,21 +88,28 @@ public function processNode(Node $node, Scope $scope): array
8288 if (
8389 !is_numeric ($ args [0 ])
8490 ) {
85-
8691 try {
87- $ testPhpVersionConstraint = $ parser ->parseConstraints ($ args [0 ]);
88- } catch (UnexpectedValueException $ e ) {
89- $ errors [] = RuleErrorBuilder::message (
90- sprintf ($ e ->getMessage ()),
91- )
92- ->identifier ('phpunit.attributeRequiresPhpVersion ' )
93- ->build ();
94-
95- continue ;
96- }
97-
98- if ($ this ->phpstanVersionConstraint ->matches ($ testPhpVersionConstraint )) {
99- continue ;
92+ $ testPhpVersionConstraint = $ parser ->parse ($ args [0 ]);
93+
94+ if ($ testPhpVersionConstraint ->complies ($ this ->phpstanPhpVersion )) {
95+ continue ;
96+ }
97+ } catch (UnsupportedVersionConstraintException $ e ) {
98+ if (preg_match (self ::VERSION_COMPARISON , $ args [0 ], $ matches ) > 0 ) {
99+ $ operator = $ matches ['operator ' ] !== '' ? $ matches ['operator ' ] : '>= ' ;
100+
101+ if (version_compare ($ this ->phpstanPhpVersion ->getVersionString (), $ matches ['version ' ], $ operator )) {
102+ continue ;
103+ }
104+ } else {
105+ $ errors [] = RuleErrorBuilder::message (
106+ sprintf ($ e ->getMessage ()),
107+ )
108+ ->identifier ('phpunit.attributeRequiresPhpVersion ' )
109+ ->build ();
110+
111+ continue ;
112+ }
100113 }
101114
102115 $ errors [] = RuleErrorBuilder::message (
0 commit comments