haphpiness

These are things in PHP which make me genuinely_happy();

Closures in Constant Expressions

PHP 8.5 allows static closures and first-class callables in constant expressions — meaning you can use them as default parameter values, in attribute arguments, and as class constant values. This unlocks patterns that were previously impossible.

// Closures as default parameter values
function process(
    array $data,
    Closure $transform = static fn($x) => $x,  // Identity function default
): array {
    return array_map($transform, $data);
}

process([1, 2, 3]);                        // [1, 2, 3] — default identity
process([1, 2, 3], fn($n) => $n * 2);     // [2, 4, 6]

// First-class callables in attribute arguments
#[Attribute]
class Validator {
    public function __construct(public Closure $rule) {}
}

class UserForm {
    #[Validator(is_string(...))]
    public string $name;

    #[Validator(static fn($v) => strlen($v) >= 8)]
    public string $password;
}

// Closures as class constants
class Transforms {
    const Closure NORMALIZE = static fn(string $s) => strtolower(trim($s));
    const Closure SLUGIFY = static fn(string $s) =>
        preg_replace('/[^a-z0-9]+/', '-', strtolower(trim($s)));
}

This was one of the last restrictions on where closures could appear in PHP. Removing it makes callback-heavy APIs much cleaner — no more "pass null and we'll use a default callback" patterns.

Significance: Composability

When closures can appear anywhere a value can, functional patterns become first-class citizens in PHP's type system. Default callback parameters, strategy constants, and attribute-based configuration all become more natural and expressive.