# 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.

- Yayın: 2017-06-04
- Kategori: Diller
- Etiketler: PHP
- Okuma süresi: 4 dk okuma
- Kaynak: https://www.muhammetsafak.com.tr/blog/phpde-closure-ve-use-ile-degisken-yakalama/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
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.

```php
$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:

```php
$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.

```php
$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.

```php
$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:

```php
$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:

```php
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:

```php
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](/blog/php-7-de-skaler-tip-bildirimleri-ve-donus-tipleri) için `callable` veya `Closure` kullanılır:

```php
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:

```php
$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.
