PHP'de fonksiyon function anahtar kelimesi, bir ad, parantez içinde parametre listesi ve süslü parantezli bir gövdeyle tanımlanır: function topla($a, $b) { return $a + $b; }. Parametrelere varsayılan değer verebilir, PHP 8.0'dan beri argümanları adıyla (named arguments) geçebilir, ... ile istediğiniz sayıda argüman kabul edebilirsiniz. return değeri çağırana döndürür ve fonksiyonu hemen bitirir; return yoksa fonksiyon null döndürür.

Bu yazıda fonksiyon tanımlamanın kurallarını, varsayılan değerlerin inceliklerini (özellikle null geçmenin varsayılanı tetiklememesi), isimli argümanlarla parametre atlamayı, değişken sayıda argümanı ve birden fazla değer döndürme kalıbını anlatıyoruz. Tip bildirimleri tip bildirimleri ve strict_types yazısının, anonim fonksiyonlar ise anonim fonksiyon ve arrow function yazısının konusu. Örnekler PHP 8.5.12 ile çalıştırıldı. Serinin tamamı için PHP öğrenme rehberi sayfasına bakabilirsiniz.

Fonksiyon tanımlama ve çağırma

Fonksiyon tanımlama ve çağırma

Fonksiyon adları PHP'deki diğer etiketlerle aynı kurallara uyar: Bir harf veya alt çizgiyle başlar, ardından harf, rakam ve alt çizgi gelebilir. Kılavuza göre fonksiyon adları ASCII harfler açısından büyük/küçük harfe duyarlı değildir; yani kdvEkle() olarak tanımlanan fonksiyonu KDVEKLE() diye de çağırabilirsiniz. Yine de tanımdaki yazımı kullanmak iyi bir alışkanlıktır. PHP 8.0'dan itibaren parametre listesinin sonunda fazladan bir virgül bırakmak serbesttir; bu, uzun parametre listelerini alt alta yazarken işinizi kolaylaştırır.

php
<?php
function kdvEkle($tutar, $oran = 20)
{
    return $tutar + $tutar * $oran / 100;
}

function selamla($ad)
{
    echo "Merhaba $ad", PHP_EOL;
}

echo kdvEkle(100), PHP_EOL;     // çıktı: 120
echo kdvEkle(100, 10), PHP_EOL; // çıktı: 110
var_dump(selamla('Ayşe'));
// çıktı: Merhaba Ayşe
// çıktı: NULL
echo KDVEKLE(50), PHP_EOL;      // çıktı: 60

selamla() bir şey yazdırıyor ama return içermiyor; bu yüzden var_dump() çağrının değeri olarak NULL gösteriyor. Yazdırmak ile döndürmek arasındaki bu farkı akılda tutmak önemlidir: Sonucu başka bir hesapta kullanacaksanız fonksiyon değeri return ile vermelidir.

Kılavuz iki önemli kısıtı daha belirtir. Birincisi, PHP fonksiyon aşırı yüklemeyi (aynı adla farklı parametreli birden fazla fonksiyon) desteklemez. İkincisi, bir kez tanımlanmış fonksiyonu kaldıramaz veya yeniden tanımlayamazsınız. Aynı adı ikinci kez kullanırsanız betik derleme aşamasında durur:

php
<?php
function hesapla() { return 1; }
function hesapla() { return 2; }
// Fatal error: Cannot redeclare function hesapla() (previously declared in /yol/ornek.php:2) in /yol/ornek.php on line 3

Bu hata çoğunlukla aynı dosyanın iki kez dahil edilmesiyle ortaya çıkar; require_once kullanmak veya tanımı function_exists() kontrolüne almak çözümdür. Hata mesajlarının genel çözümü için Fatal error ve Uncaught Error çözümü yazısına bakabilirsiniz.

Tanımlamadan önce çağırmak ve koşullu fonksiyonlar

