Skip to content

Commit 64d922a

Browse files
jakubtobiaszondrejmirtes
authored andcommitted
Improve readability and provide minor fixes
1 parent 33359b3 commit 64d922a

File tree

9 files changed

+166
-58
lines changed

9 files changed

+166
-58
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ lint:
8484
--exclude tests/PHPStan/Rules/Properties/data/abstract-non-hooked-properties-in-abstract-class.php \
8585
--exclude tests/PHPStan/Rules/Properties/data/non-abstract-hooked-properties-in-abstract-class.php \
8686
--exclude tests/PHPStan/Rules/Properties/data/non-abstract-hooked-properties-in-class.php \
87+
--exclude tests/PHPStan/Rules/Properties/data/hooked-properties-in-class.php \
88+
--exclude tests/PHPStan/Rules/Properties/data/hooked-properties-without-bodies-in-class.php \
8789
src tests
8890

8991
cs:

build/collision-detector.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212
"../tests/notAutoloaded",
1313
"../tests/PHPStan/Rules/Functions/data/define-bug-3349.php",
1414
"../tests/PHPStan/Levels/data/stubs/function.php",
15+
"../tests/PHPStan/Rules/Properties/data/properties-in-interface.php",
1516
"../tests/PHPStan/Rules/Properties/data/property-hooks-bodies-in-interface.php",
1617
"../tests/PHPStan/Rules/Properties/data/property-hooks-in-interface.php",
1718
"../tests/PHPStan/Rules/Properties/data/property-hooks-visibility-in-interface.php",
1819
"../tests/PHPStan/Rules/Properties/data/abstract-hooked-properties-in-class.php",
1920
"../tests/PHPStan/Rules/Properties/data/abstract-hooked-properties-with-bodies.php",
2021
"../tests/PHPStan/Rules/Properties/data/abstract-non-hooked-properties-in-abstract-class.php",
2122
"../tests/PHPStan/Rules/Properties/data/non-abstract-hooked-properties-in-abstract-class.php",
22-
"../tests/PHPStan/Rules/Properties/data/non-abstract-hooked-properties-in-class.php"
23+
"../tests/PHPStan/Rules/Properties/data/non-abstract-hooked-properties-in-class.php",
24+
"../tests/PHPStan/Rules/Properties/data/hooked-properties-in-class.php",
25+
"../tests/PHPStan/Rules/Properties/data/hooked-properties-without-bodies-in-class.php"
2326
]
2427
}

src/Rules/Properties/PropertiesInInterfaceRule.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ public function processNode(Node $node, Scope $scope): array
3030
return [];
3131
}
3232

