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
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
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
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
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
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
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, andtitleformatters - 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
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.