Skip to content

Commit d9082ec

Browse files
authored
Implement Scope->getPhpVersion()
1 parent c0bfae6 commit d9082ec

File tree

7 files changed

+182
-14
lines changed

7 files changed

+182
-14
lines changed

src/Analyser/MutatingScope.php

+11
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
use PHPStan\Parser\NewAssignedToPropertyVisitor;
4848
use PHPStan\Parser\Parser;
4949
use PHPStan\Php\PhpVersion;
50+
use PHPStan\Php\PhpVersions;
5051
use PHPStan\PhpDoc\Tag\TemplateTag;
5152
use PHPStan\Reflection\Assertions;
5253
use PHPStan\Reflection\Callables\CallableParametersAcceptor;
@@ -5721,4 +5722,14 @@ public function getIterableValueType(Type $iteratee): Type
57215722
return $iteratee->getIterableValueType();
57225723
}
57235724

5725+
public function getPhpVersion(): PhpVersions
5726+
{
5727+
$versionExpr = new ConstFetch(new Name('PHP_VERSION_ID'));
5728+
if (!$this->hasExpressionType($versionExpr)->yes()) {
5729+
return new PhpVersions(new ConstantIntegerType($this->phpVersion->getVersionId()));
5730+
}
5731+
5732+
return new PhpVersions($this->getType($versionExpr));
5733+
}
5734+
57245735
}

src/Analyser/Scope.php

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PhpParser\Node\Expr;
77
use PhpParser\Node\Name;
88
use PhpParser\Node\Param;
9+
use PHPStan\Php\PhpVersions;
910
use PHPStan\Reflection\ClassConstantReflection;
1011
use PHPStan\Reflection\ClassMemberAccessAnswerer;
1112
use PHPStan\Reflection\ClassReflection;
@@ -136,4 +137,6 @@ public function filterByFalseyValue(Expr $expr): self;
136137

137138
public function isInFirstLevelStatement(): bool;
138139

140+
public function getPhpVersion(): PhpVersions;
141+
139142
}

src/Php/PhpVersions.php

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Php;
4+
5+
use PHPStan\TrinaryLogic;
6+
use PHPStan\Type\IntegerRangeType;
7+
use PHPStan\Type\Type;
8+
9+
/**
10+
* @api
11+
*/
12+
final class PhpVersions
13+
{
14+
15+
public function __construct(
16+
private Type $phpVersions,
17+
)
18+
{
19+
}
20+
21+
public function producesWarningForFinalPrivateMethods(): TrinaryLogic
22+
{
23+
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
24+
}
25+
26+
}

src/Rules/Methods/FinalPrivateMethodRule.php

