Skip to content

Commit 879bcdc

Browse files
authored
EnforceReadonlyPublicProperty: exclude hooked properties (#303)
1 parent c3548a7 commit 879bcdc

File tree

3 files changed

+72
-6
lines changed

3 files changed

+72
-6
lines changed

src/Rule/EnforceReadonlyPublicPropertyRule.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,11 @@ public function processNode(Node $node, Scope $scope): array
3838
return [];
3939
}
4040

41-
if (!$node->isPublic() || $node->isReadOnly()) {
41+
if (!$node->isPublic() || $node->isReadOnly() || $node->hasHooks()) {
4242
return [];
4343
}
4444

45-
$classReflection = $scope->getClassReflection();
46-
47-
if ($classReflection === null) {
48-
return [];
49-
}
45+
$classReflection = $node->getClassReflection();
5046

5147
if (($classReflection->getNativeReflection()->getModifiers() & 65_536) !== 0) { // readonly class, since PHP 8.2
5248
return [];

tests/Rule/EnforceReadonlyPublicPropertyRuleTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPStan\Php\PhpVersion;
66
use PHPStan\Rules\Rule;
77
use ShipMonk\PHPStan\RuleTestCase;
8+
use const PHP_VERSION_ID;
89

910
/**
1011
* @extends RuleTestCase<EnforceReadonlyPublicPropertyRule>
@@ -20,6 +21,16 @@ protected function getRule(): Rule
2021
return new EnforceReadonlyPublicPropertyRule($this->phpVersion);
2122
}
2223

24+
public function testPhp84(): void
25+
{
26+
if (PHP_VERSION_ID < 8_00_00) {
27+
self::markTestSkipped('PHP7 parser fails with property hooks');
28+
}
29+
30+
$this->phpVersion = $this->createPhpVersion(80_400);
31+
$this->analyseFile(__DIR__ . '/data/EnforceReadonlyPublicPropertyRule/code-84.php');
32+
}
33+
2334
public function testPhp81(): void
2435
{
2536
$this->phpVersion = $this->createPhpVersion(80_100);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace EnforceReadonlyPublicPropertyRule84;
4+
5+
trait MyTrait {
6+
7+
public ?string $public; // error: Public property `public` not marked as readonly.
8+
9+
public ?string $hooked { get => $this->hooked; }
10+
11+
public readonly string $publicReadonly;
12+
13+
protected string $protected;
14+
15+
private string $private;
16+
17+
}
18+
19+
class MyClass {
20+
21+
use MyTrait;
22+
23+
public ?int $foo; // error: Public property `foo` not marked as readonly.
24+
25+
public ?string $classHooked { set => strtolower($value); }
26+
27+
public readonly int $bar;
28+
29+
protected int $baz;
30+
31+
private int $bag;
32+
33+
}
34+
35+
readonly class MyReadonlyClass {
36+
37+
public ?int $foo;
38+
39+
public readonly int $bar;
40+
41+
public ?string $hooked { get => $this->hooked; }
42+
43+
protected int $baz;
44+
45+
private int $bag;
46+
47+
}
48+
49+
interface MyInterface {
50+
public string $key { get; }
51+
}
52+
53+
class ImplementingClass implements MyInterface {
54+
public string $key; // error: Public property `key` not marked as readonly.
55+
}
56+
57+
class ImplementingClass2 implements MyInterface {
58+
public readonly string $key;
59+
}

0 commit comments

Comments
 (0)