Skip to content

Commit 3377080

Browse files
committed
Interfaces cannot include final properties
1 parent bfa7a6c commit 3377080

7 files changed

+97
-0
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ lint:
103103
--exclude tests/PHPStan/Rules/Properties/data/overriding-final-property.php \
104104
--exclude tests/PHPStan/Rules/Properties/data/private-final-property-hooks.php \
105105
--exclude tests/PHPStan/Rules/Properties/data/abstract-final-property-hook.php \
106+
--exclude tests/PHPStan/Rules/Properties/data/final-property-hooks-in-interface.php \
107+
--exclude tests/PHPStan/Rules/Properties/data/final-property-hooks.php \
106108
--exclude tests/PHPStan/Rules/Properties/data/final-properties.php \
107109
src tests
108110

src/Rules/Properties/PropertiesInInterfaceRule.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ public function processNode(Node $node, Scope $scope): array
7575
];
7676
}
7777

78+
if ($node->isFinal()) {
79+
return [
80+
RuleErrorBuilder::message('Interfaces cannot include final properties.')
81+
->nonIgnorable()
82+
->identifier('property.finalInInterface')
83+
->build(),
84+
];
85+
}
86+
7887
if ($this->hasAnyHookBody($node)) {
7988
return [
8089
RuleErrorBuilder::message('Interfaces cannot include property hooks with bodies.')

tests/PHPStan/Rules/Properties/PropertiesInInterfaceRuleTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public function testPhp83AndPropertiesInInterface(): void
4040
'Interfaces cannot include properties.',
4141
11,
4242
],
43+
[
44+
'Interfaces cannot include properties.',
45+
13,
46+
],
4347
]);
4448
}
4549

@@ -79,6 +83,10 @@ public function testPhp84AndPropertiesInInterface(): void
7983
'Interfaces can only include hooked properties.',
8084
11,
8185
],
86+
[
87+
'Interfaces can only include hooked properties.',
88+
13,
89+
],
8290
]);
8391
}
8492

@@ -140,6 +148,28 @@ public function testPhp84AndReadonlyPropertyHooksInInterface(): void
140148
]);
141149
}
142150

151+
public function testPhp84AndFinalPropertyHooksInInterface(): void
152+
{
153+
if (PHP_VERSION_ID < 80400) {
154+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
155+
}
156+
157+
$this->analyse([__DIR__ . '/data/final-property-hooks-in-interface.php'], [
158+
[
159+
'Interfaces cannot include final properties.',
160+
7,
161+
],
162+
[
163+
'Interfaces cannot include final properties.',
164+
9,
165+
],
166+
[
167+
'Interfaces cannot include final properties.',
168+
11,
169+
],
170+
]);
171+
}
172+
143173
public function testPhp84AndStaticHookedPropertyInInterface(): void
144174
{
145175
if (PHP_VERSION_ID < 80400) {

tests/PHPStan/Rules/Properties/PropertyInClassRuleTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,4 +281,18 @@ public function testBeforePhp84FinalProperties(): void
281281
]);
282282
}
283283

284+
public function testPhp84FinalPropertyHooks(): void
285+
{
286+
if (PHP_VERSION_ID < 80400) {
287+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
288+
}
289+
290+
$this->analyse([__DIR__ . '/data/final-property-hooks.php'], [
291+
[
292+
'Cannot use the final modifier on an abstract class member on line 19',
293+
19,
294+
],
295+
]);
296+
}
297+
284298
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace FinalPropertyHooksInInterface;
4+
5+
interface HelloWorld
6+
{
7+
public final string $firstName { get; set; }
8+
9+
public final string $middleName { get; }
10+
11+
public final string $lastName { set; }
12+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace FinalPropertyHooks;
4+
5+
class HelloWorld
6+
{
7+
public final string $firstName {
8+
get => $this->firstName;
9+
set => $this->firstName;
10+
}
11+
12+
public final string $middleName { get => $this->middleName; }
13+
14+
public final string $lastName { set => $this->lastName; }
15+
}
16+
17+
abstract class HiWorld
18+
{
19+
public abstract final string $firstName { get { return 'jake'; } set; }
20+
}
21+
22+
final class GoodMorningWorld
23+
{
24+
public string $firstName {
25+
get => $this->firstName;
26+
set => $this->firstName;
27+
}
28+
}

tests/PHPStan/Rules/Properties/data/properties-in-interface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ interface HelloWorld
99
public \DateTimeInterface $dateTime;
1010

1111
public static \Closure $callable;
12+
13+
public final \DateTime $finalProperty;
1214
}

0 commit comments

Comments
 (0)