PHP’de Türler Üzerine Uzun Ders: Union, Intersection, Nullable ile mixed, never, static, callable

PHP’de Türler Üzerine Uzun Ders: Union, Intersection, Nullable ile mixed, never, static, callable
Views: 93
0 0
Read Time:7 Minute, 28 Second

Kahvenizi alın; bugün PHP’nin modern tür sisteminde derin bir tura çıkıyoruz. Hedefimiz: union/intersection/nullable türler ile mixed, never, static ve callable’ı anlaşılır örneklerle oturtmak. Yazının sonunda hem neden varlar hem de nerede nasıl kullanılırlar, net olacak.

Hızlı sürüm haritası (özet)

  • PHP 7.1: Nullable türler (?T)
  • PHP 7.4: Tipli özellikler (typed properties)
  • PHP 8.0: Union türler (A|B), mixed, static (dönüş türü)
  • PHP 8.1: Intersection türler (A&B), never (dönüş türü), first-class callable syntax
  • PHP 8.2: DNF (Disjunctive Normal Form) türleri, true türü vb.
  1. Union türler (A|B)
    Bir değişkenin birden fazla farklı türde olabileceğini ifade eder. Söz dizimi: T1|T2|…|Tn.

Temel kurallar

  • Yinelemeye gerek yok: int|int yazmak hatadır.
  • Üstyin türler hataya yol açar: array|iterable geçersizdir; iterable zaten array’i kapsar.
  • void ve never bir union’ın parçası olamaz.
  • null bir union’a dahil edilebilir: int|string|null gibi.
  • Kısaltma: ?T, T|null demektir; ama sadece tek bir T için geçerlidir. int|string|null yazmalısınız; ?(int|string) diye bir şey yoktur.
  • false bir literal tür olarak union’da kullanılabilir (özellikle eski API’lerle uyum için). true türü de desteklenir (güncel 8.x).

Örnekler

<?php
function parseId(int|string $id): int {
    return is_int($id) ? $id : (int) $id;
}

function loadUser(int|string $id): User|null {
    // Bulunamazsa null dönebilir
    // ...
    return null;
}

// Eski tarz "false veya değer" kalıbını type-safe yapmak:
function findInCache(string $key): string|false {
    // bulamazsa false
    return false;
}

İyi pratikler

  • API sınırlarınızda union kullanmak netlik sağlar: getirme işlemi başarısızsa null, başarıda User gibi.
  • Mümkünse bool yerine özel durumlar için özel sınıflar veya Exceptions tercih edin; ama mevcut ekosistemle uyumda false/true niteli union’lar pratik olabilir.
  1. Nullable türler (?T)
    ?T, T|null anlamına gelir. Parametre, dönüş değeri ve özelliklerde kullanılabilir.

Kurallar ve örnekler

  • ?T sadece tek bir T için kısayoldur. Birden fazla tür varsa açık yazın: A|B|null.
  • ?void ve ?never geçersizdir.
  • Varsayılan değer ile uyum: ?string name=nulluygundur;stringname = null hatadır.




<?php
function greet(?string $name = null): string {
    return $name ? "Merhaba, $name" : "Merhaba!";
}

// Null-safe operator ile güzel bir ikili
$userNameLength = $user?->getProfile()?->getName()?->length();

Ne zaman kullanmalı?

  • Parametre gerçekten isteğe bağlıysa veya “değer yok” anlamlı bir durumsa (?T seçin).
  • Aksi halde “boş değeri” alan türle ifade etmek daha iyi olabilir (ör. boş dizi, boş nesne, Null Object deseni).
  1. Intersection türler (A&B)
    Intersection, verilen değerin aynı anda birden fazla türü sağlamasını ister. Özellikle arayüz kombinasyonlarını modellemek için idealdir.

Kurallar

  • Yalnızca sınıf/arayüz türleriyle anlamlıdır; int&string gibi kesişimler geçersizdir.
  • Birden fazla arayüzü birleştirebilirsiniz: Traversable&Countable.
  • En fazla bir adet somut sınıf olabilir; iki farklı sınıfın kesişimi anlamsızdır.
  • null/false/mixed gibi özel türler intersection’da kullanılamaz.
  • Union ile birlikte (8.2+): DNF türleriyle (A&B)|C gibi ifadeler yazabilirsiniz; fakat A&(B|C) yazmanız gerekirse DNF’e açın: (A&B)|(A&C).

Örnekler





<?php
function processCollection(Traversable&Countable $data): void {
    if (count($data) === 0) return;
    foreach ($data as $item) { /* ... */ }
}

// DNF tür örneği (8.2+):
// Ya hem IteratorAggregate hem Countable, ya da doğrudan Iterator
function iterate((IteratorAggregate&Countable)|Iterator $it): void {
    $iterator = $it instanceof Iterator ? $it : $it->getIterator();
    foreach ($iterator as $v) { /* ... */ }
}

