Signature
is_callable(mixed $value, bool $syntax_only = false, string &$callable_name = null): boolPurpose
is_callable() answers one question: could this value be invoked as a callback from the current scope? It is most useful before you store or run a callback that came from configuration, a plugin list or a function argument. Instead of letting call_user_func() fail later, you can check early and reject the value with a clear message.
The function accepts any value. Strings, closures, objects and arrays can all be callable, depending on their shape and on what exists when the check runs.
Signature
Parameters and defaults
$valueis the value to test. It has no default.$syntax_onlydefaults tofalse. In that mode PHP checks that the callable really exists and can be reached from the current scope. When set totrue, PHP only checks the structure. A string passes, an invokable object orClosurepasses, and an array passes if it has two entries: an object or string first, and a string second.$callable_nameis an optional by-reference output. PHP fills it with a readable name such asSomeClass::someMethod. It is a label for display or logging, not proof that a static method exists.
Return value and failure cases
The return type is bool. You get true when the value is callable and false otherwise. A bad value does not throw an exception, so an unknown function name, a wrong array shape or an inaccessible method simply gives false.
The result depends on the calling scope. A private method can be callable from inside its own class but not from outside. The function may also trigger autoloading when you pass a class name.
Example 1: functions, closures and plain strings
A string is callable when it names an existing function. Closures are always callable. Other strings are not.
function greet(string $name): string
{
return "Hello, $name";
}
$checks = [
'named function' => is_callable('greet'),
'built-in function' => is_callable('strtoupper'),
'missing function' => is_callable('no_such_function_here'),
'closure' => is_callable(fn(int $n): int => $n * 2),
'plain string' => is_callable('not a function'),
];
echo json_encode($checks, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"named function":true,"built-in function":true,"missing function":false,"closure":true,"plain string":false}
Example 2: methods, visibility and invokable objects
An array callable is [$objectOrClass, 'method']. The method must exist and be visible from where you call is_callable(). A non-static method named through a class string is not callable, which matches the manual's ['Foo', 'foo'] example. An object with a visible __invoke() method is callable itself.
class Greeter
{
public function hello(): string
{
return 'hello';
}
public static function make(): string
{
return 'made';
}
private function secret(): string
{
return 'secret';
}
public function __invoke(): string
{
return 'invoked';
}
}
$greeter = new Greeter();
$checks = [
'instance method' => is_callable([$greeter, 'hello']),
'static method string' => is_callable('Greeter::make'),
'static method array' => is_callable(['Greeter', 'make']),
'private from outside' => is_callable([$greeter, 'secret']),
'invokable object' => is_callable($greeter),
'missing method' => is_callable([$greeter, 'missing']),
'non-static via class name' => is_callable(['Greeter', 'hello']),
];
echo json_encode($checks, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"instance method":true,"static method string":true,"static method array":true,"private from outside":false,"invokable object":true,"missing method":false,"non-static via class name":false}
Example 3: syntax_only, __call() and callable_name
With syntax_only set to true, a string that names no function still passes, because the shape is plausible. An array with the wrong types fails. If an object implements __call(), any method name on that object counts as callable.
class Router
{
public function __call(string $name, array $arguments): string
{
return $name;
}
}
$router = new Router();
$actual = is_callable('missing_function_name');
$syntax = is_callable('missing_function_name', true, $nameForString);
$magic = is_callable([$router, 'anything'], false, $nameForArray);
$badShape = is_callable([1, 2], true);
echo json_encode([
'actual' => $actual,
'syntaxOnly' => $syntax,
'magicCall' => $magic,
'badShape' => $badShape,
'stringName' => $nameForString,
'arrayName' => $nameForArray,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"actual":false,"syntaxOnly":true,"magicCall":true,"badShape":false,"stringName":"missing_function_name","arrayName":"Router::anything"}
Edge cases
- A class name string is callable only when the class implements
__callStatic(), as the manual notes. - Constructors are not static methods.
is_callable(['Foo', '__construct'])isfalse, whileis_callable([$foo, '__construct'])istruefor an existing object. Do not use the function to decide whether a class can be instantiated. - Because of
__call(), atrueresult for an object does not prove that a real method with that name exists. - Autoloading can run when you check a class name, so the check may have side effects in an application with autoloaders.
Common mistakes
- Using
syntax_onlyas an existence check. It only validates shape. Leave itfalsewhen you need to know that the call can succeed. - Trusting
callable_name. The nameSomeClass::someMethodis a label. It does not make the method a static method. - Confusing it with
function_exists()ormethod_exists(). Those functions check definitions.is_callable()also considers visibility, magic methods and invokable objects. - Passing
'Class::method'for an instance method. In PHP 8 a non-static method cannot be called statically, so the check fails. Pass[$object, 'method']instead. - Ignoring scope. A private or protected method may be callable in one place and not in another.
Related functions
call_user_func()calls the callback given as its first argument.function_exists()reports whether a function has been defined.method_exists()checks whether a class or object has a method.
Recap
Use is_callable() to validate callbacks before running them. Keep syntax_only off for real reachability checks, read callable_name as a display label, and remember that __invoke(), __call(), __callStatic() and the calling scope can all change the answer.
Tested examples
function greet(string $name): string
{
return "Hello, $name";
}
$checks = [
'named function' => is_callable('greet'),
'built-in function' => is_callable('strtoupper'),
'missing function' => is_callable('no_such_function_here'),
'closure' => is_callable(fn(int $n): int => $n * 2),
'plain string' => is_callable('not a function'),
];
echo json_encode($checks, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"named function":true,"built-in function":true,"missing function":false,"closure":true,"plain string":false}
class Greeter
{
public function hello(): string
{
return 'hello';
}
public static function make(): string
{
return 'made';
}
private function secret(): string
{
return 'secret';
}
public function __invoke(): string
{
return 'invoked';
}
}
$greeter = new Greeter();
$checks = [
'instance method' => is_callable([$greeter, 'hello']),
'static method string' => is_callable('Greeter::make'),
'static method array' => is_callable(['Greeter', 'make']),
'private from outside' => is_callable([$greeter, 'secret']),
'invokable object' => is_callable($greeter),
'missing method' => is_callable([$greeter, 'missing']),
'non-static via class name' => is_callable(['Greeter', 'hello']),
];
echo json_encode($checks, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"instance method":true,"static method string":true,"static method array":true,"private from outside":false,"invokable object":true,"missing method":false,"non-static via class name":false}
class Router
{
public function __call(string $name, array $arguments): string
{
return $name;
}
}
$router = new Router();
$actual = is_callable('missing_function_name');
$syntax = is_callable('missing_function_name', true, $nameForString);
$magic = is_callable([$router, 'anything'], false, $nameForArray);
$badShape = is_callable([1, 2], true);
echo json_encode([
'actual' => $actual,
'syntaxOnly' => $syntax,
'magicCall' => $magic,
'badShape' => $badShape,
'stringName' => $nameForString,
'arrayName' => $nameForArray,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"actual":false,"syntaxOnly":true,"magicCall":true,"badShape":false,"stringName":"missing_function_name","arrayName":"Router::anything"}