+1-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Node\InClassMethodNode;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Rules\Rule;
109
use PHPStan\Rules\RuleErrorBuilder;
1110
use function sprintf;
@@ -14,12 +13,6 @@
1413
final class FinalPrivateMethodRule implements Rule
1514
{
1615

17-
public function __construct(
18-
private PhpVersion $phpVersion,
19-
)
20-
{
21-
}
22-
2316
public function getNodeType(): string
2417
{
2518
return InClassMethodNode::class;
@@ -28,7 +21,7 @@ public function getNodeType(): string
2821
public function processNode(Node $node, Scope $scope): array
2922
{
3023
$method = $node->getMethodReflection();
31-
if (!$this->phpVersion->producesWarningForFinalPrivateMethods()) {
24+
if ($scope->getPhpVersion()->producesWarningForFinalPrivateMethods()->no()) {
3225
return [];
3326
}
3427

tests/PHPStan/Php/PhpVersionsTest.php

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Php;
4+
5+
use PHPStan\TrinaryLogic;
6+
use PHPStan\Type\Constant\ConstantIntegerType;
7+
use PHPStan\Type\IntegerRangeType;
8+
use PHPStan\Type\Type;
9+
use PHPStan\Type\UnionType;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class PhpVersionsTest extends TestCase
13+
{
14+
15+
/**
16+
* @dataProvider dataProducesWarningForFinalPrivateMethods
17+
*/
18+
public function testProducesWarningForFinalPrivateMethods(TrinaryLogic $expected, Type $versionType): void
19+
{
20+
$phpVersions = new PhpVersions($versionType);
21+
$this->assertSame(
22+
$expected->describe(),
23+
$phpVersions->producesWarningForFinalPrivateMethods()->describe(),
24+
);
25+
}
26+
27+
public function dataProducesWarningForFinalPrivateMethods(): iterable
28+
{
29+
yield [
30+
TrinaryLogic::createNo(),
31+
new ConstantIntegerType(70400),
32+
];
33+
34+
yield [
35+
TrinaryLogic::createYes(),
36+
new ConstantIntegerType(80000),
37+
];
38+
39+
yield [
40+
TrinaryLogic::createYes(),
41+
new ConstantIntegerType(80100),
42+
];
43+
44+
yield [
45+
TrinaryLogic::createYes(),
46+
IntegerRangeType::fromInterval(80000, null),
47+
];
48+
49+
yield [
50+
TrinaryLogic::createMaybe(),
51+
IntegerRangeType::fromInterval(null, 80000),
52+
];
53+
54+
yield [
55+
TrinaryLogic::createNo(),
56+
IntegerRangeType::fromInterval(70200, 70400),
57+
];
58+
59+
yield [
60+
TrinaryLogic::createMaybe(),
61+
new UnionType([
62+
IntegerRangeType::fromInterval(70200, 70400),
63+
IntegerRangeType::fromInterval(80200, 80400),
64+
]),
65+
];
66+
}
67+
68+
}

tests/PHPStan/Rules/Methods/FinalPrivateMethodRuleTest.php

+30-6
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,15 @@
55
use PHPStan\Php\PhpVersion;
66
use PHPStan\Rules\Rule;
77
use PHPStan\Testing\RuleTestCase;
8+
use const PHP_VERSION_ID;
89

910
/** @extends RuleTestCase<FinalPrivateMethodRule> */
1011
class FinalPrivateMethodRuleTest extends RuleTestCase
1112
{
1213

13-
private int $phpVersionId;
14-
1514
protected function getRule(): Rule
1615
{
17-
return new FinalPrivateMethodRule(
18-
new PhpVersion($this->phpVersionId),
19-
);
16+
return new FinalPrivateMethodRule();
2017
}
2118

2219
public function dataRule(): array
@@ -44,8 +41,35 @@ public function dataRule(): array
4441
*/
4542
public function testRule(int $phpVersion, array $errors): void
4643
{
47-
$this->phpVersionId = $phpVersion;
44+
$testVersion = new PhpVersion($phpVersion);
45+
$runtimeVersion = new PhpVersion(PHP_VERSION_ID);
46+
47+
if (
48+
$testVersion->getMajorVersionId() !== $runtimeVersion->getMajorVersionId()
49+
|| $testVersion->getMinorVersionId() !== $runtimeVersion->getMinorVersionId()
50+
) {
51+
$this->markTestSkipped('Test requires PHP version ' . $testVersion->getMajorVersionId() . '.' . $testVersion->getMinorVersionId() . '.*');
52+
}
53+
4854
$this->analyse([__DIR__ . '/data/final-private-method.php'], $errors);
4955
}
5056

57+
public function testRulePhpVersions(): void
58+
{
59+
$this->analyse([__DIR__ . '/data/final-private-method-phpversions.php'], [
60+
[
61+
'Private method FinalPrivateMethodPhpVersions\FooBarPhp8orHigher::foo() cannot be final as it is never overridden by other classes.',
62+
9,
63+
],
64+
[
65+
'Private method FinalPrivateMethodPhpVersions\FooBarPhp74OrHigher::foo() cannot be final as it is never overridden by other classes.',
66+
29,
67+
],
68+
[
69+
'Private method FinalPrivateMethodPhpVersions\FooBarBaz::foo() cannot be final as it is never overridden by other classes.',
70+
39,
71+
],
72+
]);
73+
}
74+
5175
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace FinalPrivateMethodPhpVersions;
4+
5+
if (PHP_VERSION_ID >= 80000) {
6+
class FooBarPhp8orHigher
7+
{
8+
9+
final private function foo(): void
10+
{
11+
}
12+
}
13+
}
14+
15+
if (PHP_VERSION_ID < 80000) {
16+
class FooBarPhp7
17+
{
18+
19+
final private function foo(): void
20+
{
21+
}
22+
}
23+
}
24+
25+
if (PHP_VERSION_ID > 70400) {
26+
class FooBarPhp74OrHigher
27+
{
28+
29+
final private function foo(): void
30+
{
31+
}
32+
}
33+
}
34+
35+
if (PHP_VERSION_ID < 70400 || PHP_VERSION_ID >= 80100) {
36+
class FooBarBaz
37+
{
38+
39+
final private function foo(): void
40+
{
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)