Skip to content

Commit 869f3d5

Browse files
committed
Fix ReadOnlyByPhpDocPropertyAssignRule for hooked properties
1 parent 24d0e26 commit 869f3d5

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

src/Rules/Properties/ReadOnlyByPhpDocPropertyAssignRule.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\Node\PropertyAssignNode;
88
use PHPStan\Reflection\ConstructorsHelper;
99
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection;
1011
use PHPStan\Rules\Rule;
1112
use PHPStan\Rules\RuleErrorBuilder;
1213
use PHPStan\ShouldNotHappenException;
@@ -40,6 +41,18 @@ public function processNode(Node $node, Scope $scope): array
4041
return [];
4142
}
4243

44+
$inFunction = $scope->getFunction();
45+
if (
46+
$inFunction instanceof PhpMethodFromParserNodeReflection
47+
&& $inFunction->isPropertyHook()
48+
&& $propertyFetch->var instanceof Node\Expr\Variable
49+
&& $propertyFetch->var->name === 'this'
50+
&& $propertyFetch->name instanceof Node\Identifier
51+
&& $inFunction->getHookedPropertyName() === $propertyFetch->name->toString()
52+
) {
53+
return [];
54+
}
55+
4356
$errors = [];
4457
$reflections = $this->propertyReflectionFinder->findPropertyReflectionsFromNode($propertyFetch, $scope);
4558
foreach ($reflections as $propertyReflection) {

tests/PHPStan/Rules/Properties/ReadOnlyByPhpDocPropertyAssignRuleTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,22 @@ public function testFeature11775(): void
178178
]);
179179
}
180180

181+
public function testPropertyHooks(): void
182+
{
183+
if (PHP_VERSION_ID < 80400) {
184+
$this->markTestSkipped('Test requires PHP 8.4.');
185+
}
186+
187+
$this->analyse([__DIR__ . '/data/property-hooks-readonly-by-phpdoc-assign.php'], [
188+
[
189+
'@readonly property PropertyHooksReadonlyByPhpDocAssign\Foo::$i is assigned outside of the constructor.',
190+
15,
191+
],
192+
[
193+
'@readonly property PropertyHooksReadonlyByPhpDocAssign\Foo::$j is assigned outside of the constructor.',
194+
17,
195+
],
196+
]);
197+
}
198+
181199
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php // lint >= 8.4
2+
3+
namespace PropertyHooksReadonlyByPhpDocAssign;
4+
5+
class Foo
6+
{
7+
8+
/** @readonly */
9+
public int $i {
10+
get {
11+
return $this->i + 1;
12+
}
13+
set {
14+
$self = new self();
15+
$self->i = 1;
16+
17+
$this->j = 2;
18+
$this->i = $value - 1;
19+
}
20+
}
21+
22+
/** @readonly */
23+
public int $j;
24+
25+
}

0 commit comments

Comments
 (0)