PHP'de adı olmayan fonksiyonlara anonim fonksiyon ya da closure denir: $kare = function ($x) { return $x * $x; };. Bunlar bir değişkende saklanabilir, başka fonksiyonlara argüman olarak verilebilir ve use ile tanımlandıkları kapsamdaki değişkenleri yakalayabilir. PHP 7.4'te gelen arrow function (fn($x) => $x * 2) aynı işin kısa yazımıdır ve dış değişkenleri otomatik olarak değerle yakalar. PHP 8.1'den beri strlen(...) gibi first-class callable yazımıyla mevcut bir fonksiyondan da closure üretebilirsiniz.
Bu yazıda üç yazımı karşılaştırıyor, use ile değer ve referans yakalama arasındaki farkı, sınıf içinde $this'in otomatik bağlanmasını, static closure'ları ve callable tipini anlatıyoruz. Örnekler PHP 8.5.12 ile çalıştırıldı. Normal fonksiyon tanımlama için PHP fonksiyon tanımlama, serinin tamamı için PHP öğrenme rehberi sayfasına bakabilirsiniz.
Anonim fonksiyon nedir, nerede kullanılır?
Kılavuza göre anonim fonksiyonlar en çok callable bekleyen parametrelere değer olarak verildiğinde işe yarar. array_map(), array_filter(), usort() ve preg_replace_callback() gibi fonksiyonlar, her eleman için çağıracakları bir fonksiyon ister; bu fonksiyonu ayrı bir adla tanımlamak yerine çağrının içinde yazabilirsiniz. PHP bu ifadeleri arka planda Closure sınıfının bir örneğine çevirir.
Bir closure'ı değişkene atarken sözdizimi sıradan bir atamayla aynıdır; bu yüzden kapanan süslü parantezden sonra noktalı virgül gerekir. Unutulursa sonraki satırda sözdizimi hatası alırsınız.
<?php
$kare = function ($x) {
return $x * $x;
};
echo $kare(6), PHP_EOL; // çıktı: 36
echo get_class($kare), PHP_EOL; // çıktı: Closure
$sonuc = array_map(function ($ad) {
return ucfirst($ad);
}, ['ali', 'veli', 'can']);
echo implode(', ', $sonuc), PHP_EOL; // çıktı: Ali, Veli, CanDizi fonksiyonlarının callback ile nasıl kullanıldığı array_map, array_filter ve array_reduce, sıralamada karşılaştırma fonksiyonu yazmak ise PHP dizi sıralama yazısında anlatılıyor.
use ile dış değişken yakalamak
Normal bir fonksiyon gibi closure da kendi kapsamına sahiptir; dışarıda tanımlı bir değişkeni doğrudan göremez. Bir dış değişkeni kullanmak için onu use listesine yazarsınız. Kılavuzun vurguladığı kritik nokta şudur: Değerle yakalanan değişkenin değeri closure'ın tanımlandığı andaki değerdir, çağrıldığı andaki değil. Dış değişkenin sonraki değişikliklerini görmek ya da closure içinden dış değişkeni değiştirmek istiyorsanız use (&$degisken) ile referansla yakalarsınız.
<?php
$oran = 10;
$indirim = function ($fiyat) use ($oran) {
return $fiyat - $fiyat * $oran / 100;
};
$oran = 50;
echo $indirim(200), PHP_EOL; // çıktı: 180 (oran hâlâ 10)
$sayac = 0;
$artir = function () use (&$sayac) {
$sayac++;
};
$artir();
$artir();
echo $sayac, PHP_EOL; // çıktı: 2İlk örnekte $oran 50 yapılmasına rağmen closure 10 ile hesap yaptı, çünkü değer tanım anında kopyalandı. İkinci örnekte referansla yakalama sayesinde closure dışarıdaki $sayac değişkenini artırabildi. use yazmayı unutursanız değişken closure içinde tanımsız olur:
<?php
$mesaj = 'merhaba';
$f = function () {
return $mesaj;
};
var_dump($f());
/* çıktı:
Warning: Undefined variable $mesaj in /yol/ornek.php on line 4
NULL
*/Kılavuz use listesine dair birkaç kural daha sayar: PHP 7.1'den beri süper global değişkenler, $this ve bir parametreyle aynı ada sahip değişkenler yakalanamaz. PHP 8.0'dan beri listenin sonunda fazladan virgül bırakılabilir. Closure bir dönüş tipi bildiriyorsa bu tip use bölümünden sonra yazılır: function () use ($x): array { ... }.
Arrow function: fn ile kısa yazım

