Skip to content

Commit cf809ba

Browse files
committed
Improve property access checking with property tag support
1 parent 42eac28 commit cf809ba

File tree

3 files changed

+46
-6
lines changed

3 files changed

+46
-6
lines changed

src/Rules/Properties/AccessPropertiesCheck.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
use PHPStan\Type\StaticType;
2424
use PHPStan\Type\Type;
2525
use PHPStan\Type\VerbosityLevel;
26+
use function array_keys;
2627
use function array_map;
2728
use function array_merge;
2829
use function count;
30+
use function in_array;
2931
use function sprintf;
3032

3133
#[AutowiredService]
@@ -125,9 +127,11 @@ private function processSingleProperty(Scope $scope, PropertyFetch $node, string
125127
}
126128
}
127129

130+
$propertyTags = [];
128131
if (count($classNames) === 1) {
129132
$propertyClassReflection = $this->reflectionProvider->getClass($classNames[0]);
130133
$parentClassReflection = $propertyClassReflection->getParentClass();
134+
$propertyTags = $propertyClassReflection->getPropertyTags();
131135
while ($parentClassReflection !== null) {
132136
if ($parentClassReflection->hasProperty($name)) {
133137
if ($write) {
@@ -146,11 +150,15 @@ private function processSingleProperty(Scope $scope, PropertyFetch $node, string
146150
))->identifier('property.private')->build(),
147151
];
148152
}
149-
153+
$propertyTags += $parentClassReflection->getPropertyTags();
150154
$parentClassReflection = $parentClassReflection->getParentClass();
151155
}
152156
}
153157

158+
if (in_array($name, array_keys($propertyTags), true)) {
159+
return [];
160+
}
161+
154162
if ($node->name instanceof Expr) {
155163
$propertyExistsExpr = new FuncCall(new FullyQualified('property_exists'), [
156164
new Arg($node->var),

tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,38 @@ public function testPropertyExists(): void
10191019
$this->checkThisOnly = false;
10201020
$this->checkUnionTypes = true;
10211021
$this->checkDynamicProperties = true;
1022-
$this->analyse([__DIR__ . '/data/property-exists.php'], []);
1022+
$this->analyse([__DIR__ . '/data/property-exists.php'], [
1023+
[
1024+
'Access to an undefined property PropertyExists\Model::$getCreatedByColumn.',
1025+
27,
1026+
'Learn more: <fg=cyan>https://phpstan.org/blog/solving-phpstan-access-to-undefined-property</>',
1027+
],
1028+
[
1029+
'Access to an undefined property PropertyExists\Model::$getUpdatedByColumn.',
1030+
27,
1031+
'Learn more: <fg=cyan>https://phpstan.org/blog/solving-phpstan-access-to-undefined-property</>',
1032+
],
1033+
[
1034+
'Access to an undefined property PropertyExists\Model::$getDeletedByColumn.',
1035+
27,
1036+
'Learn more: <fg=cyan>https://phpstan.org/blog/solving-phpstan-access-to-undefined-property</>',
1037+
],
1038+
[
1039+
'Access to an undefined property PropertyExists\Model::$getCreatedAtColumn.',
1040+
27,
1041+
'Learn more: <fg=cyan>https://phpstan.org/blog/solving-phpstan-access-to-undefined-property</>',
1042+
],
1043+
[
1044+
'Access to an undefined property PropertyExists\Model::$getUpdatedAtColumn.',
1045+
27,
1046+
'Learn more: <fg=cyan>https://phpstan.org/blog/solving-phpstan-access-to-undefined-property</>',
1047+
],
1048+
[
1049+
'Access to an undefined property PropertyExists\Model::$getDeletedAtColumn.',
1050+
27,
1051+
'Learn more: <fg=cyan>https://phpstan.org/blog/solving-phpstan-access-to-undefined-property</>',
1052+
],
1053+
]);
10231054
}
10241055

10251056
}

tests/PHPStan/Rules/Properties/data/property-exists.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22

33
namespace PropertyExists;
44

5+
/**
6+
* @property-read \stdClass $getCreator
7+
*/
58
class Model
69
{
7-
810
}
911

1012
class Defaults
1113
{
1214
public function defaults(Model $model): void
1315
{
1416
$columns = [
17+
'getCreator',
1518
'getCreatedByColumn',
1619
'getUpdatedByColumn',
1720
'getDeletedByColumn',
@@ -21,9 +24,7 @@ public function defaults(Model $model): void
2124
];
2225

2326
foreach ($columns as $column) {
24-
if (property_exists($model, $column)) {
25-
echo $model->{$column};
26-
}
27+
echo $model->{$column};
2728
}
2829
}
2930
}

0 commit comments

Comments
 (0)