Skip to content

Commit 70177a5

Browse files
phpstan-botclaude
authored 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 c70d58c commit 70177a5

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
@@ -2080,7 +2080,14 @@ public function enterAnonymousFunctionWithoutReflection(
20802080
}
20812081
}
20822082

2083-
if ($expr instanceof PropertyFetch || $expr instanceof MethodCall) {
2083+
if (
2084+
$expr instanceof PropertyFetch
2085+
|| $expr instanceof MethodCall
2086+
|| $expr instanceof Expr\NullsafePropertyFetch
2087+
|| $expr instanceof Expr\NullsafeMethodCall
2088+
|| $expr instanceof Expr\StaticPropertyFetch
2089+
|| $expr instanceof Expr\StaticCall
2090+
) {
20842091
continue;
20852092
}
20862093

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)