Dosyanın en üst seviyesinde tanımlanan fonksiyonlar, tanımdan önce de çağrılabilir; PHP dosyayı derlerken bu fonksiyonları hazır eder. İstisna, bir if bloğu ya da başka bir fonksiyonun içinde koşullu olarak tanımlanan fonksiyonlardır: Bunlar ancak çalışma o satıra ulaştığında var olur. Kılavuz ayrıca PHP'deki tüm fonksiyon ve sınıfların global kapsamda olduğunu vurgular; bir fonksiyonun içinde tanımlanan fonksiyon da dış fonksiyon bir kez çalıştıktan sonra her yerden çağrılabilir.

php
<?php
if (!function_exists('slugla')) {
    function slugla($metin)
    {
        return strtolower(str_replace(' ', '-', trim($metin)));
    }
}
echo slugla('  Merhaba Dunya  '), PHP_EOL; // çıktı: merhaba-dunya
echo erken(), PHP_EOL;
// çıktı: Dosyanın sonunda tanımlandı ama önce çağrıldı

function erken()
{
    return 'Dosyanın sonunda tanımlandı ama önce çağrıldı';
}

slugla() koşullu tanımlandığı için ancak if bloğu çalıştıktan sonra kullanılabilir. erken() ise üst seviyede tanımlandığı için dosyanın başında bile çağrılabildi. Burada strtolower() yalnızca ASCII harfleri dönüştürür; Türkçe karakterli metinler için mbstring ve Türkçe karakterler yazısına bakın.

Varsayılan parametre değerleri

Bir parametreye değişken ataması gibi bir yazımla varsayılan değer verebilirsiniz. Varsayılan değer yalnızca o argüman hiç verilmediğinde kullanılır. Kılavuzun özellikle altını çizdiği nokta şudur: null geçmek varsayılanı tetiklemez; parametre gerçekten null değerini alır.

php
<?php
function kahve($tur = 'türk kahvesi')
{
    return "Hazırlanıyor: $tur" . PHP_EOL;
}
echo kahve();         // çıktı: Hazırlanıyor: türk kahvesi
echo kahve(null);     // çıktı: Hazırlanıyor: 
echo kahve('filtre'); // çıktı: Hazırlanıyor: filtre

Varsayılan değer sabit bir ifade olmalıdır: sayı, metin, dizi, null ya da PHP 8.1'den beri new SinifAdi() ile oluşturulan bir nesne. Bir değişken, sınıf üyesi veya fonksiyon çağrısı varsayılan değer olamaz. Çağıran tarafın null geçmesine izin verip yine de bir varsayılan kullanmak istiyorsanız, gövdede $tur ??= 'türk kahvesi'; gibi bir satır yazmak pratik bir çözümdür.

Sıralama da önemlidir: İsteğe bağlı parametreler zorunlu olanlardan sonra gelmelidir. Aksi halde varsayılan değer hiçbir zaman kullanılamaz, çünkü konumsal çağrıda ilk argüman her zaman ilk parametreye gider. PHP 8.0'dan beri bu durum deprecated uyarısı üretir ve parametre örtük olarak zorunlu kabul edilir:

php
<?php
function yogurt($kap = 'kase', $tat)
{
    return "$kap içinde $tat yoğurt";
}
echo yogurt('vanilya');
text
Deprecated: yogurt(): Optional parameter $kap declared before required parameter $tat is implicitly treated as a required parameter in /yol/ornek.php on line 2

Fatal error: Uncaught ArgumentCountError: Too few arguments to function yogurt(), 1 passed in /yol/ornek.php on line 6 and exactly 2 expected in /yol/ornek.php:2
Stack trace:
#0 /yol/ornek.php(6): yogurt('vanilya')
#1 {main}
  thrown in /yol/ornek.php on line 2

Çözüm, zorunlu parametreyi başa almak ya da kullanılmayan varsayılanı kaldırmaktır. Kılavuz, Tip $p = null biçimindeki eski bir kalıbın bu kuralın istisnası olduğunu, ancak bu örtük nullable kullanımının da PHP 8.4'te deprecated edildiğini belirtir; bunun yerine ?Tip $p yazılmalıdır. Ayrıntılar için PHP 8.4 deprecated uyarıları yazısına bakın.

İsimli argümanlar (named arguments)

