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

问题描述 投票:86回答:3

我注意到如果我们期望一些回调函数运行,我可以使用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或它们的用途相同?

php
3个回答
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作为类型提示。


43
投票

他们之间的主要区别是,closure是一个类,callable是一个类型。

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


-1
投票

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

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

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

进程退出代码255。

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

最好的祝福,

© www.soinside.com 2019 - 2024. All rights reserved.