Skip to content

Commit 03d73a9

Browse files
committed
VariableCertaintyInIssetRule - do not check always-defined variables in global scope
1 parent 99f4195 commit 03d73a9

File tree

4 files changed

+166
-109
lines changed

4 files changed

+166
-109
lines changed

src/Rules/Variables/VariableCertaintyInIssetRule.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ public function processNode(Node $node, Scope $scope): array
5050

5151
$certainty = $scope->hasVariableType($var->name);
5252
if ($certainty->no()) {
53-
$messages[] = sprintf('Variable $%s in isset() is never defined.', $var->name);
53+
if (
54+
$scope->getFunction() !== null
55+
|| $scope->isInAnonymousFunction()
56+
) {
57+
$messages[] = sprintf('Variable $%s in isset() is never defined.', $var->name);
58+
}
5459
} elseif ($certainty->yes() && !$isSubNode) {
5560
$variableType = $scope->getVariableType($var->name);
5661
if (!$variableType->accepts(new NullType())) {

tests/PHPStan/Rules/Variables/VariableCertaintyInIssetRuleTest.php

+31-13
Original file line numberDiff line numberDiff line change
@@ -15,55 +15,73 @@ public function testVariableCertaintyInIsset()
1515
$this->analyse([__DIR__ . '/data/variable-certainty-isset.php'], [
1616
[
1717
'Variable $alwaysDefinedNotNullable in isset() always exists and is not nullable.',
18-
11,
18+
14,
1919
],
2020
[
2121
'Variable $neverDefinedVariable in isset() is never defined.',
22-
19,
22+
22,
2323
],
2424
[
2525
'Variable $anotherNeverDefinedVariable in isset() is never defined.',
26-
39,
26+
42,
2727
],
2828
[
2929
'Variable $yetAnotherNeverDefinedVariable in isset() is never defined.',
30-
43,
30+
46,
3131
],
3232
[
3333
'Variable $yetYetAnotherNeverDefinedVariableInIsset in isset() is never defined.',
34-
53,
34+
56,
3535
],
3636
[
3737
'Variable $anotherVariableInDoWhile in isset() always exists and is not nullable.',
38-
101,
38+
104,
3939
],
4040
[
4141
'Variable $variableInSecondCase in isset() is never defined.',
42-
107,
42+
110,
4343
],
4444
[
4545
'Variable $variableInFirstCase in isset() always exists and is not nullable.',
46-
109,
46+
112,
4747
],
4848
[
4949
'Variable $variableInFirstCase in isset() always exists and is not nullable.',
50-
113,
50+
116,
5151
],
5252
[
5353
'Variable $variableInSecondCase in isset() always exists and is not nullable.',
54-
114,
54+
117,
5555
],
5656
[
5757
'Variable $variableAssignedInSecondCase in isset() is never defined.',
58-
116,
58+
119,
5959
],
6060
[
6161
'Variable $alwaysDefinedForSwitchCondition in isset() always exists and is not nullable.',
62-
136,
62+
139,
6363
],
6464
[
6565
'Variable $alwaysDefinedForCaseNodeCondition in isset() always exists and is not nullable.',
66-
137,
66+
140,
67+
],
68+
[
69+
'Variable $alwaysDefinedNotNullable in isset() always exists and is not nullable.',
70+
152,
71+
],
72+
[
73+
'Variable $neverDefinedVariable in isset() is never defined.',
74+
152,
75+
],
76+
]);
77+
}
78+
79+
public function testIssetInGlobalScope()
80+
{
81+
$this->analyse([__DIR__ . '/data/isset-global-scope.php'], [
82+
[
83+
'Variable $alwaysDefinedNotNullable in isset() always exists and is not nullable.',
84+
8,
6785
],
6886
]);
6987
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
$alwaysDefinedNotNullable = 'string';
4+
if (doFoo()) {
5+
$sometimesDefinedVariable = 1;
6+
}
7+
8+
if (isset(
9+
$alwaysDefinedNotNullable, // always true
10+
$sometimesDefinedVariable, // fine, this is what's isset() is for
11+
$neverDefinedVariable // always false - do not report in global scope
12+
)) {
13+
14+
}
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,159 @@
11
<?php
22

3-
/** @var string|null $alwaysDefinedNullable */
4-
$alwaysDefinedNullable = doFoo();
3+
function foo()
4+
{
55

6-
if (isset($alwaysDefinedNullable)) { // fine, checking for nullability
6+
/** @var string|null $alwaysDefinedNullable */
7+
$alwaysDefinedNullable = doFoo();
78

8-
}
9+
if (isset($alwaysDefinedNullable)) { // fine, checking for nullability
910

10-
$alwaysDefinedNotNullable = 'string';
11-
if (isset($alwaysDefinedNotNullable)) { // always true
11+
}
1212

13-
}
13+
$alwaysDefinedNotNullable = 'string';
14+
if (isset($alwaysDefinedNotNullable)) { // always true
1415

15-
if (doFoo()) {
16-
$sometimesDefinedVariable = 1;
17-
}
16+
}
1817

19-
if (isset(
20-
$sometimesDefinedVariable, // fine, this is what's isset() is for
21-
$neverDefinedVariable // always false
22-
)) {
18+
if (doFoo()) {
19+
$sometimesDefinedVariable = 1;
20+
}
2321

24-
}
22+
if (isset(
23+
$sometimesDefinedVariable, // fine, this is what's isset() is for
24+
$neverDefinedVariable // always false
25+
)) {
2526

26-
/** @var string|null $anotherAlwaysDefinedNullable */
27-
$anotherAlwaysDefinedNullable = doFoo();
27+
}
2828

29-
if (isset($anotherAlwaysDefinedNullable['test']['test'])) { // fine, checking for nullability
29+
/** @var string|null $anotherAlwaysDefinedNullable */
30+
$anotherAlwaysDefinedNullable = doFoo();
3031

31-
}
32+
if (isset($anotherAlwaysDefinedNullable['test']['test'])) { // fine, checking for nullability
3233

33-
$anotherAlwaysDefinedNotNullable = 'string';
34+
}
3435

35-
if (isset($anotherAlwaysDefinedNotNullable['test']['test'])) { // fine, variable always exists, but what about the array index?
36+
$anotherAlwaysDefinedNotNullable = 'string';
3637

37-
}
38+
if (isset($anotherAlwaysDefinedNotNullable['test']['test'])) { // fine, variable always exists, but what about the array index?
3839
39-
if (isset($anotherNeverDefinedVariable['test']['test']->test['test']['test'])) { // always false
40+
}
4041

41-
}
42+
if (isset($anotherNeverDefinedVariable['test']['test']->test['test']['test'])) { // always false
4243

43-
if (isset($yetAnotherNeverDefinedVariable::$test['test'])) { // always false
44+
}
4445

45-
}
46+
if (isset($yetAnotherNeverDefinedVariable::$test['test'])) { // always false
4647

47-
if (isset($_COOKIE['test'])) { // fine
48+
}
4849

49-
}
50+
if (isset($_COOKIE['test'])) { // fine
5051

51-
if (something()) {
52+
}
5253

53-
} elseif (isset($yetYetAnotherNeverDefinedVariableInIsset)) { // always false
54+
if (something()) {
5455

55-
}
56+
} elseif (isset($yetYetAnotherNeverDefinedVariableInIsset)) { // always false
5657

57-
if (doFoo()) {
58-
$yetAnotherVariableThatSometimesExists = 1;
59-
}
58+
}
6059

61-
if (something()) {
60+
if (doFoo()) {
61+
$yetAnotherVariableThatSometimesExists = 1;
62+
}
6263

63-
} elseif (isset($yetAnotherVariableThatSometimesExists)) { // fine
64+
if (something()) {
6465

65-
}
66+
} elseif (isset($yetAnotherVariableThatSometimesExists)) { // fine
6667

67-
/** @var string|null $nullableVariableUsedInTernary */
68-
$nullableVariableUsedInTernary = doFoo();
69-
echo isset($nullableVariableUsedInTernary) ? 'foo' : 'bar'; // fine
68+
}
7069

71-
/** @var int|null $forVariableInit */
72-
$forVariableInit = doFoo();
70+
/** @var string|null $nullableVariableUsedInTernary */
71+
$nullableVariableUsedInTernary = doFoo();
72+
echo isset($nullableVariableUsedInTernary) ? 'foo' : 'bar'; // fine
7373

74-
/** @var int|null $forVariableCond */
75-
$forVariableCond = doFoo();
74+
/** @var int|null $forVariableInit */
75+
$forVariableInit = doFoo();
7676

77-
/** @var int|null $forVariableLoop */
78-
$forVariableLoop = doFoo();
77+
/** @var int|null $forVariableCond */
78+
$forVariableCond = doFoo();
7979

80-
for ($i = 0, $init = isset($forVariableInit); $i < 10 && isset($forVariableCond); $i++, $loop = isset($forVariableLoop)) {
80+
/** @var int|null $forVariableLoop */
81+
$forVariableLoop = doFoo();
8182

82-
}
83+
for ($i = 0, $init = isset($forVariableInit); $i < 10 && isset($forVariableCond); $i++, $loop = isset($forVariableLoop)) {
8384

84-
if (something()) {
85-
$variableInWhile = 1;
86-
}
85+
}
8786

88-
while (isset($variableInWhile)) {
89-
unset($variableInWhile);
90-
}
87+
if (something()) {
88+
$variableInWhile = 1;
89+
}
9190

92-
if (something()) {
93-
$variableInDoWhile = 1;
94-
}
91+
while (isset($variableInWhile)) {
92+
unset($variableInWhile);
93+
}
9594

96-
do {
97-
$anotherVariableInDoWhile = 1;
98-
echo isset($yetAnotherVariableInDoWhile); // fine
99-
} while (
100-
isset($variableInDoWhile) // fine
101-
&& isset($anotherVariableInDoWhile) // always defined
102-
&& ($yetAnotherVariableInDoWhile = 1)
103-
);
104-
105-
switch (true) {
106-
case $variableInFirstCase = true:
107-
isset($variableInSecondCase); // does not exist yet
108-
case $variableInSecondCase = true:
109-
isset($variableInFirstCase); // always defined
110-
$variableAssignedInSecondCase = true;
111-
break;
112-
case whatever():
113-
isset($variableInFirstCase); // always defined
114-
isset($variableInSecondCase); // always defined
115-
$variableInFallthroughCase = true;
116-
isset($variableAssignedInSecondCase); // surely undefined
117-
case foo():
118-
isset($variableInFallthroughCase); // fine
119-
default:
95+
if (something()) {
96+
$variableInDoWhile = 1;
97+
}
12098

121-
}
99+
do {
100+
$anotherVariableInDoWhile = 1;
101+
echo isset($yetAnotherVariableInDoWhile); // fine
102+
} while (
103+
isset($variableInDoWhile) // fine
104+
&& isset($anotherVariableInDoWhile) // always defined
105+
&& ($yetAnotherVariableInDoWhile = 1)
106+
);
122107

123-
if (foo()) {
124-
$mightBeUndefinedForSwitchCondition = 1;
125-
$mightBeUndefinedForCaseNodeCondition = 1;
126-
}
108+
switch (true) {
109+
case $variableInFirstCase = true:
110+
isset($variableInSecondCase); // does not exist yet
111+
case $variableInSecondCase = true:
112+
isset($variableInFirstCase); // always defined
113+
$variableAssignedInSecondCase = true;
114+
break;
115+
case whatever():
116+
isset($variableInFirstCase); // always defined
117+
isset($variableInSecondCase); // always defined
118+
$variableInFallthroughCase = true;
119+
isset($variableAssignedInSecondCase); // surely undefined
120+
case foo():
121+
isset($variableInFallthroughCase); // fine
122+
default:
127123

128-
switch (isset($mightBeUndefinedForSwitchCondition)) { // fine
129-
case isset($mightBeUndefinedForCaseNodeCondition): // fine
130-
break;
131-
}
124+
}
132125

133-
$alwaysDefinedForSwitchCondition = 1;
134-
$alwaysDefinedForCaseNodeCondition = 1;
126+
if (foo()) {
127+
$mightBeUndefinedForSwitchCondition = 1;
128+
$mightBeUndefinedForCaseNodeCondition = 1;
129+
}
130+
131+
switch (isset($mightBeUndefinedForSwitchCondition)) { // fine
132+
case isset($mightBeUndefinedForCaseNodeCondition): // fine
133+
break;
134+
}
135+
136+
$alwaysDefinedForSwitchCondition = 1;
137+
$alwaysDefinedForCaseNodeCondition = 1;
138+
139+
switch (isset($alwaysDefinedForSwitchCondition)) {
140+
case isset($alwaysDefinedForCaseNodeCondition):
141+
break;
142+
}
135143

136-
switch (isset($alwaysDefinedForSwitchCondition)) {
137-
case isset($alwaysDefinedForCaseNodeCondition):
138-
break;
139144
}
145+
146+
function () {
147+
$alwaysDefinedNotNullable = 'string';
148+
if (doFoo()) {
149+
$sometimesDefinedVariable = 1;
150+
}
151+
152+
if (isset(
153+
$alwaysDefinedNotNullable, // always true
154+
$sometimesDefinedVariable, // fine, this is what's isset() is for
155+
$neverDefinedVariable // always false
156+
)) {
157+
158+
}
159+
};

0 commit comments

Comments
 (0)