Skip to content

Commit 0162284

Browse files
phpstan-botclaude
andcommitted
Also exclude nullsafe and static access from closure scope forwarding
Handle NullsafePropertyFetch, NullsafeMethodCall, StaticPropertyFetch, and StaticCall in addition to PropertyFetch and MethodCall when entering closure scope, since these can also be modified between closure definition and invocation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7efb94b commit 0162284

2 files changed

Lines changed: 63 additions & 1 deletion

File tree

src/Analyser/MutatingScope.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2152,7 +2152,14 @@ public function enterAnonymousFunctionWithoutReflection(
21522152
}
21532153
}
21542154

2155-
if ($expr instanceof PropertyFetch || $expr instanceof MethodCall) {
2155+
if (
2156+
$expr instanceof PropertyFetch
2157+
|| $expr instanceof MethodCall
2158+
|| $expr instanceof Expr\NullsafePropertyFetch
2159+
|| $expr instanceof Expr\NullsafeMethodCall
2160+
|| $expr instanceof Expr\StaticPropertyFetch
2161+
|| $expr instanceof Expr\StaticCall
2162+
) {
21562163
continue;
21572164
}
21582165

tests/PHPStan/Rules/Arrays/data/bug-10345.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,58 @@ public function setItems(array $items): void
5959

6060
$a3 = $func3();
6161
}
62+
63+
// Nullsafe property fetch
64+
$container4 = new Foo();
65+
$container4->items = [];
66+
67+
$func4 = function() use ($container4): int {
68+
foreach ($container4?->items as $item) {}
69+
return 1;
70+
};
71+
72+
$container4->items[] = '1';
73+
74+
$a4 = $func4();
75+
76+
// Static property access
77+
class Baz {
78+
/** @var list<string> */
79+
public static array $items = [];
80+
81+
/** @return list<string> */
82+
public static function getItems(): array
83+
{
84+
return self::$items;
85+
}
86+
87+
/** @param list<string> $items */
88+
public static function setItems(array $items): void
89+
{
90+
self::$items = $items;
91+
}
92+
}
93+
94+
Baz::$items = [];
95+
96+
$func5 = function(): int {
97+
foreach (Baz::$items as $item) {}
98+
return 1;
99+
};
100+
101+
Baz::$items[] = '1';
102+
103+
$a5 = $func5();
104+
105+
// Static method call
106+
Baz::setItems([]);
107+
if (Baz::getItems() === []) {
108+
$func6 = function(): int {
109+
foreach (Baz::getItems() as $item) {}
110+
return 1;
111+
};
112+
113+
Baz::setItems(['foo']);
114+
115+
$a6 = $func6();
116+
}

0 commit comments

Comments
 (0)