PHP: Tür ipucu - "Closure" ve "Callable" arasındaki fark


128

Geri çağırma işlevinin çalışmasını beklersek, tür ipucu olarak Closureveya bunlardan birini kullanabileceğimi fark ettim Callable. Örneğin:

function callFunc1(Closure $closure) {
    $closure();
}

function callFunc2(Callable $callback) {
    $callback();
}

$function = function() {
    echo 'Hello, World!';
};

callFunc1($function); // Hello, World!
callFunc2($function); // Hello, World!

Soru:

Buradaki fark nedir? Başka bir deyişle, ne zaman kullanılmalı Closureve ne zaman kullanılmalı CallableVEYA aynı amaca hizmet etmektedir?

Yanıtlar:


173

Aradaki fark, Closurea'nın anonim bir işlev olması gerektiğidir, burada callablenormal bir işlev de olabilir.

Bunu aşağıdaki örnekle görebilir / test edebilirsiniz ve ilkinde bir hata alacağınızı göreceksiniz:

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!

Bu nedenle, yalnızca ipucu anonim işlev yazmak Closureistiyorsanız : ve ayrıca normal işlevlere izin vermek istiyorsanız callabletür ipucu olarak kullanın .


5
Ayrıca, ["Foo", "bar"]for Foo::barveya [$foo, "bar"]for gibi bir dizi geçirerek çağrılabilir sınıf yöntemlerini de kullanabilirsiniz $foo->bar.
Andrea

17
Kapalı konu ama related: PHP 7.1 beri, artık kolayca Kapatma için dönüştürebilirsiniz: callFunc1(Closure::fromCallable("xy")). wiki.php.net/rfc/closurefromcallable
nevvermind

Hala neden yalnızca anonim işlevi çağırmak isteyeyim anlamıyorum. Kodu paylaşırsam, fonksiyonun nereden geldiği umurumda olmaz. Bence PHP'nin tuhaf yönlerinden biri, kafa karışıklığını önlemek için bir veya başka bir yolu kaldırmalı. Ama dürüst olmak gerekirse Closure+ Closure::fromCallableyaklaşımını seviyorum , çünkü dizge veya dizi callableher zaman garipti.
Robo Robok

2
@RoboRobok'un Closureaksine yalnızca (anonim işlev) gerektirmesinin bir nedeni callable, çağrılan işlevin kapsamı dışındaki erişimi engellemektir. Örneğin, bir private methodhesabınız olduğunda, bir callable. Bakınız: 3v4l.org/7TSf2
fyrye

59

Aralarındaki temel fark şudur: closure bir sınıf ve callablebir tür olmasıdır .

callableTipi edilebilir bir şey kabul adlandırılan :

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

Nerede bir closureirade yalnızca anonim bir işlevi kabul. PHP sürüm 7.1 size şöyle bir kapatılmasına fonksiyonlarını dönüştürebilirsiniz unutmayın: Closure::fromCallable('functionName').


Misal:

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
}

Öyleyse neden bir closureüst kullancallable ?

Kısıtlama bir nedeni closurebazı ek yöntemler vardır bir amacıdır: call(), bind()vebindto() . Bir sınıfın dışında bildirilen bir işlevi kullanmanıza ve bir sınıfın içindeymiş gibi çalıştırmanıza izin verir.

$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

Ölümcül hatalara neden olacağından, normal bir işlevde yöntemler çağırmak istemezsiniz. Yani bunu aşmak için şöyle bir şey yazmanız gerekir:

if($cb instanceof \Closure){}

Bu kontrolü her seferinde yapmak anlamsızdır. Dolayısıyla, bu yöntemleri kullanmak istiyorsanız, argümanın birclosure . Aksi takdirde sadece normal kullanın callback. Bu yoldan; Kodunuz yerine işlev çağrısında bir hata ortaya çıkar ve bu da teşhis edilmesini çok daha kolay hale getirir.

Bir yan not:closure sınıf onun kadar uzatılamaz nihai .


1
Bir çağrılabilir özelliği başka kapsamlarda da yeniden kullanabilirsiniz.
Bimal Poudel

Bu callable, herhangi bir ad alanında kalifiye olmanız gerekmediği anlamına gelir .
Jeff Puckett

0

Bunun PHP 5.3.21'den 5.3.29'a kadar olan sürümleri için çalışmayacağını belirtmekte fayda var.

Bu sürümlerin herhangi birinde aşağıdaki gibi bir çıktı alacaksınız:

Selam Dünya! Yakalanabilir ölümcül hata: callFunc2 () 'ye aktarılan bağımsız değişken,> Çağrılabilir'in bir örneği, verilen Kapatma örneği, 16. satırda / in / kqeYD içinde çağrılan ve 7. satırda / in / kqeYD içinde tanımlanmış olmalıdır

255 koduyla işlemden çıkıldı.

Bunu kullanarak deneyebilirsin https://3v4l.org/kqeYD#v5321

Saygılarımla,


2
Koda bir bağlantı göndermek yerine, başka birinin bu sorunla karşılaşması ve sağladığınız bağlantıda kesinti olması durumunda kodu buraya göndermelisiniz. Ayrıca, yardım için yayınınızda çıktıyı sağlayabilirsiniz.
Vedda

5
Bunun nedeni callablePHP 5.4'te tanıtılmış olmasıdır. Bu PHP öncesinde adında bir sınıfın örneğini bekliyor callableiçin bir ipucu belirtilen etmişti sadece sanki PDO, DateTimeya da \My\Random\ClassName.
Davey Shafik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.