Closures in Constants

Closures in Constant Expressions and Closure::getCurrent

PHP 8.5 accepts closures and first-class callables in constant expressions: constants, parameter and property defaults, and attribute arguments (Enums and Attributes). Built once, such a closure must be static and may not use variables, and arrow functions are refused; the compiler says Closures in constant expressions must be static or Cannot use(...) variables in constant expression. Also new in 8.5, Closure::getCurrent() returns the running closure, so an anonymous function can recurse without the old use (&$walk) trick.

A closure as a constant and a default value, and a recursive closurePHP
<?php
declare(strict_types=1);
const SLUG = static function (string $s): string {
  return trim(preg_replace('/[^a-z0-9]+/', '-', strtolower($s)), '-');
};
function show(string $title, Closure $format = SLUG): void { echo $format($title), "\n"; }
show('PHP 8.5 & You');
$walk = function (array $nodes, int $depth = 0): void {
  foreach ($nodes as $name => $children) {
    echo str_repeat('-', $depth), $name, ' ';
    Closure::getCurrent()($children, $depth + 1);      // recurse without a name
  }
};
$walk(['Books' => ['Fiction' => ['Crime' => []], 'Science' => []], 'Music' => []]);
echo "\n";
Output
php-8-5-you
Books -Fiction --Crime -Science Music

The default could equally be a literal static function written inline; before 8.5 it had to be ?Closure $format = null plus a null check. Outside a closure, Closure::getCurrent() throws Error: Current function is not a closure.