PHP Callback Functions

Su Subhalakshmi Chandran Updated 17 Oct 2026
7 min read ·Lesson 77 of 109
In this post

What Is a Callback? In simple terms: A callback is a function that you hand to another function, so that the other function can call it back at the right moment. You decide what should happen; the rec

What Is a Callback?

In simple terms: A callback is a function that you hand to another function, so that the other function can call it back at the right moment. You decide what should happen; the receiving function decides when it happens.

array_map('strtoupper', ['php', 'mysql']);   // array_map calls strtoupper for each element

Technically: In PHP, anything that can be called is a callable. A parameter declared with the callable type (or Closure) accepts such values, and the receiving code invokes them with $callback(...$arguments). Functions that accept or return other functions are called higher-order functions.

Why Callbacks Are Useful

Callbacks separate a general algorithm from the specific behavior:

  • usort() knows how to sort; your callback says how to compare two items.
  • array_filter() knows how to loop; your callback says which items to keep.
  • A retry helper knows how to repeat an operation; your callback is the operation.
  • An event system knows when something happened; callbacks define what to do about it.

The same general code can then be reused with many different behaviors.


The Forms of Callables

FormExampleNotes
Function name string'strtoupper'Built-in or user-defined function
Closure (anonymous function)function ($x) { return $x * 2; }See Anonymous Functions
Arrow functionfn($x) => $x * 2See Arrow Functions
First-class callable (PHP 8.1+)strtoupper(...), $obj->method(...)See First-class Callable Syntax
Object method array[$object, 'method']Older style
Static method[ClassName::class, 'method'] or 'ClassName::method'Older style
Invokable objectAn object with an __invoke() methodCovered below

Example: Each Form in Action

<?php
class Greeter
{
    public function greet(string $name): string
    {
        return "Hello, $name";
    }

    public static function shout(string $name): string
    {
        return strtoupper("hi, $name!");
    }
}

$greeter = new Greeter();

$callables = [
    'string' => 'ucfirst',
    'closure' => function (string $name): string { return "Welcome, $name"; },
    'arrow' => fn(string $name): string => "Vanakkam, $name",
    'first-class' => $greeter->greet(...),
    'array' => [$greeter, 'greet'],
    'static' => [Greeter::class, 'shout'],
];

foreach ($callables as $form => $callable) {
    echo str_pad($form, 12) . $callable('asha') . "\n";
}

Output

string      Asha
closure     Welcome, asha
arrow       Vanakkam, asha
first-class Hello, asha
array       Hello, asha
static      HI, ASHA!

Every form is called the same way: $callable('asha'). Code receiving a callback does not need to know which form it was given.


Writing Functions That Accept Callbacks

Example: Transforming With a Callback

<?php
function transformAll(array $items, callable $transform): array
{
    $result = [];

    foreach ($items as $key => $item) {
        $result[$key] = $transform($item);
    }

    return $result;
}

$prices = ['Pen' => 20, 'Lamp' => 1299];

print_r(transformAll($prices, fn(int $price): string => '₹' . number_format($price * 1.18, 2)));

Output

Array
(
    [Pen] => ₹23.60
    [Lamp] => ₹1,532.82
)

How It Works

  1. callable $transform declares that the second argument must be callable; passing anything else causes a TypeError.
  2. Inside the loop, $transform($item) calls whatever function the caller provided.
  3. transformAll() handles the looping and key preservation; the caller supplies only the transformation.

callable vs Closure Type Declarations

TypeAcceptsNotes
callableAll forms in the table aboveCannot be used for property types
ClosureClosures, arrow functions, first-class callablesCan be used for properties; convert other forms with Closure::fromCallable() or (...)

Passing Extra Data to Callbacks

A callback receives only the arguments the calling code passes. To give it additional data, capture variables in a closure:

<?php
$students = [
    ['name' => 'Asha', 'score' => 88],
    ['name' => 'Bala', 'score' => 34],
    ['name' => 'Charu', 'score' => 61],
];

$passMark = 40;

$passed = array_filter($students, fn(array $student): bool => $student['score'] >= $passMark);

echo implode(', ', array_column($passed, 'name')) . "\n";

Output

Asha, Charu

array_filter() calls the callback with one argument (the student), but the arrow function also uses $passMark from the surrounding scope. This is the standard way to "configure" a callback.


Practical Callback Patterns

Retrying an Operation

<?php
function retry(callable $operation, int $maxAttempts, ?callable $onFailure = null): mixed
{
    for ($attempt = 1; ; $attempt++) {
        try {
            return $operation($attempt);
        } catch (RuntimeException $e) {
            if ($onFailure !== null) {
                $onFailure($e, $attempt);
            }

            if ($attempt >= $maxAttempts) {
                throw $e;
            }
        }
    }
}

