Since PHP 8.5, static closures can be used in constant expressions. This includes default values of parameters and properties, constants, and attribute parameters.
Suppose we have a function that takes an optional callback to filter array elements. Before PHP 8.5, we had to define the closure as null by default and then assign a fallback closure when the user didn't provide one.
<?php
function arrayFilter(array $data, ?Closure $callback = null): array
{
$callback ??= static fn (mixed $item) => !empty($item);
$result = [];
foreach ($data as $item) {
if ($callback($item)) {
$result[] = $item;
}
}
return $result;
}
$data = ['', 1, 0, false, '1'];
var_dump(arrayFilter($data)); // array(2) {[0]=>int(1) [1]=>string(1) "1"}
Since PHP 8.5, a static closure can be used directly as a default parameter value, rather than setting the closure to null and assigning a fallback.
<?php
function arrayFilter(
array $data,
Closure $callback = static function (mixed $item) { return !empty($item); },
): array {
$result = [];
foreach ($data as $item) {
if ($callback($item)) {
$result[] = $item;
}
}
return $result;
}
$data = ['', 1, 0, false, '1'];
var_dump(arrayFilter($data)); // array(2) {[0]=>int(1) [1]=>string(1) "1"}
Other cases where static closures are valid in constant expressions:
- Default values of properties
ArrayFilter.php
<?php
class ArrayFilter
{
private Closure $callback = static function (mixed $item) {
return !empty($item);
};
public function setCallback(Closure $callback): void
{
$this->callback = $callback;
}
public function run(array $data): array
{
$result = [];
foreach ($data as $item) {
if (($this->callback)($item)) {
$result[] = $item;
}
}
return $result;
}
}
<?php
require_once __DIR__.'/ArrayFilter.php';
$arrayFilter = new ArrayFilter();
$data = ['', 1, 0, false, '1'];
var_dump($arrayFilter->run($data)); // array(2) {[0]=>int(1) [1]=>string(1) "1"}
- Constants
FilterConst.php
<?php
class FilterConst
{
public const Closure DEFAULT = static function (mixed $item) {
return !empty($item);
};
}
<?php
require_once __DIR__.'/FilterConst.php';
$data = ['', 1, 0, false, '1'];
var_dump(array_filter($data, FilterConst::DEFAULT)); // array(2) {[0]=>int(1) [1]=>string(1) "1"}
- Attribute parameters
Transform.php
<?php
#[Attribute(Attribute::TARGET_PROPERTY)]
class Transform
{
public function __construct(public Closure $callback)
{
}
}
User.php
<?php
class User
{
public function __construct(
#[Transform(static function (string $value) { return ucfirst($value); })]
private string $name,
) {
$this->name = $this->applyTransform('name', $name);
}
public function getName(): string
{
return $this->name;
}
private function applyTransform(string $property, mixed $value): mixed
{
$reflection = new ReflectionProperty($this, $property);
$attribute = $reflection->getAttributes(Transform::class)[0]->newInstance();
return ($attribute->callback)($value);
}
}
<?php
require_once __DIR__.'/Transform.php';
require_once __DIR__.'/User.php';
$user = new User('john');
echo $user->getName(); // John
Leave a Comment
Cancel reply