A closure that captures by reference mutates a copy, so the change is invisible afterwards:
phunkie > $calls = 0
$calls: Int = 0
phunkie > $bump = function () use (&$calls) { $calls = $calls + 1; return $calls; }
$bump: Callable = <function>
phunkie > $bump()
$var0: Int = 1
phunkie > $calls
$var1: Int = 0 <-- expected Int = 1
The closure sees its own incremented value (it returns 1), but the session variable is untouched. Session variables live in an immutable ImmMap on ReplSession, so there is no storage location for &$calls to alias.
Mutating an object works, because the reference is to the object rather than to the binding:
phunkie > $c = new stdClass(); $c->n = 0
phunkie > $f = function () use ($c) { $c->n = $c->n + 1; }
phunkie > $f(); $c->n
$var2: Int = 1
Reported rather than fixed because the fix is a design question, not a patch: either by-ref capture is genuinely supported (which needs a mutable cell behind session variables, in tension with the immutable session), or it is rejected with a clear message instead of silently doing nothing. Silently discarding the write is the worst of the three.
Found while writing features/repl/php8.5/void_cast.feature, where the scenario had to be rewritten to observe a side effect through an object instead.
A closure that captures by reference mutates a copy, so the change is invisible afterwards:
The closure sees its own incremented value (it returns 1), but the session variable is untouched. Session variables live in an immutable
ImmMaponReplSession, so there is no storage location for&$callsto alias.Mutating an object works, because the reference is to the object rather than to the binding:
Reported rather than fixed because the fix is a design question, not a patch: either by-ref capture is genuinely supported (which needs a mutable cell behind session variables, in tension with the immutable session), or it is rejected with a clear message instead of silently doing nothing. Silently discarding the write is the worst of the three.
Found while writing
features/repl/php8.5/void_cast.feature, where the scenario had to be rewritten to observe a side effect through an object instead.