Skip to content

Commit 94c5f2f

Browse files
author
David Jelínek
committed
Allow use get property hook in interface on readonly property #(300)
1 parent 7517d6e commit 94c5f2f

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

src/Rule/EnforceReadonlyPublicPropertyRule.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ public function processNode(Node $node, Scope $scope): array
5252
return [];
5353
}
5454

55+
if ($this->phpVersion->supportsPropertyHooks()) {
56+
foreach ($node->getHooks() as $hook) {
57+
if ($hook->name->toString() === 'set') {
58+
$error = RuleErrorBuilder::message("Public property `{$node->getName()}` cannot have a setter hook, because is mark as readonly.")
59+
->identifier('shipmonk.publicPropertyNotReadonly')
60+
->build();
61+
62+
return [$error];
63+
}
64+
}
65+
66+
if ($classReflection->isInterface()) {
67+
return [];
68+
}
69+
}
70+
5571
$error = RuleErrorBuilder::message("Public property `{$node->getName()}` not marked as readonly.")
5672
->identifier('shipmonk.publicPropertyNotReadonly')
5773
->build();

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 < 80_400) {
27+
self::markTestSkipped('Test requires PHP 8.4.');
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: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace EnforceReadonlyPublicPropertyRule84;
4+
5+
interface I
6+
{
7+
public string $key { get; }
8+
public int|float|string $value { get; set; } // error: Public property `value` cannot have a setter hook, because is mark as readonly.
9+
}
10+
11+
// there is DiscriminatorMap Attribute from symfony
12+
abstract readonly class A implements I
13+
{
14+
}
15+
16+
readonly class B
17+
{
18+
public function __construct(
19+
public string $key,
20+
public int $value,
21+
) {
22+
}
23+
}
24+
readonly class C
25+
{
26+
public function __construct(
27+
public string $key,
28+
public string $value,
29+
) {
30+
}
31+
}
32+
readonly class D
33+
{
34+
public function __construct(
35+
public string $key,
36+
public int $value,
37+
) {
38+
}
39+
}
40+
41+
42+
class X
43+
{
44+
/**
45+
* @param array<A> $foo
46+
*/
47+
public function __construct(
48+
public readonly array $foo = [],
49+
) {
50+
}
51+
52+
public function getFirstKeyOfFloat(): ?string
53+
{
54+
foreach ($this->foo as $item) {
55+
if (is_float($item->value)) {
56+
return $item->key;
57+
}
58+
}
59+
return null;
60+
}
61+
}
62+

0 commit comments

Comments
 (0)