Some places in PHP only accept constant expressions, meaning values PHP can work out at compile time. Attribute arguments are one example, and default values for parameters and properties are another. Closures weren't allowed in any of them until this RFC from Tim Düsterhus and Volker Dusch.

Show me

Here is a trimmed version of the RFC's first example. The default callback is a closure, so the parameter doesn't need to be nullable:

function my_array_filter(
    array $array,
    Closure $callback = static function ($item) { return !empty($item); },
) {
    // ...
}

It works in attributes too. Here is the RFC's example for a validation library:

final class Locale
{
    #[Validator\Custom(static function (string $languageCode): bool {
        return \preg_match('/^[a-z][a-z]$/', $languageCode);
    })]
    public string $languageCode;
}

You can use closures in:

  • Attribute arguments
  • Default values of properties and parameters
  • Constants and class constants

They can also be nested inside other expressions, such as an array of closures as a default value, or a closure passed to new in a constant.

The rules

  • They must be static, so they can't use $this.
  • No use(). They can't capture outside variables, which also rules out arrow functions (fn), since those capture variables automatically.

PHP enforces both rules at compile time.

A closure can still access private members of the class it's defined in, the same as a closure written inside the constructor.

What it means for existing code

Nothing breaks. This code used to be a compile error, so no working code uses it. Static analyzers and IDEs need updates so they don't flag it as an error.

The RFC leaves a few ideas for later: non-static closures, first-class callables like strlen(...), and capturing variables.

The vote

Accepted. Voting closed on November 27, 2024 with 19 in favor and none against, clearing the two-thirds majority it needed. It was implemented for PHP 8.5.