advanced php language

Variable Functions

Variable functions let PHP call a function or method whose name is stored in a variable. This is one form of dynamic dispatch.

They can be useful for small dispatch tables and callback-style code, but they can also make code harder to trace. Never let untrusted input directly choose an arbitrary function name.

Calling A Function By Name

PHP example
<?php

declare(strict_types=1);

function formatUpper(string $value): string
{
    return strtoupper($value);
}

$formatter = 'formatUpper';

echo $formatter('ada') . PHP_EOL;

// Prints:
// ADA

PHP sees $formatter() and calls the function named by the string.

Check Callability

Use is_callable() before calling a dynamic callable when the value may not be valid.

PHP example
<?php

declare(strict_types=1);

function formatLower(string $value): string
{
    return strtolower($value);
}

$formatter = 'formatLower';

if (!is_callable($formatter)) {
    throw new RuntimeException('Formatter is not callable.');
}

echo $formatter('ADA') . PHP_EOL;

// Prints:
// ada

This prevents a direct fatal failure from trying to call something that cannot be called.

Method Callables

Methods can be represented as callable arrays.

PHP example
<?php

declare(strict_types=1);

final class SlugFormatter
{
    public function slug(string $value): string
    {
        $slug = strtolower(trim($value));
        $slug = preg_replace('/[^a-z0-9]+/', '-', $slug) ?? '';

        return trim($slug, '-');
    }
}

$formatter = new SlugFormatter();
$callable = [$formatter, 'slug'];

echo $callable('PHP Variable Functions') . PHP_EOL;

// Prints:
// php-variable-functions

This appears in older callback APIs and some framework integration points.

Dispatch Tables

A dispatch table maps a small set of allowed names to callables.

PHP example
<?php

declare(strict_types=1);

$formatters = [
    'upper' => fn (string $value): string => strtoupper($value),
    'lower' => fn (string $value): string => strtolower($value),
    'title' => fn (string $value): string => ucwords(strtolower($value)),
];

function formatValue(string $mode, string $value, array $formatters): string
{
    if (!isset($formatters[$mode])) {
        throw new InvalidArgumentException('Unknown formatter.');
    }

    return $formatters[$mode]($value);
}

echo formatValue('title', 'hello world', $formatters) . PHP_EOL;

// Prints:
// Hello World

This is safer than calling a user-provided function name directly because the allowed operations are listed explicitly.

Do Not Call Raw User Input

This is dangerous:

PHP example
<?php

declare(strict_types=1);

$functionFromRequest = 'strtoupper';

// Do not do this with untrusted request input.
echo $functionFromRequest('safe only because this example is fixed') . PHP_EOL;

If the value came from a query string or JSON body, the caller could try to invoke functions you did not intend. Use a whitelist dispatch table or an enum-backed choice instead.

Variable Functions Versus Closures

A string function name is compact, but a closure is usually easier to type and refactor.

PHP example
<?php

declare(strict_types=1);

$formatter = fn (string $value): string => strtoupper(trim($value));

echo $formatter(' ada ') . PHP_EOL;

// Prints:
// ADA

Use the clearest callable form for the codebase. Modern PHP often prefers closures, invokable classes, first-class callables, or explicit dispatch maps.

What You Should Be Able To Do

After this lesson, you should be able to call a function by variable name, use callable arrays for methods, validate callables, build a safe dispatch table, and explain why raw user input should not choose arbitrary functions.

For junior work, this matters because dynamic dispatch appears in legacy PHP, framework internals, callback APIs, and import/export tools.

Practice

Practice: Build A Safe Formatter Dispatch Table

Create a small PHP example that chooses between allowed formatter callbacks.

Task

Build:

  • a dispatch table with upper, lower, and title formatters
  • a formatValue() function that accepts a mode and value
  • validation that rejects unknown modes

Use strict types. Keep the expected output in the PHP code block as printed lines or comments.

Check Your Work

Run cases for:

  • a valid formatter mode
  • an unknown formatter mode

Afterward, explain why this whitelist is safer than calling a raw function name from user input.

Show solution

This solution maps allowed formatter names to closures and rejects anything else.

PHP example
<?php

declare(strict_types=1);

/** @var array<string, callable(string): string> $formatters */
$formatters = [
    'upper' => fn (string $value): string => strtoupper($value),
    'lower' => fn (string $value): string => strtolower($value),
    'title' => fn (string $value): string => ucwords(strtolower($value)),
];

/**
 * @param array<string, callable(string): string> $formatters
 */
function formatValue(string $mode, string $value, array $formatters): string
{
    if (!isset($formatters[$mode])) {
        throw new InvalidArgumentException('Unknown formatter.');
    }

    return $formatters[$mode]($value);
}

echo formatValue('title', 'hello world', $formatters) . PHP_EOL;

try {
    echo formatValue('deleteEverything', 'hello world', $formatters) . PHP_EOL;
} catch (InvalidArgumentException $exception) {
    echo $exception->getMessage() . PHP_EOL;
}

// Prints:
// Hello World
// Unknown formatter.

The whitelist is safer because user input can only select from known operations. It cannot call any arbitrary PHP function.