advanced php language
First-Class Callables
First-class callable syntax creates a callable value from an existing function or method using (...). It gives you a cleaner and safer way to pass existing functions and methods around.
You will see this in callback-heavy code, collection operations, pipelines, event handlers, dependency factories, and places where older PHP code might use string function names or callable arrays.
Function Callables
<?php
declare(strict_types=1);
function normaliseEmail(string $email): string
{
return strtolower(trim($email));
}
$normalise = normaliseEmail(...);
echo $normalise(' ADA@EXAMPLE.COM ') . PHP_EOL;
// Prints:
// ada@example.com
normaliseEmail(...) does not call the function immediately. It creates a callable that can be stored and called later.
Passing First-Class Callables
First-class callables work well with functions that expect callbacks.
<?php
declare(strict_types=1);
function normaliseEmail(string $email): string
{
return strtolower(trim($email));
}
$emails = [' ADA@EXAMPLE.COM ', ' Grace@Example.com '];
$normalised = array_map(normaliseEmail(...), $emails);
echo implode(', ', $normalised) . PHP_EOL;
// Prints:
// ada@example.com, grace@example.com
This avoids stringly callback names such as 'normaliseEmail' and lets tools understand the referenced function more directly.
Method Callables
You can create a callable from an object method.
<?php
declare(strict_types=1);
final class Slugger
{
public function slug(string $value): string
{
$slug = strtolower(trim($value));
$slug = preg_replace('/[^a-z0-9]+/', '-', $slug) ?? '';
return trim($slug, '-');
}
}
$slugger = new Slugger();
$slug = $slugger->slug(...);
echo $slug('PHP First Class Callables') . PHP_EOL;
// Prints:
// php-first-class-callables
This is clearer than [$slugger, 'slug'] in many modern codebases.
Static Method Callables
Static methods can be converted too.
<?php
declare(strict_types=1);
final class MoneyFormatter
{
public static function pounds(int $pence): string
{
return 'GBP ' . number_format($pence / 100, 2);
}
}
$formatter = MoneyFormatter::pounds(...);
echo $formatter(1234) . PHP_EOL;
// Prints:
// GBP 12.34
Use static methods sparingly, but when they already exist, first-class callable syntax is concise.
Invokable Objects
Objects with __invoke() are already callable.
<?php
declare(strict_types=1);
final class Prefix
{
public function __construct(
private string $prefix,
) {
}
public function __invoke(string $value): string
{
return $this->prefix . $value;
}
}
$prefix = new Prefix('ID-');
echo $prefix('42') . PHP_EOL;
// Prints:
// ID-42
You do not need (...) for an invokable object because the object itself is the callable.
First-Class Callables Versus Closures
Use first-class callable syntax when an existing function or method already has the exact shape you need.
Use a closure or arrow function when you need to adapt arguments, capture local variables, or combine several operations.
<?php
declare(strict_types=1);
function wrap(string $prefix, string $value): string
{
return $prefix . $value;
}
$prefixId = fn (string $value): string => wrap('ID-', $value);
echo $prefixId('42') . PHP_EOL;
// Prints:
// ID-42
The closure adapts wrap() by fixing the first argument.
What You Should Be Able To Do
After this lesson, you should be able to create first-class callables from functions, object methods, and static methods; pass them to callback APIs; and decide when a closure is still clearer.
For junior work, this matters because first-class callables are a modern replacement for many string and array callback patterns.
Practice
Practice: Pass Existing Functions As Callables
Create a small PHP example using first-class callable syntax.
Task
Build:
- a
normaliseEmail()function - an array of messy email strings
- an
array_map()call usingnormaliseEmail(...) - a class with a method converted to a callable with
(...)
Use strict types. Keep the expected output in the PHP code block as printed lines or comments.
Check Your Work
Confirm:
normaliseEmail(...)creates a callable without calling immediatelyarray_map()receives that callable- an object method can also be converted to a callable
Afterward, explain when an arrow function would be clearer than a first-class callable.
Show solution
This solution uses first-class callable syntax for both a function and an object method.
<?php
declare(strict_types=1);
function normaliseEmail(string $email): string
{
return strtolower(trim($email));
}
final class Slugger
{
public function slug(string $value): string
{
$slug = strtolower(trim($value));
$slug = preg_replace('/[^a-z0-9]+/', '-', $slug) ?? '';
return trim($slug, '-');
}
}
$emails = [' ADA@EXAMPLE.COM ', ' Grace@Example.com '];
$normalised = array_map(normaliseEmail(...), $emails);
echo implode(', ', $normalised) . PHP_EOL;
$slugger = new Slugger();
$slug = $slugger->slug(...);
echo $slug('First Class Callables') . PHP_EOL;
// Prints:
// ada@example.com, grace@example.com
// first-class-callables
An arrow function is clearer when you need to adapt arguments, capture a value, or combine several operations instead of passing an existing function or method directly.