PHP: Тип намека - Разница между `Closure` и` Callable`
Я заметил, что я могу использовать любой из 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
ИЛИ они служат той же цели?
Ответы
Ответ 1
Разница в том, что 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
как подсказку типа.
Ответ 2
Основное различие между ними заключается в том, что closure
является классом и callable
типом.
callable
тип принимает все, что может быть под названием:
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
На стороне примечание: класс closure
не может быть расширен как его окончательный.
Ответ 3
Стоит отметить, что это не сработает для PHP версий 5.3.21 - 5.3.29.
В любой из этих версий вы получите вывод, например:
Привет, мир! Допустимая фатальная ошибка: аргумент 1, переданный callFunc2(), должен быть экземпляром > Callable, экземпляром данных Closure, вызывается в /in/kqeYD в строке 16 и определен в /in/kqeYD в строке 7
Процесс завершен с кодом 255.
Можно попробовать это, используя https://3v4l.org/kqeYD#v5321
С уважением,