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.