Ne zaman kullanmalı?

  • Bir tüketici fonksiyonun belirli protokolleri aynı anda beklediği durumlarda (ör. hem gezilebilirlik hem sayılabilirlik).
  • Aksi halde tür kontrolü kodun her yerine dağılır.
  1. mixed
    mixed “her şey olabilir” demektir. Kabul ettiği değerler: array, bool, callable, int, float, null, object, string ve hatta kaynaklar (resource) dahil tüm değerler. Yani türsel olarak tepedeki “en geniş” türdür.

Kurallar

  • mixed zaten null’ü içerir; ?mixed, mixed|null gibi yazımlar geçersiz veya gereksizdir.
  • mixed bir union’ın parçası olamaz (zaten her şeyi kapsadığı için).
  • Parametre, dönüş ve özellik tipi olarak kullanılabilir.

Ne zaman kullanmalı?

  • Kesin tür çıkarımı yapamayacağınız sınır yüzeyleri: JSON decode sonuçları, kullanıcı girdisi, dinamik veri.
  • İçeride mümkün olduğunca hızlı daraltın: is_array, is_string, instanceof vs. ile.

Örnek





<?php
function fromJson(string $json): mixed {
    return json_decode($json, true);
}

function head(mixed $value): mixed {
    return is_array($value) ? ($value[0] ?? null) : null;
}
  1. never
    never, fonksiyonun asla çağırana dönmeyeceğini garanti eder. Bu fonksiyonlar ya her zaman istisna fırlatır ya da çıkış yapar (exit/die) veya sonsuz döngüde kalır. never bir “alt türdür” (bottom type).

Kurallar

  • Sadece dönüş türü olarak kullanılabilir.
  • Bir never fonksiyonunda return; dahi kullanamazsınız; aksi hâlde fatal hata.
  • Union/intersection içinde kullanılamaz.

void vs never

  • void: Fonksiyon bir şey döndürmez ama normal akışla çağırana döner.
  • never: Fonksiyon çağırana dönmez (program akışı biter/atılır).

Örnekler





<?php
function redirect(string $url): never {
    header("Location: $url");
    exit; // asla geri dönmez
}

function fail(string $message): never {
    throw new RuntimeException($message);
}

Ne zaman kullanmalı?

  • Hata fırlatan yardımcılar, CLI komutlarında exit eden noktalar, web’de yönlendirme yardımcıları.
  • Statik analiz ve IDE’ler, never sayesinde sonraki kodu “ulaşılamaz” olarak bilir.
  1. static
    static türü “geç bağlanan (late static binding) sınıf türünü” ifade eder: Çağrılan sınıfın gerçek türü. self ile farkı, self bildirim yapılan sınıfı sabitlerken static alt sınıfı da yakalayabilmesidir.

Özellikler

  • Dönüş türü olarak (8.0+) çok faydalıdır: Fluent API’ler, named constructor’lar, klonlama/immutability kalıpları.
  • self vs parent vs static:
    • self: Bildiren sınıfın kendisi.
    • parent: Üst sınıf.
    • static: Çağrıyı yapan geç bağlanan sınıf (geç statik bağlama).
  • Güncel PHP sürümlerinde static, dönüşün yanı sıra parametre/özellik türü olarak da desteklenir (8.x). Farklı sunucularda farklı PHP sürümleri olabileceği için bu kullanımları yapmadan önce minimum sürümü doğrulayın.

Örnekler



class Euro extends Money {}

$e = Euro::fromInt(100); // Euro döner (static sayesinde)
<?php
class Money {
    public function add(self $other): static {
        // Hesaplama...
        return new static(); // alt sınıf döndürülebilir
    }

    public static function fromInt(int $amount): static {
        // alt sınıflar kendi örneğini döndürür
        return new static();
    }
}

Ne zaman kullanmalı?

  • Miras hiyerarşisinde “aynı sınıf türünde dön” garantisi istediğinizde.
  • Builder/fluent zincirlerde alt sınıf türünü korumak istediğinizde.
  1. callable
    callable, herhangi bir “çağrılabilir” şeyi ifade eder:
  • Bir Closure (anonim fonksiyon)
  • Bir fonksiyon ismi (string): ‘strlen’
  • Statik yöntem: [‘ClassName’, ‘method’]
  • Nesne yöntemi: [$obj, ‘method’]
  • __invoke tanımlı bir nesne

Kullanım

  • Parametre, dönüş ve özellik tipi olarak kullanılabilir.
  • Bir callable değeri doğrudan çağırabilirsiniz: $cb(…).
  • Closure türü, callable’ın alt kümesidir (Closure ⊂ callable).

Örnekler





