Подтвердить что ты не робот

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 ИЛИ они служат той же цели?

4b9b3361

Ответ 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

С уважением,