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 example
<?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 example
<?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 example
<?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 example
<?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 example
<?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 example
<?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 using normaliseEmail(...)
  • 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 immediately
  • array_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 example
<?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.