forked from symplify/phpstan-rules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNoEmptyClassRule.php
130 lines (111 loc) · 2.96 KB
/
NoEmptyClassRule.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<?php
declare(strict_types=1);
namespace Symplify\PHPStanRules\Rules;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Trait_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use Symplify\Astral\Naming\SimpleNameResolver;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Throwable;
/**
* @see \Symplify\PHPStanRules\Tests\Rules\NoEmptyClassRule\NoEmptyClassRuleTest
*/
final class NoEmptyClassRule extends AbstractSymplifyRule
{
/**
* @var string
*/
public const ERROR_MESSAGE = 'There should be no empty class';
public function __construct(
private SimpleNameResolver $simpleNameResolver,
private ReflectionProvider $reflectionProvider
) {
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Class_::class, Trait_::class];
}
/**
* @param Class_|Trait_ $node
* @return string[]
*/
public function process(Node $node, Scope $scope): array
{
if ($node->stmts !== []) {
return [];
}
if ($this->shouldSkipClassLike($node)) {
return [];
}
return [self::ERROR_MESSAGE];
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(self::ERROR_MESSAGE, [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public function getSome()
{
}
}
CODE_SAMPLE
),
]);
}
/**
* @param Class_|Trait_ $classLike
*/
private function shouldSkipClassLike(ClassLike $classLike): bool
{
$className = $this->simpleNameResolver->getName($classLike);
if ($className === null) {
return true;
}
if (is_a($className, Throwable::class, true)) {
return true;
}
if ($classLike->getComments() !== []) {
return true;
}
return $this->isFinalClassWithAbstractOrInterfaceParent($classLike);
}
/**
* @param Class_|Trait_ $classLike
*/
private function isFinalClassWithAbstractOrInterfaceParent(ClassLike $classLike): bool
{
if (! $classLike instanceof Class_) {
return false;
}
if ($classLike->implements !== []) {
return true;
}
if (! $classLike->isFinal()) {
return false;
}
if ($classLike->extends === null) {
return false;
}
$parentClass = $this->simpleNameResolver->getName($classLike->extends);
if ($parentClass === null) {
return false;
}
$parentClassReflection = $this->reflectionProvider->getClass($parentClass);
return $parentClassReflection->isAbstract();
}
}