Skip to content

Commit 9660125

Browse files
phpstan-botclaude
andcommitted
Add test for isset in methods called from constructor
Tests that isset() on properties without defaults in methods called from the constructor does not produce false positives, while isset() on properties with defaults or in non-constructor-called methods still correctly reports errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cd5f31f commit 9660125

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

tests/PHPStan/Rules/Variables/IssetRuleTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,26 @@ public function testBug13473(): void
543543
]);
544544
}
545545

546+
public function testIssetMethodCalledFromConstructor(): void
547+
{
548+
$this->treatPhpDocTypesAsCertain = true;
549+
550+
$this->analyse([__DIR__ . '/data/isset-method-called-from-constructor.php'], [
551+
[
552+
'Property IssetMethodCalledFromConstructor\MethodCalledFromConstructorWithDefault::$bar in isset() is not nullable nor uninitialized.',
553+
34,
554+
],
555+
[
556+
'Property IssetMethodCalledFromConstructor\MethodNotCalledFromConstructor::$bar in isset() is not nullable nor uninitialized.',
557+
51,
558+
],
559+
[
560+
'Property IssetMethodCalledFromConstructor\MultipleProperties::$bar in isset() is not nullable nor uninitialized.',
561+
72,
562+
],
563+
]);
564+
}
565+
546566
public function testBug14393(): void
547567
{
548568
$this->treatPhpDocTypesAsCertain = true;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace IssetMethodCalledFromConstructor;
6+
7+
final class MethodCalledFromConstructor {
8+
private int $bar;
9+
10+
public function __construct(int $bar)
11+
{
12+
$this->setBar($bar);
13+
}
14+
15+
private function setBar(int $bar): void
16+
{
17+
if (isset($this->bar)) { // $bar has no default, could be uninitialized when called from constructor - no error
18+
throw new \Exception('bar is set');
19+
}
20+
$this->bar = $bar;
21+
}
22+
}
23+
24+
final class MethodCalledFromConstructorWithDefault {
25+
private int $bar = 1;
26+
27+
public function __construct(int $bar)
28+
{
29+
$this->setBar($bar);
30+
}
31+
32+
private function setBar(int $bar): void
33+
{
34+
if (isset($this->bar)) { // $bar has default value, always initialized - should error
35+
throw new \Exception('bar is set');
36+
}
37+
$this->bar = $bar;
38+
}
39+
}
40+
41+
final class MethodNotCalledFromConstructor {
42+
private int $bar;
43+
44+
public function __construct(int $bar)
45+
{
46+
$this->bar = $bar;
47+
}
48+
49+
private function checkBar(): void
50+
{
51+
if (isset($this->bar)) { // Not called from constructor, property is initialized after construction - should error
52+
echo 'bar is set';
53+
}
54+
}
55+
}
56+
57+
final class MultipleProperties {
58+
private int $foo;
59+
private int $bar = 5;
60+
61+
public function __construct(int $bar)
62+
{
63+
$this->init($bar);
64+
$this->foo = 42;
65+
}
66+
67+
private function init(int $bar): void
68+
{
69+
if (isset($this->foo)) { // $foo has no default, could be uninitialized - no error
70+
throw new \Exception('foo is set');
71+
}
72+
if (isset($this->bar)) { // $bar has default value, always initialized - should error
73+
echo 'bar is set';
74+
}
75+
$this->bar = $bar;
76+
}
77+
}

0 commit comments

Comments
 (0)