Skip to content

Commit 3d52930

Browse files
committed
Fixed false positive about undefined property guarded with property_exists
1 parent a2834bc commit 3d52930

File tree

4 files changed

+59
-1
lines changed

4 files changed

+59
-1
lines changed

src/Rules/Properties/AccessPropertiesCheck.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
namespace PHPStan\Rules\Properties;
44

5+
use PhpParser\Node\Arg;
56
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Expr\FuncCall;
68
use PhpParser\Node\Expr\PropertyFetch;
79
use PhpParser\Node\Identifier;
10+
use PhpParser\Node\Name\FullyQualified;
811
use PHPStan\Analyser\NullsafeOperatorHelper;
912
use PHPStan\Analyser\Scope;
1013
use PHPStan\Internal\SprintfHelper;
@@ -143,6 +146,17 @@ private function processSingleProperty(Scope $scope, PropertyFetch $node, string
143146
}
144147
}
145148

149+
if ($node->name instanceof Expr) {
150+
$propertyExistsExpr = new FuncCall(new FullyQualified('property_exists'), [
151+
new Arg($node->var),
152+
new Arg($node->name),
153+
]);
154+
155+
if ($scope->getType($propertyExistsExpr)->isTrue()->yes()) {
156+
return [];
157+
}
158+
}
159+
146160
$ruleErrorBuilder = RuleErrorBuilder::message(sprintf(
147161
'Access to an undefined property %s::$%s.',
148162
$typeForDescribe->describe(VerbosityLevel::typeOnly()),

src/Type/Php/PropertyExistsTypeSpecifyingExtension.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PhpParser\Node\Expr\FuncCall;
66
use PhpParser\Node\Expr\PropertyFetch;
77
use PhpParser\Node\Identifier;
8+
use PhpParser\Node\Name\FullyQualified;
89
use PHPStan\Analyser\Scope;
910
use PHPStan\Analyser\SpecifiedTypes;
1011
use PHPStan\Analyser\TypeSpecifier;
@@ -13,6 +14,7 @@
1314
use PHPStan\Reflection\FunctionReflection;
1415
use PHPStan\Rules\Properties\PropertyReflectionFinder;
1516
use PHPStan\Type\Accessory\HasPropertyType;
17+
use PHPStan\Type\Constant\ConstantBooleanType;
1618
use PHPStan\Type\Constant\ConstantStringType;
1719
use PHPStan\Type\FunctionTypeSpecifyingExtension;
1820
use PHPStan\Type\IntersectionType;
@@ -53,7 +55,12 @@ public function specifyTypes(
5355
{
5456
$propertyNameType = $scope->getType($node->getArgs()[1]->value);
5557
if (!$propertyNameType instanceof ConstantStringType) {
56-
return new SpecifiedTypes([], []);
58+
return $this->typeSpecifier->create(
59+
new FuncCall(new FullyQualified('property_exists'), $node->getRawArgs()),
60+
new ConstantBooleanType(true),
61+
$context,
62+
$scope,
63+
);
5764
}
5865

5966
if ($propertyNameType->getValue() === '') {

tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,4 +1035,12 @@ public function testNewIsAlwaysFinalClass(): void
10351035
]);
10361036
}
10371037

1038+
public function testPropertyExists(): void
1039+
{
1040+
$this->checkThisOnly = false;
1041+
$this->checkUnionTypes = true;
1042+
$this->checkDynamicProperties = true;
1043+
$this->analyse([__DIR__ . '/data/property-exists.php'], []);
1044+
}
1045+
10381046
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace PropertyExists;
4+
5+
class Model
6+
{
7+
8+
}
9+
10+
class Defaults
11+
{
12+
public function defaults(Model $model): void
13+
{
14+
$columns = [
15+
'getCreatedByColumn',
16+
'getUpdatedByColumn',
17+
'getDeletedByColumn',
18+
'getCreatedAtColumn',
19+
'getUpdatedAtColumn',
20+
'getDeletedAtColumn',
21+
];
22+
23+
foreach ($columns as $column) {
24+
if (property_exists($model, $column)) {
25+
echo $model->{$column};
26+
}
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy