PHP:类型提示 - “Closure”和“Callable”之间的区别

时间:2015-04-19 13:49:13

标签: php

我注意到如果我们期望运行一些回调函数,我可以使用ClosureCallable作为类型提示。例如:

function callFunc1(Closure $closure) {
    $closure();
}

function callFunc2(Callable $callback) {
    $callback();
}

$function = function() {
    echo 'Hello, World!';
};

callFunc1($function); // Hello, World!
callFunc2($function); // Hello, World!

问题:

这里的区别是什么?换句话说,何时使用Closure以及何时使用Callable或者它们是出于同样的目的?

3 个答案:

答案 0 :(得分:129)

不同之处在于,Closure必须是匿名函数,其中callable也可以是正常函数。

你可以通过下面的例子看到/测试这个,你会发现第一个会出错:

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!

因此,如果您只想输入提示匿名函数,请使用:Closure如果您还想允许普通函数使用callable作为类型提示。

答案 1 :(得分:42)

它们之间的主要区别在于closurecallable类型

callable类型接受任何可以called的内容:

var_dump(
  is_callable('functionName'),
  is_callable([$myClass, 'methodName']),
  is_callable(function(){})
);

closure 接受匿名函数。请注意,在PHP 7.1版中,您可以将函数转换为闭包,如下所示:  Closure::fromCallable('functionName')

实施例

namespace foo{
  class bar{
    private $val = 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
}

为什么要使用closure而不是callable

严格,因为closure是一个包含其他方法的对象:call()bind()bindto()。它们允许您使用在类外部声明的函数,并像在类中一样执行它。

$inject = function($i){return $this->val * $i;};
$cb1 = Closure::bind($inject, $fb);
$cb2 = $inject->bindTo($fb);

echo $cb1->call($fb, 2); // 20
echo $cb2(3);            // 30

您不希望在正常函数上调用方法,因为这会引发致命错误。所以为了规避你必须写下这样的东西:

if($cb instanceof \Closure){}

每次检查都是毫无意义的。因此,如果您想使用这些方法声明参数是closure。否则只需使用普通callback。这条路;函数调用而不是代码引发错误,导致它更容易诊断。

旁注: closure类无法扩展为final

答案 2 :(得分:-1)

值得一提的是,这不适用于PHP版本5.3.21到5.3.29。

在任何这些版本中,您都会获得如下输出:

  

你好,世界!   可捕获的致命错误:传递给callFunc2()的参数1必须是>的实例可调用的Closure实例,在第16行的/ in / kqeYD中调用,在第7行的/ in / kqeYD中定义

     

使用代码255退出流程。

可以尝试使用https://3v4l.org/kqeYD#v5321

致以最诚挚的问候,