$result = retry(
    function (int $attempt): string {
        if ($attempt < 3) {
            throw new RuntimeException("Gateway timeout");
        }
        return "Payment confirmed on attempt $attempt";
    },
    5,
    fn(RuntimeException $e, int $attempt) => print("Attempt $attempt failed: {$e->getMessage()}\n")
);

echo $result . "\n";

Output

Attempt 1 failed: Gateway timeout
Attempt 2 failed: Gateway timeout
Payment confirmed on attempt 3

retry() contains the general logic (counting attempts, catching failures, giving up); the operation and the optional failure handler are callbacks. The same helper can retry API calls, database connections, or file uploads.

Measuring Execution Time

<?php
function timed(string $label, callable $task): mixed
{
    $start = hrtime(true);

    try {
        return $task();
    } finally {
        printf("%s took %.1f ms\n", $label, (hrtime(true) - $start) / 1e6);
    }
}

$sum = timed('Summing', fn(): int => array_sum(range(1, 100_000)));
echo $sum . "\n";

Output (the time varies)

Summing took 1.2 ms
5000050000

The finally block prints the time even if the task throws an exception. Wrapping code in callbacks like this adds behavior (timing, logging, transactions) around any operation without modifying it.


Invokable Objects: __invoke()

An object whose class defines __invoke() can be called like a function. This is useful when a callback needs configuration or dependencies:

<?php
final class DiscountCalculator
{
    public function __construct(private float $percent) {}

    public function __invoke(float $price): float
    {
        return round($price * (1 - $this->percent / 100), 2);
    }
}

$festivalDiscount = new DiscountCalculator(15);

echo $festivalDiscount(1000) . "\n";
print_r(array_map($festivalDiscount, [499, 1299]));
var_dump(is_callable($festivalDiscount));

Output

850
Array
(
    [0] => 424.15
    [1] => 1104.15
)
bool(true)

The object carries its configuration (15%) and can be passed anywhere a callable is expected. Unlike a closure, it has a name, can be type-hinted by class, and can be tested and reused independently.


Practical Example: A Simple Event Hook System

<?php
final class Hooks
{
    private array $listeners = [];

    public function on(string $event, callable $listener, int $priority = 10): void
    {
        $this->listeners[$event][$priority][] = $listener;
    }

    public function trigger(string $event, mixed ...$args): void
    {
        $byPriority = $this->listeners[$event] ?? [];
        ksort($byPriority);

        foreach ($byPriority as $listeners) {
            foreach ($listeners as $listener) {
                $listener(...$args);
            }
        }
    }
}

$hooks = new Hooks();

$hooks->on('order.paid', fn(array $order) => print("Email receipt to {$order['email']}\n"));
$hooks->on('order.paid', fn(array $order) => print("Reserve stock for order #{$order['id']}\n"), priority: 1);
$hooks->on('order.paid', fn(array $order) => print("Notify warehouse\n"), priority: 20);

$hooks->trigger('order.paid', ['id' => 501, 'email' => 'asha@example.com']);
$hooks->trigger('order.refunded', ['id' => 502]);   // no listeners: nothing happens

Output

Reserve stock for order #501
Email receipt to asha@example.com
Notify warehouse

Step-by-Step Explanation

  1. on() stores callbacks grouped by event name and priority.
  2. trigger() sorts the priorities (lower numbers run first) and calls each listener with the event's arguments.
  3. The order-processing code only triggers order.paid; it does not need to know which actions follow. New behavior is added by registering another callback.

This is the core idea behind WordPress actions and filters, Laravel events, and Symfony's EventDispatcher.


Common Mistakes

Passing the Result Instead of the Function

array_map(strtoupper('php'), $items);   // wrong: passes the string "PHP"
array_map('strtoupper', $items);        // right: passes the function

Callbacks With the Wrong Number of Parameters

Effect: Built-in functions call callbacks with specific arguments (for example, array_filter() with ARRAY_FILTER_USE_BOTH passes value and key). A callback expecting different parameters fails or behaves unexpectedly.

Fix: Check the documentation for the arguments each function passes.

Using String Callables for Methods

Effect: Typos are found only at runtime, and IDEs cannot follow them.

Fix: Prefer closures and first-class callable syntax.

Hidden Side Effects in Callbacks

Effect: Callbacks in array_map() that modify external state are hard to follow.

Fix: Keep transformation callbacks pure; use explicit loops or event hooks for side effects.


Best Practices

  • Use callbacks to separate general algorithms from specific behavior.
  • Type-hint callback parameters with callable or Closure.
  • Use closures to pass configuration into callbacks.
  • Use invokable classes for callbacks that need dependencies or should be reusable and testable.
  • Prefer first-class callable syntax over string and array callables in modern code.

Official reference: https://www.php.net/manual/en/language.types.callable.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Home PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else Slicing Strings User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators
Ask about this post
AI Ask about this post

Ask questions about PHP Callback Functions and get answers drawn from it.

Signed-in readers only.