我注意到如果我们期望一些回调函数运行,我可以使用Closure
或Callable
作为类型提示。例如:
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
或它们的用途相同?
区别在于,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
作为类型提示。
他们之间的主要区别是,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。
值得一提的是,这不适用于PHP版本5.3.21至5.3.29。
在任何这些版本中,您将获得如下输出:
你好,世界!可捕获的致命错误:传递给callFunc2()的参数1必须是> Callable的实例,给定Closure的实例,在第16行的/ in / kqeYD中调用,在第7行的/ in / kqeYD中定义
进程退出代码255。
人们可以尝试使用https://3v4l.org/kqeYD#v5321
最好的祝福,