İsimli argümanlar (named arguments)

PHP 8.0 ile gelen isimli argümanlar, değeri parametrenin konumuna göre değil adına göre geçmenizi sağlar: etiket(kalin: true). Bu yazımın üç faydası vardır: Çağrı kendini açıklar, argüman sırası önemsizleşir ve aradaki isteğe bağlı parametreleri tek tek yazmadan atlayabilirsiniz. Konumsal ve isimli argümanları birlikte kullanabilirsiniz, ancak isimli olanlar konumsal olanlardan sonra gelmelidir.

php
<?php
function etiket($metin, $renk = 'gri', $boyut = 'orta', $kalin = false)
{
    return "$metin [$renk, $boyut" . ($kalin ? ', kalın' : '') . ']';
}

echo etiket('Yeni', kalin: true), PHP_EOL;
// çıktı: Yeni [gri, orta, kalın]
echo etiket(boyut: 'küçük', metin: 'İndirim'), PHP_EOL;
// çıktı: İndirim [gri, küçük]
echo etiket('Stokta', 'yeşil', kalin: true), PHP_EOL;
// çıktı: Stokta [yeşil, orta, kalın]

echo str_pad(string: '7', length: 3, pad_type: STR_PAD_LEFT, pad_string: '0'), PHP_EOL;
// çıktı: 007

Son satır, isimli argümanların PHP'nin kendi fonksiyonlarında da çalıştığını gösteriyor. Parametre adları kılavuzdaki imzada yazan adlardır; str_pad() için string, length, pad_string ve pad_type. Parametre adı bir tanımlayıcı olmalıdır, bir değişkenden dinamik olarak alınamaz. Ayrılmış anahtar kelimeleri parametre adı olarak kullanmak ise serbesttir.

İki tipik hata vardır. Aynı parametreye iki kez değer vermek Error fırlatır; bu, konumsal olarak zaten doldurulmuş bir parametreyi adıyla tekrar vermeyi de kapsar. Var olmayan bir adı kullanmak da hata üretir:

php
<?php
function bol($a, $b)
{
    return $a / $b;
}
echo bol(10, a: 5);
// Fatal error: Uncaught Error: Named parameter $a overwrites previous argument in /yol/ornek.php:6

// Olmayan bir ad, örneğin bol(10, c: 2), ise şunu verir:
// Fatal error: Uncaught Error: Unknown named parameter $c in /yol/ornek.php:6

Değişken sayıda argüman: ... (variadic)

Parametre listesinde bir parametrenin önüne ... koyarsanız, o konumdan sonraki tüm argümanlar bir dizi olarak bu parametrede toplanır. Önünde normal konumsal parametreler olabilir; yalnızca onlarla eşleşmeyen fazlalık diziye girer. ... öncesine bir tip yazarsanız, toplanan her argüman o tipe uymak zorundadır.

Aynı ... işareti çağrı tarafında ters yönde çalışır: Bir diziyi ya da Traversable nesneyi açarak elemanlarını ayrı argümanlar olarak geçirir. PHP 8.1'den beri metin anahtarlı dizileri açtığınızda anahtarlar parametre adı olarak kullanılır ve açma işleminden sonra isimli argüman da ekleyebilirsiniz; ancak isimli argüman, açılan diziden gelen bir değerin üzerine yazamaz.

php
<?php
function topla(...$sayilar)
{
    $t = 0;
    foreach ($sayilar as $s) {
        $t += $s;
    }
    return $t;
}
echo topla(), PHP_EOL;           // çıktı: 0
echo topla(1, 2, 3, 4), PHP_EOL; // çıktı: 10

function logla($seviye, ...$mesajlar)
{
    echo "[$seviye] " . implode(' | ', $mesajlar), PHP_EOL;
}
logla('UYARI', 'disk dolmak üzere', 'yedek alınmadı');
// çıktı: [UYARI] disk dolmak üzere | yedek alınmadı

$degerler = [5, 10, 15];
echo topla(...$degerler), PHP_EOL; // çıktı: 30

