A principal diferença entre eles é que a closure
é uma classe e callable
um tipo .
O callable
tipo aceita qualquer coisa que possa ser chamada :
var_dump(
is_callable('functionName'),
is_callable([$myClass, 'methodName']),
is_callable(function(){})
);
Quando um closure
vai única aceitar uma função anônima. Note que no PHP versão 7.1 você pode converter funções para um fecho assim:
Closure::fromCallable('functionName')
.
Exemplo:
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
}
Então, por que usar um closure
over callable
?
Rigor porque um closure
é um objecto que tem alguns métodos adicionais: call()
, bind()
e bindto()
. Eles permitem que você use uma função declarada fora de uma classe e execute-a como se estivesse dentro de uma classe.
$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
Você não gostaria de chamar métodos com uma função normal, pois isso gerará erros fatais. Portanto, para contornar você teria que escrever algo como:
if($cb instanceof \Closure){}
Para fazer isso, verifique sempre que não faz sentido. Portanto, se você deseja usar esses métodos, declare que o argumento é a closure
. Caso contrário, basta usar um normal callback
. Deste jeito; Um erro é gerado na chamada de função em vez do seu código, tornando-o muito mais fácil de diagnosticar.
Em uma nota lateral: A closure
turma não pode ser estendida como final .
["Foo", "bar"]
paraFoo::bar
ou[$foo, "bar"]
para$foo->bar
.