PHP: Type hinting - Difference between `Closure` and `Callable`
I noticed that I can use either of Closure
or Callable
as type hint if we expected some callback function to run. For example:
function callFunc1(Closure $closure) {
$closure();
}
function callFunc2(Callable $callback) {
$callback();
}
$function = function() {
echo 'Hello, World!';
};
callFunc1($function); // Hello, World!
callFunc2($function); // Hello, World!
Question:
What's the difference here ? In other words when to use Closure
and when to use Callable
OR they serve the same purpose ?
Solution 1:
The difference is, that a Closure
must be an anonymous function, where callable
also can be a normal function.
You can see/test this with the example below and you will see that you will get an error for the first one:
function callFunc1(Closure $closure) {
$closure();
}
function callFunc2(Callable $callback) {
$callback();
}
function xy() {
echo 'Hello, World!';
}
callFunc1("xy"); // Catchable fatal error: Argument 1 passed to callFunc1() must be an instance of Closure, string given
callFunc2("xy"); // Hello, World!
So if you only want to type hint anonymous function use: Closure
and if you want also to allow normal functions use callable
as type hint.
Solution 2:
The main difference between them is that a closure
is a class and callable
a type.
The callable
type accepts anything that can be called:
var_dump(
is_callable('functionName'),
is_callable([$myClass, 'methodName']),
is_callable(function(){})
); // all true
Where a closure
will only accept an anonymous function. Note that in PHP version 7.1 you can convert functions to a closure like so:
Closure::fromCallable('functionName')
.
Example:
namespace foo{
class bar{
private $baz = 10;
function myCallable(callable $cb){$cb()}
function myClosure(\Closure $cb){$cb()} // type hint must refer to global namespace
}
function func(){}
$cb = function(){};
$fb = new bar;
$fb->myCallable(function(){});
$fb->myCallable($cb);
$fb->myCallable('func');
$fb->myClosure(function(){});
$fb->myClosure($cb);
$fb->myClosure(\Closure::fromCallable('func'));
$fb->myClosure('func'); # TypeError
}
So why use a closure
over callable
?
Strictness because a closure
is an object that has some additional methods: call()
, bind()
and bindto()
. They allow you to use a function declared outside of a class and execute it as if it was inside a class:
$inject = function($i){return $this->baz * $i;};
$cb1 = \Closure::bind($inject, $fb);
$cb2 = $inject->bindTo($fb);
echo $cb1->call($fb, 2); // 20
echo $cb2(3); // 30
You would not like to call methods on a normal function as that will raise fatal errors. So in order to circumvent that you would have to write something like:
if($cb instanceof \Closure){}
To do this check every time is pointless. So if you want to use those methods state that the argument is a closure
. Otherwise just use a normal callback
. This way; An error is raised on function call instead of your code causing it making it much easier to diagnose.
On a side note: The closure
class cannot be extended as its final.