PHP: Type hinting-разница между '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 или они служат той же цели ?

3 66
php

3 ответа:

разница в том, что 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 a тип.

The callable тип принимает все, что может быть под названием:

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

где a 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 over callable?

строгость, потому что a 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. Таким образом; ошибка возникает при вызове функции вместо вашего кода, что делает его гораздо проще диагностировать.

на заметку: The closure класс не может быть расширен как его финал.

стоит отметить, что это не будет работать для PHP версий 5.3.21-5.3.29.

в любой из этих версий вы получите результат:

Привет, Мир! Уловимая фатальная ошибка: Аргумент 1, передаваемый в callFunc2 (), должен быть экземпляром > Callable, экземпляром заданного закрытия, вызываемым в /в/kqeYD в строке 16 и определяемым в /в/kqeYD в строке 7

процесс завершается с кодом 255.

можно попробовать через https://3v4l.org/kqeYD#v5321

с уважением,