Skip to content

Commit da12dc2

Browse files
committed
OverridingPropertyRule - check for final
1 parent c34432b commit da12dc2

File tree

4 files changed

+77
-1
lines changed

4 files changed

+77
-1
lines changed

src/Reflection/Php/PhpPropertyReflection.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,14 @@ public function isAbstract(): TrinaryLogic
234234

235235
public function isFinal(): TrinaryLogic
236236
{
237-
return TrinaryLogic::createFromBoolean($this->reflection->isFinal());
237+
if ($this->reflection->isFinal()) {
238+
return TrinaryLogic::createYes();
239+
}
240+
if ($this->reflection->isPrivate()) {
241+
return TrinaryLogic::createNo();
242+
}
243+
244+
return TrinaryLogic::createFromBoolean($this->isPrivateSet());
238245
}
239246

240247
public function isVirtual(): TrinaryLogic

src/Rules/Properties/OverridingPropertyRule.php

+12
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ public function processNode(Node $node, Scope $scope): array
102102
))->identifier('property.visibility')->nonIgnorable()->build();
103103
}
104104

105+
if ($prototype->isFinal()->yes()) {
106+
$errors[] = RuleErrorBuilder::message(sprintf(
107+
'Property %s::$%s overrides final property %s::$%s.',
108+
$classReflection->getDisplayName(),
109+
$node->getName(),
110+
$prototype->getDeclaringClass()->getDisplayName(),
111+
$node->getName(),
112+
))->identifier('property.parentPropertyFinal')
113+
->nonIgnorable()
114+
->build();
115+
}
116+
105117
$typeErrors = [];
106118
$nativeType = $node->getNativeType();
107119
if ($prototype->hasNativeType()) {

tests/PHPStan/Rules/Properties/OverridingPropertyRuleTest.php

+28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPStan\Rules\Rule;
66
use PHPStan\Testing\RuleTestCase;
77
use function sprintf;
8+
use const PHP_VERSION_ID;
89

910
/**
1011
* @extends RuleTestCase<OverridingPropertyRule>
@@ -171,4 +172,31 @@ public function testBug7692(): void
171172
$this->analyse([__DIR__ . '/data/bug-7692.php'], []);
172173
}
173174

175+
public function testFinal(): void
176+
{
177+
if (PHP_VERSION_ID < 80400) {
178+
$this->markTestSkipped('Test requires PHP 8.4.');
179+
}
180+
181+
$this->reportMaybes = true;
182+
$this->analyse([__DIR__ . '/data/overriding-final-property.php'], [
183+
[
184+
'Property OverridingFinalProperty\Bar::$a overrides final property OverridingFinalProperty\Foo::$a.',
185+
21,
186+
],
187+
[
188+
'Property OverridingFinalProperty\Bar::$b overrides final property OverridingFinalProperty\Foo::$b.',
189+
23,
190+
],
191+
[
192+
'Property OverridingFinalProperty\Bar::$c overrides final property OverridingFinalProperty\Foo::$c.',
193+
25,
194+
],
195+
[
196+
'Property OverridingFinalProperty\Bar::$d overrides final property OverridingFinalProperty\Foo::$d.',
197+
27,
198+
],
199+
]);
200+
}
201+
174202
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php // lint >= 8.4
2+
3+
namespace OverridingFinalProperty;
4+
5+
class Foo
6+
{
7+
8+
final public $a;
9+
10+
final protected $b;
11+
12+
public private(set) $c;
13+
14+
protected private(set) $d;
15+
16+
}
17+
18+
class Bar extends Foo
19+
{
20+
21+
public $a;
22+
23+
public $b;
24+
25+
public $c;
26+
27+
public $d;
28+
29+
}

0 commit comments

Comments
 (0)