PHP : 타입 힌트-`Closure`와`Callable`의 차이점


128

콜백 함수가 실행될 것으로 예상되면 유형 힌트 로 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하는지 또는 그들은 동일한 목적을 제공합니까?

답변:


173

차이점은 a 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타입 힌트를 사용하십시오.


5
["Foo", "bar"]for Foo::bar또는 [$foo, "bar"]for 와 같은 배열을 전달하여 callable과 함께 클래스 메서드를 사용할 수도 있습니다 $foo->bar.
Andrea

17
주제가 아닌 관련 : PHP 7.1부터는 이제 Closure :로 쉽게 변환 할 수 있습니다 callFunc1(Closure::fromCallable("xy")). wiki.php.net/rfc/closurefromcallable
nevvermind

나는 왜 익명 함수 만 호출하고 싶은지 알지 못합니다. 코드를 공유하면 함수의 출처를 신경 쓰지 않아야합니다. PHP의 단점 중 하나는 혼란을 피하기 위해 하나 또는 다른 방법을 제거해야한다고 생각합니다. 그러나 문자열이나 배열 이 항상 이상 했기 때문에 솔직히 Closure+ Closure::fromCallable접근법을 좋아합니다 callable.
Robo Robok

2
@RoboRobok Closure과 반대되는 (익명 함수) 만 요구하는 한 가지 이유 callable는 호출 된 함수의 범위를 벗어난 액세스를 방지하는 것입니다. 예를 들어,가있는 사람은 a private method를 학대하는 사람이 액세스하기를 원하지 않습니다 callable. 참조 : 3v4l.org/7TSf2
fyrye

58

그들 사이의 가장 큰 차이점은이 있다는 것입니다 closureA는 클래스유형 .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합니까?

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. 그렇지 않으면 normal을 사용하십시오 callback. 이 방법; 코드 대신 함수 호출에서 오류가 발생하여 훨씬 쉽게 진단 할 수 있습니다.

보조 노트에 :closure 클래스는 그것으로 확장 할 수 없습니다 마지막 .


1
다른 범위에서도 호출 가능 항목을 재사용 할 수 있습니다.
Bimal Poudel

즉, callable네임 스페이스를 사용할 필요가 없습니다 .
Jeff Puckett

0

PHP 5.3.21에서 5.3.29까지는 작동하지 않습니다.

이러한 버전 중 하나에서 다음과 같은 출력을 얻을 수 있습니다.

안녕, 월드! 캐치 가능한 치명적 오류 : callFunc2 ()에 전달 된 인수 1은> Callable, Closure 인스턴스가 있어야하며, 16 번째 줄의 / in / kqeYD에서 호출되고 7 번째 줄의 / in / kqeYD에 정의되어야합니다.

코드 255로 프로세스가 종료되었습니다.

https://3v4l.org/kqeYD#v5321 을 사용하여 시도해 볼 수 있습니다

친애하는,


2
코드에 대한 링크를 게시하는 대신 다른 사람이이 문제를 겪고 제공 한 링크가 끊어 질 경우 여기에 코드를 게시해야합니다. 게시물에 출력을 제공하여 도움을 줄 수도 있습니다.
Vedda

5
callablePHP 5.4에서 소개 되었기 때문 입니다. 그 PHP 이전라는 클래스의 인스턴스를 기대하고있다 callable당신이 힌트를 지정했던 것처럼, PDO, DateTime, 또는 \My\Random\ClassName.
Davey Shafik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.