function adres($il, $ilce, $posta = '-')
{
    return "$ilce / $il ($posta)";
}
echo adres(...['ilce' => 'Çankaya', 'il' => 'Ankara']), PHP_EOL;
// çıktı: Çankaya / Ankara (-)
echo adres(...['İzmir', 'Konak'], posta: '35250'), PHP_EOL;
// çıktı: Konak / İzmir (35250)

Variadic parametre, isimli argümanları da yakalar. function goster(...$args) fonksiyonunu goster(1, renk: 'mavi') diye çağırırsanız $args dizisi hem 0 anahtarlı konumsal değeri hem 'renk' anahtarlı isimli değeri içerir. Bu davranış, gelen seçenekleri olduğu gibi başka bir fonksiyona aktaran sarmalayıcılar yazarken işe yarar.

php
<?php
function goster(...$args)
{
    var_dump($args);
}
goster(1, renk: 'mavi');
/* çıktı:
array(2) {
  [0]=>
  int(1)
  ["renk"]=>
  string(4) "mavi"
}
*/

Eksik ve fazla argüman

Varsayılan değeri olmayan bir parametreyi atlarsanız PHP 7.1'den beri ArgumentCountError fırlatılır; daha eski sürümlerde yalnızca uyarı verilirdi. Kullanıcı tanımlı bir fonksiyona fazladan argüman geçmek ise hata değildir: Fazlalar yok sayılır, ama func_num_args() ve func_get_args() ile hâlâ okunabilir.

php
<?php
function carp($a, $b)
{
    return $a * $b;
}
echo carp(3);
/* Fatal error: Uncaught ArgumentCountError: Too few arguments to function carp(), 1 passed in /yol/ornek.php on line 6 and exactly 2 expected in /yol/ornek.php:2 */
php
<?php
function selam($ad)
{
    return "Selam $ad, " . func_num_args() . ' argüman geldi';
}
echo selam('Ali', 'fazla', 'değer'), PHP_EOL;
// çıktı: Selam Ali, 3 argüman geldi

Bu hataların tamamı ve mesajlarının nasıl okunacağı TypeError ve ArgumentCountError yazısında ayrıca ele alınıyor. Fazla argümanın sessizce yutulması bazen hatayı gizler; bu yüzden değişken sayıda argüman bekleyen fonksiyonlarda func_get_args() yerine açıkça ...$parametre yazmak niyetinizi daha net gösterir.

Değer döndürmek: tek değer, dizi ve erken return

return herhangi bir tipte değer döndürebilir: sayı, metin, dizi, nesne. Çalıştığı anda fonksiyonu bitirir ve kontrolü çağrının yapıldığı satıra geri verir. Kılavuz, bir fonksiyonun birden fazla değer döndüremeyeceğini, ama bir dizi döndürüp çağıran tarafta dizi açmayla aynı sonuca ulaşılabileceğini belirtir:

php
<?php
function minMax(array $dizi)
{
    return [min($dizi), max($dizi)];
}
[$enKucuk, $enBuyuk] = minMax([7, 2, 9, 4]);
echo "$enKucuk - $enBuyuk", PHP_EOL; // çıktı: 2 - 9

function istatistik(array $dizi)
{
    return ['adet' => count($dizi), 'ortalama' => array_sum($dizi) / count($dizi)];
}
['adet' => $n, 'ortalama' => $ort] = istatistik([10, 20, 30]);
echo "$n eleman, ortalama $ort", PHP_EOL; // çıktı: 3 eleman, ortalama 20

Anahtarlı dizi döndürmek, değerlerin sırasını ezberlemek zorunda kalmadan hangisinin ne olduğunu açık eder. Döndürülen veri büyüdükçe küçük bir sınıf tanımlamak daha iyi bir seçenek olabilir; bunun için PHP sınıf ve nesne yazısına bakın. Erken return ise geçersiz girdileri fonksiyonun başında eleyip asıl mantığı iç içe if bloklarına gömmeden yazmanızı sağlar.

Özyineleme, değişken fonksiyonlar ve static değişkenler

