Skip to content

Commit 992427f

Browse files
committed
Extract AttributeVersionRequirementHelper
1 parent 952d0ee commit 992427f

6 files changed

Lines changed: 232 additions & 192 deletions

extension.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ services:
8080
-
8181
class: PHPStan\Type\PHPUnit\DynamicCallToAssertionIgnoreExtension
8282

83+
-
84+
class: PHPStan\Rules\PHPUnit\AttributeVersionRequirementHelper
85+
arguments:
86+
deprecationRulesInstalled: %deprecationRulesInstalled%
87+
bleedingEdge: %featureToggles.bleedingEdge%
88+
8389
conditionalTags:
8490
PHPStan\PhpDoc\PHPUnit\MockObjectTypeNodeResolverExtension:
8591
phpstan.phpDoc.typeNodeResolverExtension: %phpunit.convertUnionToIntersectionType%

rules.neon

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ services:
2727

2828
-
2929
class: PHPStan\Rules\PHPUnit\AttributeRequiresPhpVersionRule
30-
arguments:
31-
deprecationRulesInstalled: %deprecationRulesInstalled%
32-
bleedingEdge: %featureToggles.bleedingEdge%
3330
tags:
3431
- phpstan.rules.rule
3532

src/Rules/PHPUnit/AttributeRequiresPhpVersionRule.php

Lines changed: 7 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -2,67 +2,29 @@
22

33
namespace PHPStan\Rules\PHPUnit;
44

5-
use PharIo\Version\UnsupportedVersionConstraintException;
6-
use PharIo\Version\Version;
7-
use PharIo\Version\VersionConstraintParser;
85
use PhpParser\Node;
96
use PHPStan\Analyser\Scope;
107
use PHPStan\Node\InClassMethodNode;
11-
use PHPStan\Php\PhpMinorVersionIterator;
12-
use PHPStan\Php\PhpVersion;
138
use PHPStan\Rules\Rule;
14-
use PHPStan\Rules\RuleErrorBuilder;
15-
use PHPStan\Type\Constant\ConstantIntegerType;
16-
use PHPStan\Type\IntegerRangeType;
179
use PHPUnit\Framework\TestCase;
18-
use function count;
19-
use function is_numeric;
20-
use function preg_match;
21-
use function sprintf;
22-
use function substr_count;
23-
use function version_compare;
2410

2511
/**
2612
* @implements Rule<InClassMethodNode>
2713
*/
2814
class AttributeRequiresPhpVersionRule implements Rule
2915
{
3016

31-
private const VERSION_COMPARISON = "/(?P<operator>!=|<|<=|<>|=|==|>|>=)?\s*(?P<version>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m";
32-
33-
private PHPUnitVersion $PHPUnitVersion;
34-
3517
private TestMethodsHelper $testMethodsHelper;
3618

37-
private PhpVersion $fallbackPhpVersion;
38-
39-
/**
40-
* When phpstan-deprecation-rules is installed, rule reports deprecated usages.
41-
*/
42-
private bool $deprecationRulesInstalled;
43-
44-
/**
45-
* Whether warnings about incomplete versions are allowed to be emitted
46-
*/
47-
private bool $warnAboutIncompleteVersion;
48-
49-
private bool $bleedingEdge;
19+
private AttributeVersionRequirementHelper $attributeVersionRequirementHelper;
5020

5121
public function __construct(
52-
PHPUnitVersion $PHPUnitVersion,
5322
TestMethodsHelper $testMethodsHelper,
54-
bool $deprecationRulesInstalled,
55-
PhpVersion $phpVersion,
56-
bool $bleedingEdge,
57-
bool $warnAboutIncompleteVersion = true
23+
AttributeVersionRequirementHelper $attributeVersionRequirementHelper
5824
)
5925
{
60-
$this->PHPUnitVersion = $PHPUnitVersion;
6126
$this->testMethodsHelper = $testMethodsHelper;
62-
$this->deprecationRulesInstalled = $deprecationRulesInstalled;
63-
$this->fallbackPhpVersion = $phpVersion;
64-
$this->warnAboutIncompleteVersion = $warnAboutIncompleteVersion;
65-
$this->bleedingEdge = $bleedingEdge;
27+
$this->attributeVersionRequirementHelper = $attributeVersionRequirementHelper;
6628
}
6729

6830
public function getNodeType(): string
@@ -82,145 +44,10 @@ public function processNode(Node $node, Scope $scope): array
8244
return [];
8345
}
8446