Arrow function'lar fn (parametreler) => ifade biçimindedir ve PHP 7.4'ten beri kullanılabilir. Anonim fonksiyonlarla aynı Closure sınıfını kullanırlar ve aynı özellikleri destekler; tek fark dış kapsamdaki değişkenleri kullanmanın her zaman otomatik olmasıdır. İfadede kullandığınız ve dış kapsamda tanımlı olan her değişken, sanki use ($degisken) yazmışsınız gibi değerle yakalanır. Gövde tek bir ifadedir ve sonucu otomatik olarak döndürülür; return yazılmaz.
<?php
$kdv = 0.20;
$brut = fn($net) => $net * (1 + $kdv);
echo $brut(100), PHP_EOL; // çıktı: 120
$carpan = fn($a) => fn($b) => $a * $b;
echo $carpan(3)(7), PHP_EOL; // çıktı: 21
$x = 1;
$dene = fn() => $x++;
$dene();
echo $x, PHP_EOL; // çıktı: 1 (dışarıdaki $x değişmedi)
$tekler = array_filter([1, 2, 3, 4, 5], fn($n) => $n % 2 === 1);
print_r($tekler);
/* çıktı:
Array
(
[0] => 1
[2] => 3
[4] => 5
)
*/İç içe arrow function'larda da yakalama otomatik çalışır: İçteki fn($b), dıştakinin parametresi $a'yı görür. Üçüncü örnek önemli bir sınırı gösteriyor: Yakalama değerle olduğu için arrow function dış değişkeni değiştiremez. Dışarıdaki bir değeri güncellemeniz gerekiyorsa use (&$x) içeren klasik anonim fonksiyona dönmelisiniz. Arrow function'lar parametre ve dönüş tipleri, varsayılan değerler, variadic parametre ve referansla parametre gibi tüm imza özelliklerini destekler: static fn(int $x): int => $x * 2 geçerli bir yazımdır.
| Özellik | function () use (...) | fn () => |
|---|---|---|
| Gövde | Birden çok deyim, return gerekir | Tek ifade, sonuç otomatik döner |
| Dış değişken | Yalnızca use ile açıkça | Otomatik, kullanılan her değişken |
| Yakalama türü | Değerle ya da &$x ile referansla | Her zaman değerle |
| Dış değişkeni değiştirme | use (&$x) ile mümkün | Mümkün değil |
| Sınıf içinde $this | Otomatik bağlanır | Otomatik bağlanır |
| Sürüm | PHP 5.3 | PHP 7.4 |
Sınıf içinde closure: $this ve static