PHP'de bir fonksiyon kendini çağırabilir. Kılavuz, 100-200 seviyeyi aşan özyinelemeli çağrıların yığını tüketip betiği sonlandırabileceği konusunda uyarır ve sonsuz özyinelemeyi bir programlama hatası sayar. Derinliği belli olmayan yapılarda döngü ile yazmayı düşünün.

php
<?php
function faktoriyel($n)
{
    if ($n <= 1) {
        return 1;
    }
    return $n * faktoriyel($n - 1);
}
echo faktoriyel(5), PHP_EOL;  // çıktı: 120
echo faktoriyel(20), PHP_EOL; // çıktı: 2432902008176640000

Değişken fonksiyon kavramında bir değişkenin sonuna parantez eklerseniz PHP, değişkenin değeriyle aynı ada sahip fonksiyonu arar ve çağırır. Bu, bir işlem tablosu kurmak için kullanılabilir. Kılavuz, echo, isset(), empty(), unset() ve include gibi dil yapılarının bu yolla çağrılamayacağını belirtir.

php
<?php
$islemler = ['buyut' => 'strtoupper', 'ters' => 'strrev', 'uzunluk' => 'strlen'];
foreach ($islemler as $ad => $fonksiyon) {
    echo $ad, ': ', $fonksiyon('php'), PHP_EOL;
}
/* çıktı:
buyut: PHP
ters: php
uzunluk: 3
*/

Metin olarak fonksiyon adı taşımak yerine modern PHP'de strtoupper(...) yazımıyla bir Closure nesnesi oluşturmak daha güvenlidir; bu konu anonim fonksiyonlar yazısında. Bir fonksiyonun çağrılar arasında değer hatırlaması gerekiyorsa gövdede static $sayac = 0; gibi bir static değişken kullanabilirsiniz; kapsam kuralları PHP değişkenler ve kapsam yazısında anlatılıyor.

Değerle ve referansla parametre geçirme

Değerle ve referansla parametre geçirme: Varsayılan değer, İsimli argüman, Variadic parametre, Argüman açma

PHP'de argümanlar varsayılan olarak değerle geçer: Fonksiyon içinde parametreyi değiştirmeniz dışarıdaki değişkeni etkilemez. Fonksiyonun çağıranın değişkenini değiştirmesini istiyorsanız tanımda parametrenin önüne & koyarsınız: function ekle(array &$liste). Çağrı tarafında işaret kullanılmaz. Bu durumda yalnızca değişken geçirilebilir; 5 gibi bir sabit ifade vermek hatadır. Referansların ayrıntısı için PHP referanslar yazısına bakın.

ÖzellikYazımSürüm notu
Varsayılan değerfunction f($a = 1)new ile nesne varsayılanı PHP 8.1
İsimli argümanf(a: 1)PHP 8.0
Variadic parametrefunction f(...$a)Tip yazılabilir: int ...$a
Argüman açmaf(...$dizi)Metin anahtarlar PHP 8.1
Referansla parametrefunction f(&$a)Yalnızca değişken geçirilir
Sondaki virgülfunction f($a, $b,)Parametrede PHP 8.0, çağrıda 7.3

Küçük uygulama: esnek bir fatura fonksiyonu

Aşağıdaki iki fonksiyon, yazıda anlatılan parçaları bir araya getiriyor. fatura() müşteri adını zorunlu, kalem tutarlarını ise variadic olarak alıyor; kalem yoksa erken return ile sıfır değerli bir dizi döndürüyor. yazdir() iki isteğe bağlı parametreye sahip ve ikinci çağrıda yalnızca detay parametresi adıyla verilerek para birimi parametresi atlanıyor. Sepet dizisi ise ...$sepet ile açılıp ayrı argümanlar olarak geçiriliyor.

php
<?php
function fatura(string $musteri, int|float ...$kalemler): array
{
    if ($kalemler === []) {
        return ['musteri' => $musteri, 'ara' => 0, 'kdv' => 0, 'toplam' => 0];
    }
    $ara = array_sum($kalemler);
    $kdv = round($ara * 0.20, 2);
    return ['musteri' => $musteri, 'ara' => $ara, 'kdv' => $kdv, 'toplam' => $ara + $kdv];
}