85-
$phpstanPharIoVersions = $this->getAnalyzedPhpVersions($scope);
86-
if ($phpstanPharIoVersions === []) {
87-
return [];
88-
}
89-
90-
$errors = [];
91-
$parser = new VersionConstraintParser();
92-
foreach ($reflectionMethod->getAttributesByName('PHPUnit\Framework\Attributes\RequiresPhp') as $attr) {
93-
$args = $attr->getArguments();
94-
if (count($args) !== 1) {
95-
continue;
96-
}
97-
98-
// the following block is mimicing PHPUnit version parsing
99-
// see https://github.com/sebastianbergmann/phpunit/blob/43c2cd7b96ee1e800b35e4df23b419a88b53111d/src/Metadata/Version/Requirement.php
100-
101-
$versionRequirement = $args[0];
102-
103-
if ($this->warnAboutIncompleteVersion($versionRequirement)) {
104-
$errors[] = RuleErrorBuilder::message(
105-
sprintf('Version requirement is incomplete.'),
106-
)
107-
->identifier('phpunit.attributeRequiresPhpVersion')
108-
->build();
109-
}
110-
111-
if (
112-
!is_numeric($versionRequirement)
113-
) {
114-
if (!$this->bleedingEdge) {
115-
continue;
116-
}
117-
118-
try {
119-
// check composer like version constraints, e.g. ^1 or ~2
120-
$testPhpVersionConstraint = $parser->parse($versionRequirement);
121-
122-
foreach ($phpstanPharIoVersions as $pharIoVersion) {
123-
if ($testPhpVersionConstraint->complies($pharIoVersion)) {
124-
// one of the versions within range matched, check next attribute
125-
continue 2;
126-
}
127-
}
128-
} catch (UnsupportedVersionConstraintException $e) {
129-
// test php-src builtin operators as in version_compare()
130-
if (preg_match(self::VERSION_COMPARISON, $versionRequirement, $matches) <= 0) {
131-
$errors[] = RuleErrorBuilder::message(
132-
sprintf($e->getMessage()),
133-
)
134-
->identifier('phpunit.attributeRequiresPhpVersion')
135-
->build();
136-
137-
continue;
138-
}
139-
140-
$operator = $matches['operator'] !== '' ? $matches['operator'] : '>=';
141-
142-
foreach ($phpstanPharIoVersions as $pharIoVersion) {
143-
if (version_compare($pharIoVersion->getVersionString(), $matches['version'], $operator)) {
144-
// one of the versions within range matched, check next attribute
145-
continue 2;
146-
}
147-
}
148-
}
149-
150-
$errors[] = RuleErrorBuilder::message(
151-
sprintf('Version requirement will always evaluate to false.'),
152-
)
153-
->identifier('phpunit.attributeRequiresPhpVersion')
154-
->build();
155-
156-
continue;
157-
}
158-
159-
if ($this->PHPUnitVersion->requiresPhpversionAttributeWithOperator()->yes()) {
160-
$errors[] = RuleErrorBuilder::message(
161-
sprintf('Version requirement is missing operator.'),
162-
)
163-
->identifier('phpunit.attributeRequiresPhpVersion')
164-
->build();
165-
} elseif (
166-
$this->deprecationRulesInstalled
167-
&& $this->PHPUnitVersion->deprecatesPhpversionAttributeWithoutOperator()->yes()
168-
) {
169-
$errors[] = RuleErrorBuilder::message(
170-
sprintf('Version requirement without operator is deprecated.'),
171-
)
172-
->identifier('phpunit.attributeRequiresPhpVersion')
173-
->build();
174-
}
175-
}
176-
177-
return $errors;
178-
}
179-
180-
/**
181-
* @return Version[]
182-
*/
183-
private function getAnalyzedPhpVersions(Scope $scope): array
184-
{
185-
$scopePhpVersion = $scope->getPhpVersion()->getType();
186-
if ($scopePhpVersion instanceof ConstantIntegerType) {
187-
$v = new PhpVersion($scopePhpVersion->getValue());
188-
return [new Version($v->getVersionString())];
189-
} elseif ($scopePhpVersion instanceof IntegerRangeType) {
190-
if ($scopePhpVersion->getMin() === null || $scopePhpVersion->getMax() === null) {
191-
return [];
192-
}
193-
194-
$versions = [];
195-
$minorVersionIterator = new PhpMinorVersionIterator(
196-
new PhpVersion($scopePhpVersion->getMin()),
197-
new PhpVersion($scopePhpVersion->getMax()),
198-
);
199-
foreach ($minorVersionIterator as $phpstanVersion) {
200-
$versions[] = new Version($phpstanVersion->getVersionString());
201-
}
202-
return $versions;
203-
}
204-
205-
return [new Version($this->fallbackPhpVersion->getVersionString())];
206-
}
207-
208-
// see https://github.com/sebastianbergmann/phpunit/issues/6451
209-
private function warnAboutIncompleteVersion(string $versionRequirement): bool
210-
{
211-
if (!$this->bleedingEdge) {
212-
return false;
213-
}
214-
215-
if (!$this->warnAboutIncompleteVersion) {
216-
return false;
217-
}
218-
219-
if (!$this->PHPUnitVersion->warnsAboutIncompleteVersion()->yes()) {
220-
return false;
221-
}
222-
223-
return substr_count($versionRequirement, '.') !== 2;
47+
return $this->attributeVersionRequirementHelper->checkRequiresPhpVersion(
48+
$reflectionMethod->getAttributesByName('PHPUnit\Framework\Attributes\RequiresPhp'),
49+
$scope,
50+
);
22451
}
22552

22653
}

0 commit comments

Comments
 (0)