33+
if (!$this->phpVersion->supportsPropertyHooks() && $node->hasHooks()) {
34+
return [
35+
RuleErrorBuilder::message('Property hooks in interfaces are supported only on PHP 8.4 and later.')
36+
->nonIgnorable()
37+
->identifier('property.unsupportedHooksInInterface')
38+
->build(),
39+
];
40+
}
41+
3342
if (!$this->phpVersion->supportsPropertyHooks()) {
3443
return [
3544
RuleErrorBuilder::message('Interfaces may not include properties.')

src/Rules/Properties/PropertyInClassRule.php

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,57 +28,76 @@ public function processNode(Node $node, Scope $scope): array
2828
{
2929
$classReflection = $node->getClassReflection();
3030

31-
if (!$classReflection->isClass() || !$this->phpVersion->supportsPropertyHooks()) {
31+
if (!$classReflection->isClass()) {
3232
return [];
3333
}
3434

35-
if (!$classReflection->isAbstract() && $node->hasHooks() && $node->isAbstract()) {
35+
if (!$this->phpVersion->supportsPropertyHooks() && $node->hasHooks()) {
3636
return [
37-
RuleErrorBuilder::message('Classes may not include abstract hooked properties.')
37+
RuleErrorBuilder::message('Property hooks in classes are supported only on PHP 8.4 and later.')
3838
->nonIgnorable()
39-
->identifier('property.abstractHookedInClass')
39+
->identifier('property.unsupportedHooksInClass')
4040
->build(),
4141
];
4242
}
4343

44-
if (!$classReflection->isAbstract() && $node->hasHooks() && !$this->doAllHooksHaveBody($node)) {
45-
return [
46-
RuleErrorBuilder::message('Classes may not include hooked properties without bodies.')
47-
->nonIgnorable()
48-
->identifier('property.hookedWithoutBodyInClass')
49-
->build(),
50-
];
51-
}
52-
53-
if (!$classReflection->isAbstract()) {
44+
if (!$this->phpVersion->supportsPropertyHooks()) {
5445
return [];
5546
}
5647

57-
if ($node->hasHooks() && !$node->isAbstract()) {
58-
return [
59-
RuleErrorBuilder::message('Abstract classes may not include non-abstract hooked properties without bodies.')
60-
->nonIgnorable()
61-
->identifier('property.nonAbstractHookedWithoutBodyInAbstractClass')
62-
->build(),
63-
];
64-
}
48+
if ($classReflection->isAbstract()) {
49+
if ($node->isAbstract()) {
50+
if (!$node->hasHooks()) {
51+
return [
52+
RuleErrorBuilder::message('Only hooked properties may be declared abstract.')
53+
->nonIgnorable()
54+
->identifier('property.nonHookedAbstractInClass')
55+
->build(),
56+
];
57+
}
58+
59+
if (!$this->isAtLeastOneHookBodyEmpty($node)) {
60+
return [
61+
RuleErrorBuilder::message('Abstract properties must specify at least one abstract hook.')
62+
->nonIgnorable()
63+
->identifier('property.hookedAbstractWithBodies')
64+
->build(),
65+
];
66+
}
67+
}
6568

66-
if ($node->isAbstract() && !$node->hasHooks()) {
67-
return [
68-
RuleErrorBuilder::message('Only hooked properties may be declared abstract.')
69-
->nonIgnorable()
70-
->identifier('property.nonHookedAbstractInClass')
71-
->build(),
72-
];
69+
if (!$node->isAbstract()) {
70+
if ($node->hasHooks()) {
71+
return [
72+
RuleErrorBuilder::message('Abstract classes may not include non-abstract hooked properties without bodies.')
73+
->nonIgnorable()
74+
->identifier('property.nonAbstractHookedWithoutBodyInAbstractClass')
75+
->build(),
76+
];
77+
}
78+
}
79+
80+
return [];
7381
}
7482

75-
if ($node->isAbstract() && !$this->isAtLeastOneHookBodyEmpty($node)) {
76-
return [
77-
RuleErrorBuilder::message('Abstract properties must specify at least one abstract hook.')
78-
->nonIgnorable()
79-
->identifier('property.hookedAbstractWithBodies')
80-
->build(),
81-
];
83+
if ($node->hasHooks()) {
84+
if ($node->isAbstract()) {
85+
return [
86+
RuleErrorBuilder::message('Classes may not include abstract hooked properties.')
87+
->nonIgnorable()
88+
->identifier('property.abstractHookedInClass')
89+
->build(),
90+
];
91+
}
92+
93+
if (!$this->doAllHooksHaveBody($node)) {
94+
return [
95+
RuleErrorBuilder::message('Non-abstract classes may not include hooked properties without bodies.')
96+
->nonIgnorable()
97+
->identifier('property.hookedWithoutBodyInClass')
98+
->build(),
99+
];
100+
}
82101
}
83102

84103
return [];

tests/PHPStan/Rules/Properties/PropertiesInInterfaceRuleTest.php

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,38 @@
1313
class PropertiesInInterfaceRuleTest extends RuleTestCase
1414
{
1515

16-
private int $phpVersion = PHP_VERSION_ID;
17-
1816
protected function getRule(): Rule
1917
{
20-
return new PropertiesInInterfaceRule(new PhpVersion($this->phpVersion));
18+
return new PropertiesInInterfaceRule(new PhpVersion(PHP_VERSION_ID));
2119
}
2220

2321
public function testPhp83AndPropertiesInInterface(): void
2422
{
25-
$this->phpVersion = 80300;
23+
if (PHP_VERSION_ID >= 80400) {
24+
$this->markTestSkipped('Test requires PHP 8.3 or earlier.');
25+
}
2626

2727
$this->analyse([__DIR__ . '/data/properties-in-interface.php'], [
2828
[
29-
'Interfaces may not include properties.',
29+
'Property hooks in interfaces are supported only on PHP 8.4 and later.',
3030
7,
3131
],
3232
[
3333
'Interfaces may not include properties.',
3434
9,
3535
],
36+
[
37+
'Interfaces may not include properties.',
38+
11,
39+
],
3640
]);
3741
}
3842

3943
public function testPhp83AndPropertyHooksInInterface(): void
4044
{
41-
$this->phpVersion = 80300;
45+
if (PHP_VERSION_ID >= 80400) {
46+
$this->markTestSkipped('Test requires PHP 8.3 or earlier.');
47+
}
4248

4349
$this->analyse([__DIR__ . '/data/property-hooks-in-interface.php'], [
4450
[
@@ -54,23 +60,27 @@ public function testPhp83AndPropertyHooksInInterface(): void
5460

5561
public function testPhp84AndPropertiesInInterface(): void
5662
{
57-
$this->phpVersion = 80400;
63+
if (PHP_VERSION_ID < 80400) {
64+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
65+
}
5866

5967
$this->analyse([__DIR__ . '/data/properties-in-interface.php'], [
6068
[
6169
'Interfaces may only include hooked properties.',
62-
7,
70+
9,
6371
],
6472
[
6573
'Interfaces may only include hooked properties.',
66-
9,
74+
11,
6775
],
6876
]);
6977
}
7078

7179
public function testPhp84AndNonPublicPropertyHooksInInterface(): void
7280
{
73-
$this->phpVersion = 80400;
81+
if (PHP_VERSION_ID < 80400) {
82+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
83+
}
7484

7585
$this->analyse([__DIR__ . '/data/property-hooks-visibility-in-interface.php'], [
7686
[
@@ -86,7 +96,9 @@ public function testPhp84AndNonPublicPropertyHooksInInterface(): void
8696

8797
public function testPhp84AndPropertyHooksWithBodiesInInterface(): void
8898
{
89-
$this->phpVersion = 80400;
99+
if (PHP_VERSION_ID < 80400) {
100+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
101+
}
90102

91103
$this->analyse([__DIR__ . '/data/property-hooks-bodies-in-interface.php'], [
92104
[

tests/PHPStan/Rules/Properties/PropertyInClassRuleTest.php

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,66 @@
1313
class PropertyInClassRuleTest extends RuleTestCase
1414
{
1515

16-
private int $phpVersion = PHP_VERSION_ID;
17-
1816
protected function getRule(): Rule
1917
{
20-
return new PropertyInClassRule(new PhpVersion($this->phpVersion));
18+
return new PropertyInClassRule(new PhpVersion(PHP_VERSION_ID));
19+
}
20+
21+
public function testPhpLessThan84AndHookedPropertiesInClass(): void
22+
{
23+
if (PHP_VERSION_ID >= 80400) {
24+
$this->markTestSkipped('Test requires PHP 8.3 or earlier.');
25+
}
26+
27+
$this->analyse([__DIR__ . '/data/hooked-properties-in-class.php'], [
28+
[
29+
'Property hooks in classes are supported only on PHP 8.4 and later.',
30+
7,
31+
],
32+
]);
33+
}
34+
35+
public function testPhp84AndHookedPropertiesWithoutBodiesInClass(): void
36+
{
37+
if (PHP_VERSION_ID < 80400) {
38+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
39+
}
40+
41+
$this->analyse([__DIR__ . '/data/hooked-properties-without-bodies-in-class.php'], [
42+
[
43+
'Non-abstract classes may not include hooked properties without bodies.',
44+
7,
45+
],
46+
[
47+
'Non-abstract classes may not include hooked properties without bodies.',
48+
9,
49+
],
50+
]);
2151
}
2252

2353
public function testPhp84AndNonAbstractHookedPropertiesInClass(): void
2454
{
25-
$this->phpVersion = 80400;
55+
if (PHP_VERSION_ID < 80400) {
56+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
57+
}
2658

2759
$this->analyse([__DIR__ . '/data/non-abstract-hooked-properties-in-class.php'], [
2860
[
29-
'Classes may not include hooked properties without bodies.',
61+
'Non-abstract classes may not include hooked properties without bodies.',
3062
7,
3163
],
3264
[
33-
'Classes may not include hooked properties without bodies.',
65+
'Non-abstract classes may not include hooked properties without bodies.',
3466
9,
3567
],
3668
]);
3769
}
3870

3971
public function testPhp84AndAbstractHookedPropertiesInClass(): void
4072
{
41-
$this->phpVersion = 80400;
73+
if (PHP_VERSION_ID < 80400) {
74+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
75+
}
4276

4377
$this->analyse([__DIR__ . '/data/abstract-hooked-properties-in-class.php'], [
4478
[
@@ -54,7 +88,9 @@ public function testPhp84AndAbstractHookedPropertiesInClass(): void
5488

5589
public function testPhp84AndNonAbstractHookedPropertiesInAbstractClass(): void
5690
{
57-
$this->phpVersion = 80400;
91+
if (PHP_VERSION_ID < 80400) {
92+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
93+
}
5894

5995
$this->analyse([__DIR__ . '/data/non-abstract-hooked-properties-in-abstract-class.php'], [
6096
[
@@ -70,7 +106,9 @@ public function testPhp84AndNonAbstractHookedPropertiesInAbstractClass(): void
70106

71107
public function testPhp84AndAbstractNonHookedPropertiesInAbstractClass(): void
72108
{
73-
$this->phpVersion = 80400;
109+
if (PHP_VERSION_ID < 80400) {
110+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
111+
}
74112

75113
$this->analyse([__DIR__ . '/data/abstract-non-hooked-properties-in-abstract-class.php'], [
76114
[
@@ -86,7 +124,9 @@ public function testPhp84AndAbstractNonHookedPropertiesInAbstractClass(): void
86124

87125
public function testPhp84AndAbstractHookedPropertiesWithBodies(): void
88126
{
89-
$this->phpVersion = 80400;
127+
if (PHP_VERSION_ID < 80400) {
128+
$this->markTestSkipped('Test requires PHP 8.4 or later.');
129+
}
90130

91131
$this->analyse([__DIR__ . '/data/abstract-hooked-properties-with-bodies.php'], [
92132
[
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace NonAbstractHookedPropertiesInClass;
4+
5+
class Person
6+
{
7+
public string $name {
8+
get => $this->name;
9+
set => $this->name = $value;
10+
}
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace HookedPropertiesWithoutBodiesInClass;
4+
5+
class AbstractPerson
6+
{
7+
public string $name { get; set; }
8+
9+
public string $lastName { get; set; }
10+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
interface HelloWorld
66
{
7+
public string $name { get; }
8+
79
public \DateTimeInterface $dateTime;
810

911
public static \Closure $callable;

0 commit comments

Comments
 (0)