Bir sınıfın metodu içinde tanımlanan closure'a o anki nesne otomatik olarak bağlanır; closure içinde $this kullanabilir, hatta private metotlara erişebilirsiniz. Bu, callback içinden nesnenin kendi yardımcı metotlarını çağırmayı kolaylaştırır:
<?php
class Sepet
{
private array $urunler = ['kalem' => 3, 'silgi' => 0, 'defter' => 5];
public function stoktakiler(): array
{
return array_keys(array_filter($this->urunler, function ($adet) {
return $adet > 0 && $this->gecerli($adet);
}));
}
private function gecerli(int $adet): bool
{
return $adet < 100;
}
}
print_r((new Sepet)->stoktakiler());
/* çıktı:
Array
(
[0] => kalem
[1] => defter
)
*/Bu otomatik bağlamayı istemiyorsanız closure'ı static olarak tanımlarsınız: static function () { ... } veya static fn() => .... Static closure'a ne tanım anında ne de sonradan bir nesne bağlanabilir. İçeride $this kullanmaya kalkarsanız Using $this when not in object context hatası alırsınız. Nesneye ihtiyaç duymayan callback'leri static yazmak, closure'ın nesneyi gereksiz yere tutmasını önler.
<?php
class Rapor
{
public function olustur()
{
$f = static function () {
return isset($this) ? 'nesne bağlı' : 'nesne bağlı değil';
};
return $f();
}
}
echo (new Rapor)->olustur(), PHP_EOL; // çıktı: nesne bağlı değilClosure sınıfı, bağlamayı elle değiştirmek için bind(), bindTo() ve call() metotlarını sunar. Örneğin $oku->call(new Hesap), closure'ı geçici olarak verilen nesneye bağlayıp hemen çalıştırır ve closure içinden o nesnenin private özelliklerine erişilebilir. Bu araçlar test ve çerçeve kodlarında görülür; günlük kodda nadiren gerekir. Ayrıca kılavuz, closure nesnelerinin serileştirilemeyeceğini belirtir; serialize() denemesi Serialization of 'Closure' is not allowed mesajlı bir Exception fırlatır.
First-class callable: strlen(...) yazımı
PHP 8.1 ile gelen first-class callable yazımı, çağrılabilir herhangi bir ifadenin sonuna (...) ekleyerek ondan bir Closure üretir. Buradaki üç nokta bir kısaltma değil, sözdiziminin kendisidir. Fonksiyonlar, nesne metotları, statik metotlar ve __invoke içeren nesneler bu yolla closure'a çevrilebilir. Kılavuz bu yazımın, metin ve dizi biçimindeki eski callable gösterimlerinin yerini aldığını ve statik analiz araçlarınca anlaşılabildiğini belirtir.
<?php
$uzunluk = strlen(...);
echo $uzunluk('kitap'), PHP_EOL; // çıktı: 5
var_dump($uzunluk instanceof Closure); // çıktı: bool(true)
class Metin
{
public function __construct(private string $onek) {}
public function ekle(string $s): string
{
return $this->onek . $s;
}
public static function buyut(string $s): string
{
return strtoupper($s);
}
}
$m = new Metin('#');
echo implode(',', array_map($m->ekle(...), ['a', 'b'])), PHP_EOL; // çıktı: #a,#b
echo implode(',', array_map(Metin::buyut(...), ['x', 'y'])), PHP_EOL; // çıktı: X,YBu yazımın eski [$nesne, 'metot'] dizisine göre önemli bir avantajı vardır: Closure, oluşturulduğu yerin kapsamını kullanır. Bir sınıf private bir metodunu closure olarak dışarı verirse, bu closure dışarıdan çağrıldığında da çalışır. Dizi biçimindeki callable ise çağrıldığı yerde çözümlenir ve görünürlük kontrolü o noktada yapılır:
<?php
class Kasa
{
public function dizi()
{
return [$this, 'gizli'];
}
public function closure()
{
return $this->gizli(...);
}
private function gizli()
{
return 'gizli metot çalıştı';
}
}
$k = new Kasa;
echo ($k->closure())(), PHP_EOL; // çıktı: gizli metot çalıştı
echo ($k->dizi())(), PHP_EOL;
// Fatal error: Uncaught Error: Call to private method Kasa::gizli() from global scope in /yol/ornek.php:21Kılavuz iki kısıtı da not eder: new Sinif(...) ile nesne oluşturma bu yazımla desteklenmez, çünkü new bir çağrı sayılmaz. Nullsafe operatörüyle birleştirmek ($nesne?->metot(...)) de derleme hatasıdır. Sınıf, metot ve görünürlük kuralları için PHP sınıf ve nesne ile public, private, protected yazılarına bakabilirsiniz.
callable tipi ve çağrılabilir değer türleri
callable, bir fonksiyon veya metodu temsil eden ve çağrılabilen değerler için kullanılan tip bildirimidir. Kılavuz dört çağrılabilir türü sayar: bir Closure nesnesi; bir fonksiyon veya 'Sinif::metot' biçiminde statik metot adı içeren metin; ilk elemanı sınıf adı ya da nesne, ikinci elemanı metot adı olan dizi; ve __invoke() sihirli metodunu uygulayan bir nesne.
<?php
function uygula(callable $islem, array $veri): array
{
$sonuc = [];
foreach ($veri as $v) {
$sonuc[] = $islem($v);
}
return $sonuc;
}
class Araclar
{
public static function iki($x) { return $x * 2; }
public function uc($x) { return $x * 3; }
}
class Ondalik
{
public function __invoke($x) { return $x / 10; }
}
echo implode(' ', uygula('abs', [-1, -2])), PHP_EOL; // çıktı: 1 2
echo implode(' ', uygula('Araclar::iki', [1, 2])), PHP_EOL; // çıktı: 2 4
echo implode(' ', uygula([new Araclar, 'uc'], [1, 2])), PHP_EOL; // çıktı: 3 6
echo implode(' ', uygula(new Ondalik, [5, 25])), PHP_EOL; // çıktı: 0.5 2.5
echo implode(' ', uygula(fn($x) => $x + 1, [1, 2])), PHP_EOL; // çıktı: 2 3
var_dump(is_callable('yok_boyle_fonksiyon')); // çıktı: bool(false)
echo call_user_func('str_repeat', 'ab', 3), PHP_EOL; // çıktı: abababÇağrılamayan bir değer callable parametresine verilirse TypeError fırlatılır: uygula(): Argument #1 ($islem) must be of type callable, string given. Kılavuza göre callable sınıf özelliği tipi olarak kullanılamaz; PHP 8.5.12 bu durumda Property Gorev::$isle cannot have type callable hatasıyla derlemeyi durdurur. Özellikte callback saklamak istiyorsanız tip olarak Closure yazın. callable tipiyle fonksiyonun imzası da belirtilemez; hangi parametreleri alacağını belgelemek size kalır.
Özyinelemeli closure ve PHP 8.5 notu
Bir closure'ın kendini çağırması için klasik yöntem, closure'ı tuttuğu değişkeni referansla yakalamaktır: function ($n) use (&$fib) { ... }. Referans gerekir, çünkü değerle yakalama anında $fib henüz atanmamıştır. PHP 8.5 ile gelen Closure::getCurrent() ise o anda çalışan closure'ı döndürür ve bu referans hilesine gerek bırakmaz. Bu metot PHP 8.5 veya üstünü gerektirir; birçok sunucu hâlâ daha eski sürümlerde çalıştığı için projenizin sürümünü kontrol edin.
<?php
// PHP 8.5 ve üstü
$fib = function (int $n) {
if ($n < 2) {
return $n;
}
$kendisi = Closure::getCurrent();
return $kendisi($n - 1) + $kendisi($n - 2);
};
echo $fib(10), PHP_EOL; // çıktı: 55
// Her sürümde çalışan klasik yol
$fib2 = function (int $n) use (&$fib2) {
return $n < 2 ? $n : $fib2($n - 1) + $fib2($n - 2);
};
echo $fib2(10), PHP_EOL; // çıktı: 55Kılavuz getCurrent() metodunun closure dışında çağrılırsa Error fırlattığını belirtir. PHP 8.5 ile closure ve first-class callable ifadeleri sabit ifadelerde (örneğin özellik varsayılan değerleri ve attribute parametreleri) de kullanılabilir hale geldi. Diğer 8.5 yenilikleri için PHP 8.5 yenilikler yazısına bakabilirsiniz.
Küçük uygulama: fiyat filtresi üreten fonksiyon
Closure'ların en pratik kullanımlarından biri, ayarı dışarıdan alan küçük fonksiyonlar üretmektir. Aşağıda filtreOlustur() bir sınır değeri alıyor ve o sınırı hatırlayan bir arrow function döndürüyor. Ardından array_walk() içinde referansla yakalanan $toplam değişkeni güncelleniyor; bu, arrow function ile yapılamayacak bir iştir.
<?php
$urunler = [
['ad' => 'Çay', 'fiyat' => 85],
['ad' => 'Kahve', 'fiyat' => 240],
['ad' => 'Şeker', 'fiyat' => 60],
['ad' => 'Bal', 'fiyat' => 410],
];
function filtreOlustur(int $sinir): Closure
{
return fn(array $u) => $u['fiyat'] >= $sinir;
}
$pahali = filtreOlustur(200);
$secilenler = array_filter($urunler, $pahali);
$adlar = array_map(fn($u) => $u['ad'], $secilenler);
echo implode(', ', $adlar), PHP_EOL; // çıktı: Kahve, Bal
$toplam = 0;
array_walk($secilenler, function ($u) use (&$toplam) {
$toplam += $u['fiyat'];
});
echo "Toplam: $toplam TL", PHP_EOL; // çıktı: Toplam: 650 TLfiltreOlustur(200) her çağrıldığında yeni bir closure döner ve her biri kendi $sinir değerini saklar; filtreOlustur(50) ile ikinci bir filtre üretseniz ilki etkilenmez. Closure'ları bir dizide saklayarak küçük bir işlem tablosu da kurabilirsiniz. Anahtar bir işlem adı, değer ise o işi yapan fonksiyon olur. Tek satırlık işler için arrow function, kontrol gerektiren işler için klasik anonim fonksiyon kullanılabilir; ikisi de aynı dizide yan yana durabilir:
<?php
$islemler = [
'topla' => fn($a, $b) => $a + $b,
'cikar' => fn($a, $b) => $a - $b,
'bol' => function ($a, $b) {
if ($b == 0) {
return 'sıfıra bölünemez';
}
return $a / $b;
},
];
foreach ([['topla', 8, 2], ['bol', 9, 3], ['bol', 1, 0], ['us', 2, 3]] as [$ad, $x, $y]) {
$islem = $islemler[$ad] ?? null;
echo $ad, ': ', $islem ? $islem($x, $y) : 'tanımsız işlem', PHP_EOL;
}
/* çıktı:
topla: 10
bol: 3
bol: sıfıra bölünemez
us: tanımsız işlem
*/Bu yapı, uzun bir switch bloğunun yerine geçebilir ve yeni bir işlem eklemek diziye bir satır eklemek kadar kolaydır. Tabloda olmayan bir anahtar istendiğinde ?? operatörü sayesinde uyarı oluşmaz ve kontrollü bir mesaj döner.
Sık yapılan hatalar