<?php
function map(array $xs, callable $f): array {
    $out = [];
    foreach ($xs as $x) {
        $out[] = $f($x);
    }
    return $out;
}

$uppercase = fn(string $s): string => strtoupper($s);
$result = map(['a', 'b'], $uppercase);

// Dizi biçiminde callable:
class Greeter {
    public function hello(string $name): string { return "Hi $name"; }
}
$g = new Greeter();
$cb = [$g, 'hello'];
echo $cb('Alice');

// İlk sınıf callable sözdizimi (8.1+):
$len = strlen(...); // Closure döner
echo $len('hello'); // 5

İmza belirtme meselesi

  • Dil düzeyinde callable(int): string gibi bir imza yazamazsınız. Bunu PHPDoc ile ifade edin ve statik analiz (PHPStan/Psalm) kullanın.
  • Eğer imza sabitse, callable yerine Closure kullanıp Closure’ın parametre ve dönüş türlerini işlevin içinde belirtmek genelde daha sağlamdır.
  1. Birlikte kullanım ve sık düşülen tuzaklar

Union + nullable

  • Sadece bir tür varsa ?T kullanın; birden fazlaysa açık yazın:
    • Doğru: ?Foo
    • Doğru: Foo|Bar|null
    • Yanlış: ?(Foo|Bar)

Redundans hataları

  • array|iterable, Traversable|iterable gibi türler geçersizdir (iterable zaten kapsar).
  • mixed ile başka türleri birleştirmeyin; mixed her şeyi kapsar.
  • void ve never union/intersection içinde kullanılamaz.

Intersection pratikleri

  • Genellikle arayüzlerle kullanın: Traversable&Countable yaygın bir örnek.
  • Union’la birlikte gerekiyorsa DNF’e dönüştürün: A&(B|C) yerine (A&B)|(A&C).

static ile miras

  • Dönüş türünüz static ise alt sınıflarınız doğru türle dönmeye devam eder; self yazsaydınız, alt sınıf için dönüş türü yanlış sabitlenebilirdi.

callable doğrulama

  • Kullanıcıdan gelen veriyi callable bekliyorsanız is_callable ile doğrulayın.
  • İmzayı korumak için Closure terchi often more predictable.

nullable ve varsayılan

  • ?T tipli parametreye varsayılan null vermeyi unutmayın, yoksa çağıranın her zaman bir değer geçmesi gerekir:
phpDownloadCopy code Wrapfunction foo(?string $x = null) { /* ... */ }
  1. Kısa bir “hangi durumda hangisi” rehberi
  • union (A|B): “Şunlardan biri” diyorsanız.
  • intersection (A&B): “Aynı anda şu özelliklerin hepsi” diyorsanız.
  • ?T: “Evet, null gelebilir” diyorsanız.
  • mixed: “Her şey olabilir, içerde daraltacağım” diyorsanız.
  • never: “Bu fonksiyon asla geri dönmez” diyorsanız.
  • static: “Çağrılan gerçek sınıf türüyle dön/çalış” diyorsanız.
  • callable: “Bana çalıştırılabilir bir şey ver” diyorsanız.
  1. Daha kapsamlı örnek: Hepsinden biraz
<?php

interface Normalizer {
    public function normalize(mixed $value): string;
}

final class StringNormalizer implements Normalizer {
    public function normalize(mixed $value): string {
        return is_string($value) ? trim($value) : json_encode($value);
    }
}

/**
 * $source:
 * - Traversable&Countable: tek geçişlik, sayılabilir koleksiyon
 * - veya array
 */
function pipeline(
    (Traversable&Countable)|array $source,
    callable $transform,           // her elemanı dönüştüren callable
    ?Normalizer $normalizer = null // opsiyonel
): array {
    $norm = $normalizer ?? new StringNormalizer();

    $iter = is_array($source) ? new ArrayIterator($source) : $source;

    $out = [];
    foreach ($iter as $item) {
        $out[] = $norm->normalize($transform($item));
    }
    return $out;
}

// Kullanım
$data = [1, 2, 3];
$result = pipeline($data, fn(int $x): int => $x * 10);
// ["10","20","30"]




Son söz
PHP’nin modern tür sistemi, niyeti kodun içine taşıyıp hataları erkenden yakalamak için güçlü araçlar sunuyor. Union ve intersection ile ifade gücünüz artıyor; nullable ile “değer yok” durumunu açık ediyorsunuz; mixed ve callable ile esnekliği yönetiyor; static ile miras hiyerarşilerinde tür güvenliğini koruyor; never ile akış garantisi veriyorsunuz. Küçük adımlarla başlayın: önce en dış API’leri tipleyin, ardından içeride türleri giderek daraltın. Kodunuz hem daha okunaklı olacak hem de daha sağlam.

Happy
Happy
100 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %

Average Rating

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir