İçeriğe geç
Muhammet Şafak
Diller 4 dk okuma

PHP'de closure ve use ile değişken yakalama

PHP'de closure (anonim fonksiyon) nedir, use anahtar kelimesiyle değişken yakalama nasıl çalışır; günlük kullanım örnekleri.


PHP 5.3 ile dile giren closure (anonim fonksiyon), yani bir değişkene atanabilen, başka bir fonksiyona parametre olarak gönderilebilen fonksiyon yapısı, zamanla günlük PHP kodumun ayrılmaz parçası haline geldi. Özellikle dizi işlemlerinde ve callback gerektiren yerlerde ne kadar işlevsel olduğunu fark ettikçe daha çok kullandım.

Ama closure’ın tek başına anlaşılması yetmiyor; use ile değişken yakalama meselesini de anlamak gerekiyor. Bunu atlayan kişi, neden beklenen verinin fonksiyon içinde görünmediğini anlayamıyor.

Closure nedir?

En yalın haliyle: ismi olmayan, bir değişkene atanabilen fonksiyon.

$greet = function (string $name): string {
    return 'Merhaba, ' . $name;
};

echo $greet('Ahmet'); // Merhaba, Ahmet

Burada $greet bir Closure nesnesi tutuyor. Bunu başka bir fonksiyona geçirebilirsiniz:

$names = ['Ahmet', 'Mehmet', 'Ayşe'];

$greeted = array_map(function (string $name) {
    return 'Merhaba, ' . $name;
}, $names);

array_map, array_filter, usort gibi yerleşik fonksiyonlar bir callback parametresi alır; closure bu parametreye doğal oturur.

use ile değişken yakalama

Closure, tanımlandığı scope’taki değişkenlere otomatik olarak erişemez. PHP bu konuda JavaScript’ten farklı davranır. Dışarıdaki bir değişkeni closure içinde kullanmak için use anahtar kelimesiyle açıkça yakalamak gerekir.

$discount = 0.10;

$applyDiscount = function (float $price) use ($discount): float {
    return $price * (1 - $discount);
};

echo $applyDiscount(100); // 90

use ($discount) olmadan $discount undefined variable hatası verir.

Bu farkın neden var olduğunu anlayınca mantıklı geliyor: PHP, kapsamı kontrol altında tutmak için tasarlanmış. Closure’ın çevresindeki tüm değişkenlere sessizce erişmesine izin verseydi, büyük fonksiyonlarda hangi dış verinin kullanıldığını takip etmek zorlaşırdı. use listesi, closure’ın dışarıya olan bağımlılıklarını açıkça belgeler.

Değere göre ve referansla yakalama

Varsayılan davranış by value yakalamadır: closure tanımlandığı andaki değeri alır, sonradan değişkenin değeri değişse closure etkilenmez.

$multiplier = 2;

$double = function (int $n) use ($multiplier): int {
    return $n * $multiplier;
};

$multiplier = 5; // Bu değişiklik $double'ı etkilemez

echo $double(10); // 20, çünkü tanım anındaki değer (2) kullanıldı

Referansla (by reference) yakalamak için use (&$multiplier) yazılır. Bu daha nadir ihtiyaç duyulan bir durum; genellikle closure’ın dışarıdaki durumu değiştirmesini istediğinizde kullanılır.

Referansla yakalamanın klasik kullanımı sayaç veya accumulator kalıplarıdır:

$count = 0;
$increment = function () use (&$count): void {
    $count++;
};

$increment();
$increment();
echo $count; // 2

Ama bu kalıbı dikkatli kullanmak gerekiyor. Closure’ın dış durumu değiştirmesi, side effect’leri takip etmeyi güçleştirebilir. Genellikle değer döndürmek ve dışarıda biriktirmek daha okunabilir.

Gerçek bir kullanım: dizi filtreleme

Pratikte en sık kullandığım yer: dinamik filtreler. Bir filtre kriteri dışarıdan geliyor, closure içinde kullanılıyor:

function filterByStatus(array $orders, string $status): array
{
    return array_filter($orders, function (array $order) use ($status): bool {
        return $order['status'] === $status;
    });
}

$pendingOrders = filterByStatus($orders, 'pending');

$status değişkeni use ile yakalanmasaydı closure içinde $status’a erişmek mümkün olmayacaktı. Bu yapı hem okunur hem de tekrar kullanılabilir.

Aynı mantığı birden fazla kriter için de genişletebilirsiniz:

function filterOrders(array $orders, string $status, ?string $city = null): array
{
    return array_filter($orders, function (array $order) use ($status, $city): bool {
        if ($order['status'] !== $status) {
            return false;
        }
        if ($city !== null && $order['city'] !== $city) {
            return false;
        }
        return true;
    });
}

use listesine birden fazla değişken yazılabiliyor; aralarına virgül koyuyorsunuz.

Closure’ı fonksiyona parametre olarak göndermek

Kendi yazdığınız fonksiyonlar da closure parametre alabilir. Tip bildirimi için callable veya Closure kullanılır:

function applyToEach(array $items, Closure $fn): array
{
    $result = [];
    foreach ($items as $key => $item) {
        $result[$key] = $fn($item);
    }
    return $result;
}

$prices = [100, 200, 150];
$discounted = applyToEach($prices, function (float $price) {
    return $price * 0.9;
});

Bu yapı, işlemi soyutlamanızı sağlar. Döngü mantığı applyToEach içinde, dönüşüm mantığı dışarıdan geliyor.

callable ve Closure arasındaki fark şudur: callable string fonksiyon adlarını ve [$nesne, 'metod'] dizilerini de kabul eder; Closure yalnızca anonim fonksiyonları kabul eder. Tip güvenliği açısından Closure daha kısıtlayıcıdır, ama kasıtlı olarak yalnızca anonim fonksiyon bekliyorsanız niyeti daha net ifade eder.

PHP 7.4’ten itibaren: kısa closure sözdizimi

PHP 7.4’te gelen arrow function’lar closure yazımını kısalttı. Tek ifadeli closure’lar için fn anahtar kelimesi kullanılıyor ve use yazmak gerekmiyor — dış scope’a otomatik erişim sağlanıyor:

$discount = 0.10;
$applyDiscount = fn(float $price): float => $price * (1 - $discount);

Bu yazının tarihinde (2017) ok fonksiyonları henüz dilde yoktu; ama günümüzde basit tek-ifadeli closure’lar için bu sözdizimi daha yaygın. use ile yakalama ise çok satırlı closure’larda hâlâ geçerli ve gerekli.

Closure’ları öğrenirken en büyük tuzak use ile yakalama meselesini atlamak. Bir kez anlaşıldığında dizi işlemleri, olay dinleyicileri ve callback gerektiren her yerde kodu daha kısa ve daha niyetli hale getiriyor.

Etiketler: #PHP
Paylaş:

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi