Hi everyone

A few years ago, an RFC was proposed to introduce special syntax for
self-referencing closures, mainly used for implementing recursion.

https://wiki.php.net/rfc/closure_self_reference

The proposed solution allows specifying a variable that will be
assigned the closure object on function entry.

$fibonacci = function (int $n) as $fn {
    if ($n === 0) return 0;
    if ($n === 1) return 1;
    return $fn($n-1) + $fn($n-2);
};

This is essentially already possible through a by-reference capture.

$fibonacci = function (int $n) use (&$fibonacci) { ... };

This cannot be a by-value capture because by the time the closure is
created and variables are bound, $fibonacci is not assigned yet. The
downside of by-reference capturing is that if $fibonacci is
accidentally reassigned, the reference within the closure is also
changed.

However, I consider a language change largely unnecessary. Instead,
this may be solved with a very simple static function:
Closure::getCurrent().

$fibonacci = function (int $n) {
    if ($n === 0) return 0;
    if ($n === 1) return 1;

    $self = Closure::getCurrent();
    return $self($n-1) + $self($n-2);

    // Or
    return Closure::getCurrent()($n-1) + Closure::getCurrent()($n-2);
};

Another suggestion was to introduce a magic constant __CLOSURE__.
However, this would be misleading, given it can refer to different
closure instances, thus not actually being constant.

You can find the implementation here:
https://github.com/php/php-src/pull/18167

I picked the name Closure::getCurrent() to be consistent with
Fiber::getCurrent(). Contrary to fibers, calling Closure::getCurrent()
from non-closures is nonsensical and thus disallowed, resulting in an
Error exception. It will only succeed when called directly from
closures. It will not recurse the call stack.

Do you have thoughts or concerns with this solution? If not, I'm
planning to merge it into master in a few weeks.

Ilija

Reply via email to