call_user_func, call_user_func_array, and the callable type
Concept
PHP's callable type represents anything that can be called as a function. Understanding callable forms is essential for building flexible APIs, implementing callbacks, and reading Laravel's source code — which uses callables extensively in middleware, macros, and container bindings.
Callable forms: (1) A string naming a function: 'strlen' or 'MyClass::staticMethod'. (2) An array: [$object, 'method'] for instance methods, ['ClassName', 'staticMethod'] for static. (3) A Closure (anonymous function). (4) An invokable object (a class with __invoke). (5) A first-class callable syntax (PHP 8.1): strlen(...) — creates a Closure from a function reference.
call_user_func($callable, ...$args) calls a callable with individual arguments. call_user_func_array($callable, $args) calls with an array of arguments — equivalent to $callable(...$args) in modern PHP. Both were the primary way to call callables before PHP 5.4's variable function improvements.
Modern PHP: Variable functions ($fn($arg)) work for closures, invokables, and simple string callables. The spread operator ($fn(...$args)) replaces call_user_func_array. call_user_func / call_user_func_array are still useful when you receive a callable from external code and don't know its form, or for strict callable type checking.
is_callable($val) checks if a value is a valid callable. It also checks method existence and accessibility — useful for validation before invocation.
Code Example
<?php
declare(strict_types=1);
// Callable forms
$forms = [
'strlen', // string: built-in function
[new DateTime(), 'format'], // array: instance method
['DateTime', 'createFromFormat'], // array: static method
fn(string $s) => strtoupper($s), // Closure
];
foreach ($forms as $callable) {
var_dump(is_callable($callable)); // true for all
}
// Invokable object
class Multiplier
{
public function __construct(private int $factor) {}
public function __invoke(int $n): int { return $n * $this->factor; }
}
$triple = new Multiplier(3);
echo $triple(5); // 15 — called like a function
echo $triple(5); // also works via call_user_func
call_user_func($triple, 5);
// First-class callable syntax (PHP 8.1)
$strlenFn = strlen(...); // Closure wrapping strlen
$strlenFn('hello'); // 5
$arrMapFn = array_map(...);
$result = $arrMapFn(fn($x) => $x * 2, [1,2,3]); // [2,4,6]
// call_user_func_array (legacy but still useful)
function add(int $a, int $b, int $c): int { return $a + $b + $c; }
$args = [1, 2, 3];
echo call_user_func_array('add', $args); // 6
// Modern equivalent:
echo add(...$args); // 6
// Higher-order function accepting any callable
function applyTo(array $items, callable $fn): array
{
return array_map($fn, $items);
}
$result = applyTo([1,2,3], fn($x) => $x ** 2); // [1,4,9]
$result = applyTo(['a','b','c'], 'strtoupper'); // ['A','B','C']
$result = applyTo(['hello'], [new Multiplier(2), '__invoke']); // won't work for this but pattern works