Skip to content

Commit b22512d

Browse files
Fix phpstan/phpstan#13539: property.notFound in chained isset() with checkDynamicProperties (#5562)
1 parent 1e062f1 commit b22512d

5 files changed

Lines changed: 80 additions & 3 deletions

File tree

src/Rules/Properties/AccessPropertiesCheck.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ private function processSingleProperty(Scope $scope, PropertyFetch $node, string
140140
return [];
141141
}
142142

143+
if ($type->getObjectClassNames() === []) {
144+
return [];
145+
}
146+
143147
$maybePropertyReflection = $this->pickProperty($scope, $type, $name);
144148
if ($maybePropertyReflection !== null && $maybePropertyReflection->isDummy()->no()) {
145149
return [];

tests/PHPStan/Analyser/AnalyserWithCheckDynamicPropertiesTest.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ class AnalyserWithCheckDynamicPropertiesTest extends PHPStanTestCase
1212
public function testBug13529(): void
1313
{
1414
$errors = $this->runAnalyse(__DIR__ . '/data/bug-13529.php');
15-
$this->assertCount(1, $errors);
16-
$this->assertSame('Access to an undefined property object::$bar.', $errors[0]->getMessage());
17-
$this->assertSame(8, $errors[0]->getLine());
15+
$this->assertCount(0, $errors);
1816
}
1917

2018
/**

tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,4 +1272,26 @@ public function testBug13537(): void
12721272
$this->analyse([__DIR__ . '/data/bug-13537.php'], $errors);
12731273
}
12741274

1275+
public function testBug12390(): void
1276+
{
1277+
$this->checkThisOnly = false;
1278+
$this->checkUnionTypes = true;
1279+
$this->checkDynamicProperties = true;
1280+
$this->analyse([__DIR__ . '/data/bug-12390.php'], []);
1281+
}
1282+
1283+
public function testBug13539(): void
1284+
{
1285+
$this->checkThisOnly = false;
1286+
$this->checkUnionTypes = true;
1287+
$this->checkDynamicProperties = true;
1288+
$this->analyse([__DIR__ . '/data/bug-13539.php'], [
1289+
[
1290+
'Access to an undefined property object::$baz.',
1291+
29,
1292+
'Learn more: <fg=cyan>https://phpstan.org/blog/solving-phpstan-access-to-undefined-property</>',
1293+
],
1294+
]);
1295+
}
1296+
12751297
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
function testIssetOnGenericObject(object $obj): void
4+
{
5+
if (isset($obj->foo)) {
6+
}
7+
}
8+
9+
function testChainedIssetOnGenericObject(object $obj): void
10+
{
11+
if (isset($obj->foo) && isset($obj->bar)) {
12+
}
13+
}
14+
15+
function testIssetAfterIsObjectNarrowing(string $date): void
16+
{
17+
if (is_object($obj = json_decode($date))) {
18+
if (isset($obj->crashId) && is_string($obj->crashId)) {
19+
var_dump($obj->crashId);
20+
}
21+
}
22+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
function broken(string $x): void {
4+
$tmp = json_decode($x, false);
5+
if (!isset($tmp->foo) || !isset($tmp->bar)) {
6+
}
7+
}
8+
9+
function works(string $x): void {
10+
$tmp = json_decode($x, false);
11+
if (!isset($tmp->foo, $tmp->bar)) {
12+
}
13+
}
14+
15+
function works_too(string $x): void {
16+
/** @var stdClass $tmp */
17+
$tmp = json_decode($x, false);
18+
if (!isset($tmp->foo) || !isset($tmp->bar)) {
19+
}
20+
}
21+
22+
/**
23+
* @param mixed $tmp
24+
*/
25+
function also_ok($tmp): void {
26+
if (isset($tmp->foo) && isset($tmp->bar)) {
27+
echo $tmp->foo;
28+
echo $tmp->bar;
29+
echo $tmp->baz; // intentional: baz not checked by isset
30+
}
31+
}

0 commit comments

Comments
 (0)