function yazdir(array $f, string $para = 'TL', bool $detay = true): void
{
    if ($detay) {
        echo "Ara toplam: {$f['ara']} $para, KDV: {$f['kdv']} $para", PHP_EOL;
    }
    echo "{$f['musteri']} toplam: {$f['toplam']} $para", PHP_EOL;
}

$sepet = [120, 80.5, 49.5];
yazdir(fatura('Ayşe Kaya', ...$sepet));
yazdir(fatura('Mert Ak'), detay: false);
/* çıktı:
Ara toplam: 250 TL, KDV: 50 TL
Ayşe Kaya toplam: 300 TL
Mert Ak toplam: 0 TL
*/

Örnekte int|float ve : array gibi tip bildirimleri de kullanıldı; bunlar fonksiyonun ne beklediğini ve ne döndürdüğünü belgelediği gibi yanlış tipte bir değer geldiğinde hatayı erken yakalar. Gerçek bir faturada para hesabını kayan noktalı sayılarla yapmak, kılavuzun float sayfasında anlatılan hassasiyet sınırları nedeniyle küçük yuvarlama farkları doğurabilir; tutarları kuruş cinsinden tam sayı olarak tutmayı düşünebilirsiniz. Sonucu Türkçe binlik ayırıcıyla biçimlendirmek için sprintf ve number_format yazısına bakabilirsiniz.

Sık yapılan hatalar

  • null geçince varsayılanın kullanılacağını sanmak: f(null) parametreye null verir; argümanı hiç yazmayın ya da gövdede ??= kullanın.
  • İsteğe bağlı parametreyi zorunlu olanın önüne koymak: Deprecated uyarısı alırsınız ve varsayılan değer hiç işe yaramaz.
  • echo ile return'ü karıştırmak: Değeri yazdıran ama döndürmeyen bir fonksiyonun sonucu null olur.
  • Aynı dosyayı iki kez include etmek: Cannot redeclare function hatası verir; require_once kullanın.
  • İsimli argümanda yazım hatası: Unknown named parameter hatası alırsınız; adlar kılavuzdaki imzayla birebir aynı olmalıdır.

Sık Sorulan Sorular

PHP'de fonksiyon aşırı yükleme (overloading) var mı?

Hayır. Kılavuz, PHP'nin aynı adla birden fazla fonksiyon tanımlamayı desteklemediğini açıkça belirtir. Benzer bir esnekliği varsayılan değerler, isimli argümanlar, variadic parametreler ve birleşik (union) tipler ile sağlarsınız. Tip birleşimleri için tip bildirimleri yazısına bakın.

Varsayılan değer olarak başka bir fonksiyonun sonucunu verebilir miyim?

Hayır. Varsayılan değer sabit bir ifade olmak zorundadır; değişken, sınıf üyesi veya fonksiyon çağrısı kabul edilmez. PHP 8.1'den beri tek istisna new SinifAdi() ile nesne oluşturmaktır. Dinamik bir varsayılana ihtiyacınız varsa parametreyi nullable yapıp gövdede $tarih ??= date('Y-m-d'); gibi bir satırla doldurabilirsiniz.

İsimli argümanlar hangi sürümden itibaren çalışır?

PHP 8.0'dan itibaren. Açılan bir diziden sonra isimli argüman eklemek ise PHP 8.1 ile geldi. Projeniz daha eski bir sürümde çalışıyorsa bu yazımlar sözdizimi hatası üretir. Kullandığınız sürümü php -v ile kontrol edebilirsiniz.

Fonksiyon içinden dışarıdaki bir değişkene nasıl erişirim?

En temiz yol değeri parametre olarak geçmek ve sonucu return ile almaktır. global anahtar kelimesi ve $GLOBALS dizisi de vardır, ancak bağımlılığı gizledikleri için dikkatli kullanılmalıdır. Kapsam kuralları değişkenler ve kapsam yazısında anlatılıyor.

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 · Tip bildirimleri ve strict_types · Anonim fonksiyon ve arrow function · PHP referanslar · TypeError ve ArgumentCountError