Skip to content

Commit 595b186

Browse files
authored
Implement FinalPrivateConstantRule
1 parent 80fec2e commit 595b186

File tree

5 files changed

+89
-0
lines changed

5 files changed

+89
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ lint:
108108
--exclude tests/PHPStan/Rules/Properties/data/final-property-hooks.php \
109109
--exclude tests/PHPStan/Rules/Properties/data/final-properties.php \
110110
--exclude tests/PHPStan/Rules/Properties/data/property-in-interface-explicit-abstract.php \
111+
--exclude tests/PHPStan/Rules/Constants/data/final-private-const.php \
111112
src tests
112113

113114
cs:

conf/config.level0.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ rules:
4646
- PHPStan\Rules\Constants\FinalConstantRule
4747
- PHPStan\Rules\Constants\MagicConstantContextRule
4848
- PHPStan\Rules\Constants\NativeTypedClassConstantRule
49+
- PHPStan\Rules\Constants\FinalPrivateConstantRule
4950
- PHPStan\Rules\EnumCases\EnumCaseAttributesRule
5051
- PHPStan\Rules\Exceptions\NoncapturingCatchRule
5152
- PHPStan\Rules\Exceptions\ThrowExpressionRule
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Constants;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Stmt\ClassConst;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\ShouldNotHappenException;
11+
use function sprintf;
12+
13+
/** @implements Rule<ClassConst> */
14+
final class FinalPrivateConstantRule implements Rule
15+
{
16+
17+
public function getNodeType(): string
18+
{
19+
return ClassConst::class;
20+
}
21+
22+
public function processNode(Node $node, Scope $scope): array
23+
{
24+
if (!$scope->isInClass()) {
25+
throw new ShouldNotHappenException();
26+
}
27+
$classReflection = $scope->getClassReflection();
28+
29+
if (!$node->isFinal()) {
30+
return [];
31+
}
32+
33+
if (!$node->isPrivate()) {
34+
return [];
35+
}
36+
37+
$errors = [];
38+
foreach ($node->consts as $classConstNode) {
39+
$errors[] = RuleErrorBuilder::message(sprintf(
40+
'Private constant %s::%s() cannot be final as it is never overridden by other classes.',
41+
$classReflection->getDisplayName(),
42+
$classConstNode->name->name,
43+
))->identifier('classConstant.finalPrivate')->nonIgnorable()->build();
44+
}
45+
46+
return $errors;
47+
}
48+
49+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Constants;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/** @extends RuleTestCase<FinalPrivateConstantRule> */
9+
class FinalPrivateConstantRuleTest extends RuleTestCase
10+
{
11+
12+
protected function getRule(): Rule
13+
{
14+
return new FinalPrivateConstantRule();
15+
}
16+
17+
public function testRule(): void
18+
{
19+
$this->analyse([__DIR__ . '/data/final-private-const.php'], [
20+
[
21+
'Private constant FinalPrivateConstants\User::FINAL_PRIVATE() cannot be final as it is never overridden by other classes.',
22+
8,
23+
],
24+
]);
25+
}
26+
27+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace FinalPrivateConstants;
4+
5+
class User
6+
{
7+
private const PRIVATE = 'mailto: example.org';
8+
final private const FINAL_PRIVATE = 'mailto: example.org';
9+
final protected const PROTECTED = 'mailto: example.org';
10+
final public const PUBLIC = 'mailto: example.org';
11+
}

0 commit comments

Comments
 (0)