A PHP closure recursive function cannot call itself through a variable captured with use ($var), because use copies the variable's value at the moment the closure is defined — not when it runs. The fix is to capture by reference with use (&$var).
TL;DR: change use ($var1) to use (&$var1). Everything below explains why that single & matters, what error you get without it, and which alternatives avoid the reference entirely.
What is the closure of a function?

A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables. For example, the adder function returns a closure. Each closure is bound to its own sum variable. Ref
PHP differs from JavaScript here in one important way. In JavaScript, a nested function looks up outer variables when it runs. In PHP, you must declare what you want to capture with use, and by default PHP copies the value at the moment the closure is created. That timing difference is the entire source of this bug.
Why a PHP closure recursive function fails
The below code in PHP will throw Error:

$var1 = "test";
$var1 = static function($num) use ($var1) {
if ($num < 1) return 1;
return $var1($num - 1);
};
$var1(5);
echo "ok";
Trace the order of operations and the failure becomes obvious:
$var1is assigned the string"test".- The closure is created. At this instant,
use ($var1)copies the current value — the string"test"— into the closure's scope. $var1is then reassigned to point at the closure itself. The copy already taken is unaffected.- Calling
$var1(5)runs the closure, which tries to invoke its captured copy — still the string"test".
On PHP 8 this raises Error: Value of type string is not callable. On PHP 7 you would instead see Fatal error: Function name must be a string. Either way the closure never sees itself.
The Solution
To pass $var1 by reference, so that it will point to last definition of $var1 which is closure function instead of string.
$var1 = "test";
$var1 = static function($num) use (&$var1) {
if ($num < 1) return 1;
return $var1($num - 1);
};
$var1(5);
echo "ok";

With &, the closure holds a reference to the variable slot rather than a snapshot of its contents. When step 3 reassigns $var1, the closure's reference follows along and resolves to the closure itself at call time.
Alternatives that avoid the reference
Capturing by reference creates a cycle: the variable points at the closure, and the closure points back at the variable. PHP's cycle collector will clean this up, but the memory is not freed the moment the variable leaves scope. If that matters, two alternatives sidestep the problem:
Pass the closure to itself as an argument. No capture, no cycle:
$fact = static function($self, $num) {
if ($num < 1) return 1;
return $num * $self($self, $num - 1);
};
echo $fact($fact, 5); // 120
Or just use a named function. A named function is resolved at call time by the engine, so recursion works with no special handling — and it is easier to test and profile:
function factorial(int $num): int {
return $num < 1 ? 1 : $num * factorial($num - 1);
}
Note that arrow functions (fn() =>) do not help here. They capture outer variables automatically, but they capture by value, exactly like a plain use ($var).
Frequently asked questions
Why does use ($var) copy the value instead of reading it later?
PHP binds captured variables when the closure object is constructed, not when it is invoked. This is a deliberate design choice that makes a closure's behaviour independent of whatever happens to the outer scope afterwards.
Does static on the closure cause this error?
No. static only prevents the closure from binding $this. The recursion failure happens identically with or without it.
Is capturing by reference safe to use in production?
Yes. The only caveat is the reference cycle described above. For a short-lived closure it is a non-issue; inside a long-running worker that creates many such closures, prefer a named function.
For more PHP internals write-ups, see my PR merged into PHP core adding ReflectionConstant::inNamespace(). The official reference for capture semantics lives in the PHP manual on anonymous functions.
Comments