PHP: Type hinting - Difference between `Closure` and `Callable`

Php

Php Problem Overview


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 ?

Php Solutions


Solution 1 - Php

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 - Php

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.

Solution 3 - Php

It's worth mentioning that this won't work for PHP versions 5.3.21 to 5.3.29.

In any of those versions you will get an output like:

> Hello, World! > Catchable fatal error: Argument 1 passed to callFunc2() must be an instance of > Callable, instance of Closure given, called in /in/kqeYD on line 16 and defined in /in/kqeYD on line 7 > > Process exited with code 255.

One can try that out using https://3v4l.org/kqeYD#v5321

Best regards,

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDev01View Question on Stackoverflow
Solution 1 - PhpRizier123View Answer on Stackoverflow
Solution 2 - PhpXorifelseView Answer on Stackoverflow
Solution 3 - PhpRod EliasView Answer on Stackoverflow