Twinkle

Twinkle

Example of Refactoring Code Using PHP 8.1's "First-Class Callable Syntax"

https://www.php.net/manual/en/functions.first_class_callable_syntax.php

When using Laravel, it’s common to have complex closures lined up. This new syntax can help organize them.

use App\Models\Post;
use Illuminate\Contracts\Database\Query\Builder;

public function index()
{
    $posts = Post::query()
                ->where(function (Builder $query) {
                    $query->where('name', 'test')
                          ->where('foo', 'foo');
                })->where(function (Builder $query) {
                    $query->where('title', 'test')
                          ->where('bar', 'bar');
                })->get();
}

First, separate the closures into methods and convert them to closures using Closure::fromCallable. This is the way to write it up to PHP 8.0.

use App\Models\Post;
use Closure;
use Illuminate\Contracts\Database\Query\Builder;

public function index()
{
    $posts = Post::query()
                ->where(Closure::fromCallable([$this, 'name']))
                ->where(Closure::fromCallable([$this, 'title']))
                ->get();
}

private function name(Builder $query): void
{
    $query->where('name', 'test')
          ->where('foo', 'foo');
}

private function title(Builder $query): void
{
    $query->where('title', 'test')
          ->where('bar', 'bar');
}

PHP 8.1's "first-class callable syntax" is almost the same as Closure::fromCallable, so it can be rewritten as such.

use App\Models\Post;
use Illuminate\Contracts\Database\Query\Builder;

public function index()
{
    $posts = Post::query()
                ->where($this->name(...))
                ->where($this->title(...))
                ->get();
}

private function name(Builder $query): void
{
    $query->where('name', 'test')
          ->where('foo', 'foo');
}

private function title(Builder $query): void
{
    $query->where('title', 'test')
          ->where('bar', 'bar');
}

If you’re using PhpStorm, this can be automatically replaced using the intention action, so you don’t have to rewrite it yourself. If you’re not used to the new (...) syntax, you can write it with Closure::fromCallable and let PhpStorm handle the conversion.

Since it’s not widely adopted yet, it’s common to write code with closures first and then refactor it later using the new syntax.