PHP'de Closure Kullanımı

PHP
Closure, PHP'ye 5.3.0 ile gelmiş olan önemli bir özellik. Tam olarak ne olduğunu tanımlamam biraz zor, ancak tanımlamaya çalışacak olursam, isimsiz, fonksiyon görünümlü objeler olduklarını söyleyebilirim. Bu tanım size garip gelmiş olabilir, ama örnekleri inceleyince daha net anlayabileceğinizi düşünüyorum.

Örneğin, alttaki gibi bir çağrıyı bir yerde görmüş ve anlam verememiş olabilirsiniz.
$a = function() {};
var_dump($a); // class Closure#1 (0) {}
Veya alttaki fonksiyonun aldığı ikinci parametredeki, callback parametresindeki anonim fonksiyon kafanızı karıştırmış olabilir.
$greaterThanThree = array_filter([1,2,3,4,5], function ($v) { 
return $v > 3; // 3'ten büyük olan dizi değerlerini döndürür
});
var_dump($greaterThanThree); // array(2) {  [3] =>  int(4),  [4] =>  int(5) }
Ya da alttaki gibi bir kullanım dikkatinizi çekmiş olabilir.
$incrementNumbers = array_map(function ($number) {
return ++$number; // dizinin her bir üyesinin değerini 1 arttırır
}, [1,2,3,4,5]);
var_dump($incrementNumbers); // array(5) {  [0] =>  int(2), [1] =>  int(3),  [2] =>  int(4),  [3] =>  int(5),  [4] =>  int(6) }
Üstteki örneklerde olduğu gibi, callback çağrılarında kullanabileceğiniz, direkt olarak belirtilebilen, sadece bulunduğu scope'ta(kapsamda) tanımlı, çağrıldığı anda tanımlanan, Closure class'ına ait olan bu örnekler, closure veya anonim fonksiyon olarak PHP'de kullanılmakta.

Örneğin üstte array_map ile yapılan işlem için alttaki gibi bir global fonksiyon da yazabilir ve onu da çağırabilirdiniz.
function increment($value) 
{
return ++$value;
}
$incrementNumbers = array_map('increment', [1,2,3,4,5]);
Ancak düşünün ki üstteki arttırma işlemine, uygulamanızın sadece belli bir bölgesinde, tek seferliğine ihtiyacınız var. Bu ve bunun gibi durumlarda, global scope'ta tanımlı olacak bir fonksiyon yazmanız pek iyi bir tercih değildir. Bu yüzden, kullanıldıktan sonra geçerliliğini yitireceği için, aynı zamanda gereksiz yere uygulamanızda yer kaplamayacak olmasından ötürü, aklınıza ilk gelen, anonim fonksiyon kullanımı olmalıdır.

Closure kullanımında, global scope'taki değişkenlere erişebilmek için de, use kullanmanız yeterli.
$name = "Burak";
$surname = "Özdemir";
$variable = 1;

$ahoy = function() use ($name, $surname, $variable) {
    echo "Ahoy $name $surname \n";
    $another = 1;
    $variable = 2;
};

$ahoy(); // "Ahoy Burak Özdemir"
echo $variable; // 1
echo $another; // Undefined variable: another
Üstteki örneği incelerseniz, scope(kapsam) ile ne belirtmek istediğimi daha iyi anlayabilirsiniz. $another değişkeni, sadece anonim fonksiyonun scope aralığında tanımlanıp erişilebilir, fonksiyon çağrısı gerçekleştikten sonra kendisini yok edeceği için, global scope'ta ulaşmaya çalıştığınızda undefined variable hatası verecektir. Aynı şekilde, $variable değişkenini her ne kadar değiştirdiğinizi zannetseniz de, referansını yollamadığınız sürece(use ($name, $surname, &$variable)), bu değerin kopyası, anonim fonksiyon içerisinde kullanılır ve değiştirmeniz mümkün olmaz.

Bu örnekleri saçma bulmuş olabilirsiniz, ancak herhangi bir framework'un kaynak kodlarını incelerseniz, ne kadar değerli olduklarını görebilirsiniz. Daha önemli ve geçerli bir closure kullanımı ise, Closure class'ının sahip olduğu bind() ve bindTo() yöntemleri ile ilgili.

PHP 5.4.0 ve sonrasında, bind() ve bindTo() yöntemleriyle Closure objesinin iç yapısını, private ve protected özellikleri dahil, bir başka objeye aktarabileceğiniz gibi, Closure'u yeni bir scope'a taşıyabilirsiniz.
class Country { 
private $name = "Turkey";
}

$visit = function (Country $country) {
return $country->name;
};
$country = new Country();
echo $visit($country); //  Cannot access private property Country::$name
Country class'ının herhangi bir objesinin $name değerine ulaşmanız mümkün değil. Ancak bind() veya bindTo() ile Closure'u Country class'ının scope'una dahil edebilir, bu durumda da $name değerine ulaşabilirsiniz.
$visit = $visit->bindTo(null, $country); // veya $visit = Closure::bind($visit, null, $country); 
echo $visit($country); // Turkey
Bu örneği biraz daha geliştirelim.
class Country {
    private $name = "Turkey";
    public  $abbr = "tr";

    public function getClosure() {
        return function ($property) {
            return $this->$property;
        };
    }

}

class City {
    public  $name = "Fethiye";
    private $code = 82;
}

$country = new Country();
$closure = $country->getClosure();
echo $closure('name'); // Turkey
echo $closure('abbr'); // tr

$city = new City();
$closure_2 = $closure->bindTo($city); // Closure::bind($closure, $city);
echo $closure_2('name'); // Fethiye
echo $closure_2('code'); // Cannot access private property City::$code

$closure_3  = $closure->bindTo($city, $city); // Closure::bind($closure, $city, $city);
echo $closure_3('code'); // 82
Üstteki örnekten de görebileceğiniz gibi, $closure_2 ve $closure_3 değişkenlerinin nasıl atandıklarına dikkat edin, buradaki bindTo() yönteminin ilk parametresi, $this'e atanacak olan nesneyi, ikinci parametre, scope'u belirtiyor, $city ataması yaparsak, bu nesnenin ait olduğu Class olan City değerini alacak. Dolayısıyla ikinci parametrede scope'u belirtmezseniz(default değeri 'static', yani aynısı, bu durumda Country olarak kalıyor), şehre ait olan private veya protected değerlere ulaşamazsınız.

Sonuç olarak, birçok modern projede kullanılan, birçok framework'te karşılacağınız closure kullanımı önemli olup, korkmanız gereken bir özellik değildir.