- use yazmayı unutmak: Dış değişken closure içinde tanımsız olur ve
Undefined variableuyarısı alırsınız. - Değerle yakalanan değişkenin güncelleneceğini sanmak:
use ($x)tanım anındaki değeri kopyalar; güncel değer içinuse (&$x)gerekir. - Arrow function ile dış değişkeni değiştirmeye çalışmak: Yakalama her zaman değerledir; değişiklik dışarıya yansımaz.
- Atamadan sonra noktalı virgülü unutmak:
$f = function () { ... };bir atama ifadesidir ve sonunda;ister. - Özellik tipi olarak callable yazmak: Derleme hatası verir;
Closurekullanın. - Private metodu dizi callable ile dışarı vermek: Çağrı dışarıdan yapıldığında görünürlük hatası alırsınız;
$this->metot(...)kullanın.
Sık Sorulan Sorular
Closure ile anonim fonksiyon aynı şey mi?
PHP'de pratikte evet. Kılavuz anonim fonksiyonlara closure da dendiğini belirtir ve hem function () {} hem fn() => yazımı Closure sınıfının bir örneğini üretir. First-class callable yazımı ve Closure::fromCallable() da aynı sınıftan nesne döndürür.
Arrow function içinde birden fazla satır yazabilir miyim?
Hayır. Arrow function'ın gövdesi tek bir ifadedir ve sonucu otomatik döndürülür. Birden fazla deyim, yerel değişken ataması veya if bloğu gerekiyorsa klasik function () use (...) { ... } yazımını kullanın. Basit koşullar için ifade içinde üçlü operatör veya match kullanılabilir.
callable mı Closure mı yazmalıyım?
Kılavuz, closure oluşturmanın çağrılabilir değer üretmenin tercih edilen yolu olduğunu belirtir. Closure nesnesi oluşturulduğu kapsamdan bağımsızdır ve her zaman çağrılabilir; metin veya dizi biçimindeki callable ise çağrıldığı yere göre çalışmayabilir. Parametrede metin fonksiyon adlarını da kabul etmek istiyorsanız callable, özellik tipinde ise zorunlu olarak Closure kullanırsınız. PHP 8.5'ten itibaren Closure, callable tipinin tam bir alt tipidir.
Closure içinde func_get_args() kullanılabilir mi?
Evet. Kılavuz, hem anonim fonksiyonlarda hem arrow function'larda func_num_args(), func_get_arg() ve func_get_args() fonksiyonlarının kullanılabildiğini belirtir. Yine de değişken sayıda argüman için ...$args parametresi daha açık bir yazımdır.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: PHP öğrenme rehberi · PHP fonksiyon tanımlama · array_map ve array_filter · Tip bildirimleri · Sihirli metotlar ve __invoke