类型提示——“闭包”和“可调用”的区别

我注意到,如果我们希望运行一些回调函数,我可以使用 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或者,它们的作用是一样的吗?

50554 次浏览

区别在于,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作为类型提示。

值得一提的是,这在 PHP 版本5.3.21到5.3.29之间不起作用。

在这些版本中的任何一个中,您都会得到如下输出:

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

进程代码255退出。

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

最好的问候,

它们之间的主要区别是 closure同学们,而 callable类型

callable类型接受任何可以是 电话的东西:

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

其中 closure只有接受一个匿名函数。注意,在 PHP 版本7.1中,可以像下面这样将函数转换为闭包: Closure::fromCallable('functionName') .


例如:

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
}

那么为什么使用 closure而不是 callable呢?

严格是因为 closure是一个具有一些附加方法的对象: call()bind()bindto()。它们允许您使用在类之外声明的函数,并像在类中一样执行它:

$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

您不希望对普通函数调用方法,因为那将引发致命错误。因此,为了避免这种情况,你必须写下这样的东西:

if($cb instanceof \Closure){}

每次都这样检查是没有意义的。所以如果你想使用这些方法,那么这个参数就是 closure。否则就使用正常的 callback。这样,在函数调用时会引发错误,而不是在代码中引发错误,从而使诊断变得更加容易。

附注: closure类不能作为其 期末考试进行扩展。