PHP'de Statik Üyeler, Sabitler ve Enum Kullanımı

PHP'de Statik Üyeler, Sabitler ve Enum Kullanımı

6 dakika okuma

Uygulamalarda durum bilgisi çoğunlukla metin olarak taşınır: bir siparişin durumu bekliyor, hazırlanıyor ya da tamamlandı olabilir. Bu değerler metin olarak yazıldığında yazım hataları çalışma zamanına kadar fark edilmez, geçerli değerlerin listesi hiçbir yerde tanımlı olmaz ve karşılaştırmalar kod tabanına dağılır.

Bu bölümde sabitler, statik üyeler ve enum yapısı ele alınıyor. Üçü de sınıf örneği olmadan erişilen yapılardır, ancak kullanım alanları belirgin biçimde ayrışır.

Sabitler

Sabitler çalışma boyunca değişmeyen değerleri taşır. Sınıf içinde tanımlanan sabitler, ilgili oldukları kavramın yanında durdukları için genel ad alanına yayılan tanımlardan daha düzenlidir.

PHP16 satır
final class Yukleme
{
    public const int AZAMI_BOYUT = 5 * 1024 * 1024;      // tip bildirimi PHP 8.3 ile
    public const array IZINLI_TURLER = ['image/jpeg', 'image/png', 'image/webp'];

    public function denetle(int $boyut, string $tur): void
    {
        if ($boyut > self::AZAMI_BOYUT) {
            throw new DomainException('Dosya çok büyük');
        }

        if (!in_array($tur, self::IZINLI_TURLER, strict: true)) {
            throw new DomainException('Desteklenmeyen tür');
        }
    }
}

Sabitler arayüzlerde de tanımlanabilir ve uygulayan tüm sınıflardan erişilir. Alt sınıfın sabiti yeniden tanımlamasını engellemek gerekiyorsa final kullanılır.

Ad alanı düzeyindeki sabitler için iki yol vardır. define çalışma zamanında, const ise derleme aşamasında tanımlar; ikincisi daha hızlıdır ve ad alanına saygı gösterir. Yapılandırma değerleri sabit olarak değil, okunabilir bir yapılandırma nesnesi üzerinden taşınmalıdır; sabitler ortama göre değişemez.

Statik özellik ve metotlar

Statik üyeler sınıfa aittir, nesneye değil. Nesne durumuna ihtiyaç duymayan yardımcı işlemler için uygundur. Ancak statik özelliklerde tutulan veri, isteğin tamamı boyunca paylaşılan gizli bir durum oluşturur ve test yazmayı zorlaştırır.

PHP14 satır
final class Slug
{
    private const array HARFLER = ['ı' => 'i', 'ğ' => 'g', 'ü' => 'u',
                                   'ş' => 's', 'ö' => 'o', 'ç' => 'c'];

    public static function uret(string $baslik): string
    {
        $metin = strtr(mb_strtolower($baslik, 'UTF-8'), self::HARFLER);

        return trim(preg_replace('/[^a-z0-9]+/u', '-', $metin), '-');
    }
}

echo Slug::uret('Kaz Dağları Yürüyüş Notları');   // kaz-daglari-yuruyus-notlari

Statik çağrılar yer değiştirilemez. Bir sınıf içeride başka bir sınıfın statik metodunu çağırıyorsa, o bağımlılık testte değiştirilemez ve yapıcı metoda bakılarak görülemez. Bu yüzden veritabanı erişimi, dosya işlemleri ve dış servis çağrıları statik metotlara konulmaz.

Tek örnek deseni de aynı sorunu taşır. Genel erişim noktası sağlaması pratik görünse de, bağımlılığı gizlediği ve durumu istekler arasında paylaştırdığı için bugün önerilmez. Aynı ihtiyaç, nesnenin tek örneğini üreten bir kapsayıcı ile bağımlılık aktarımı üzerinden karşılanır.

Enum

Enum, sınırlı sayıda değerden oluşan bir tip tanımlar. Değerler tip sistemi tarafından denetlendiği için yazım hatası çalışma zamanına kalmaz ve geçerli değerlerin listesi tek bir yerde durur.

PHP22 satır
enum SiparisDurumu: string
{
    case Bekliyor = 'bekliyor';
    case Hazirlaniyor = 'hazirlaniyor';
    case Kargoda = 'kargoda';
    case Tamamlandi = 'tamamlandi';

    public function etiket(): string
    {
        return match ($this) {
            self::Bekliyor => 'Ödeme bekleniyor',
            self::Hazirlaniyor => 'Hazırlanıyor',
            self::Kargoda => 'Kargoya verildi',
            self::Tamamlandi => 'Teslim edildi',
        };
    }

    public function iptalEdilebilir(): bool
    {
        return in_array($this, [self::Bekliyor, self::Hazirlaniyor], strict: true);
    }
}

Değer taşıyan enum, veritabanı ile arasındaki dönüşümü kolaylaştırır. from geçersiz değerde istisna fırlatır, tryFrom ise null döndürür. Dış kaynaktan gelen veride ikincisi tercih edilir.

PHP9 satır
$durum = SiparisDurumu::from($satir['durum']);          // geçersizse ValueError
$durum = SiparisDurumu::tryFrom($istek['durum'] ?? '');  // geçersizse null

foreach (SiparisDurumu::cases() as $secenek) {          // tüm değerler
    printf('<option value="%s">%s</option>', $secenek->value, $secenek->etiket());
}

// veritabanına yazarken
$sorgu->execute(['durum' => $durum->value]);

Enum arayüz uygulayabilir ve sabit tanımlayabilir. Böylece farklı enum tipleri ortak bir sözleşmeye bağlanır. Enum içinde özellik tanımlanamaz; durum taşıması gereken bir kavram enum ile değil sınıfla ifade edilir.

PHP20 satır
interface Etiketli
{
    public function etiket(): string;
}

enum Oncelik: int implements Etiketli
{
    case Dusuk = 1;
    case Orta = 2;
    case Yuksek = 3;

    public function etiket(): string
    {
        return match ($this) {
            self::Dusuk => 'Düşük',
            self::Orta => 'Orta',
            self::Yuksek => 'Yüksek',
        };
    }
}

Durum geçişlerini enum içinde tanımlamak

Durumların hangi sırayla değişebileceği çoğu uygulamada dağınık koşullarla ifade edilir. Bu kural enumun kendisine taşındığında, geçerli geçişlerin listesi tek bir yerde durur ve geçersiz bir geçiş denemesi anında hata verir.

PHP28 satır
enum SiparisDurumu: string
{
    case Bekliyor = 'bekliyor';
    case Hazirlaniyor = 'hazirlaniyor';
    case Kargoda = 'kargoda';
    case Tamamlandi = 'tamamlandi';
    case Iptal = 'iptal';

    /** @return list<self> */
    public function sonraki(): array
    {
        return match ($this) {
            self::Bekliyor => [self::Hazirlaniyor, self::Iptal],
            self::Hazirlaniyor => [self::Kargoda, self::Iptal],
            self::Kargoda => [self::Tamamlandi],
            self::Tamamlandi, self::Iptal => [],
        };
    }

    public function gecebilirMi(self $hedef): bool
    {
        return in_array($hedef, $this->sonraki(), strict: true);
    }
}

if (!$siparis->durum->gecebilirMi(SiparisDurumu::Kargoda)) {
    throw new DomainException('Bu durumdan kargoya geçilemez');
}

Aynı yapı arayüzde de kullanılır: kullanıcıya yalnızca geçerli sonraki adımlar gösterilir. Kural tek yerde tanımlı olduğu için arayüz ile iş kuralı arasında ayrışma oluşmaz.

Enum ile metin sabiti karşılaştırması

Aynı işin metin sabitleriyle yapıldığı bir kodda geçersiz değerin fark edilmesi, o değerin bir karşılaştırmaya girmesine kadar gecikir. Enum kullanıldığında hata, değerin üretildiği noktada ortaya çıkar.

PHP9 satır
// metin ile: yazım hatası sessizce yanlış davranışa yol açar
if ($siparis->durum === 'hazirlniyor') {   // hiçbir zaman doğru olmaz
    // ...
}

// enum ile: yanlış yazım anında hata verir
if ($siparis->durum === SiparisDurumu::Hazirlaniyor) {
    // ...
}

match ifadesi enum ile birlikte kullanıldığında ek bir kazanç sağlar: karşılanmayan bir durum kaldığında çalışma zamanında hata alınır. Enuma yeni bir değer eklendiğinde, o değeri işlemeyen tüm yerler ilk denemede kendini gösterir.

Enumun sınırları

Enum örnekleri tekildir: aynı durum her yerde aynı nesnedir. Bu yüzden karşılaştırma === ile yapılabilir ve nesne kimliği güvenle kullanılır. Ancak aynı nedenle enum durum taşıyamaz; her siparişe özel bir değer tutulacaksa bu bilgi enumda değil sınıfta yer alır.

Değer taşıyan enumlar JSON çıktısında kendi değerlerine dönüşür. Değer taşımayan enumlar ise doğrudan serileştirilemez; API yanıtında kullanılacak her enumun bir değeri olması bu yüzden pratik bir kuraldır.

PHP5 satır
enum Durum: string { case Aktif = 'aktif'; }
enum Renk { case Kirmizi; }

echo json_encode(['durum' => Durum::Aktif]);   // {"durum":"aktif"}
echo json_encode(['renk' => Renk::Kirmizi]);   // false döner, çıktı üretilmez

Veritabanı tarafında değerin metin olarak saklanması, sayısal karşılıklardan daha dayanıklıdır. Sayı kullanıldığında kayıtların anlamı şemaya bakılarak anlaşılamaz ve sıralamaya bağlı hatalar oluşabilir.

Kontrol listesi

  • Sınırlı değer kümeleri metin yerine enum ile tanımlanıyor.
  • Dış kaynaktan gelen değerlerde tryFrom kullanılıp sonuç denetleniyor.
  • Sabitler ilgili oldukları sınıfın içinde ve tip bildirimiyle tanımlı.
  • Statik metotlar dış dünyaya erişmiyor; yalnızca saf hesaplama yapıyor.
  • Tek örnek deseni yerine bağımlılık aktarımı kullanılıyor.
  • Enum üzerinde yapılan match ifadelerinde tüm durumlar karşılanıyor.

İlgili yazılar

PHP eğitim serisi

Bu yazı, temel sözdiziminden üretim ortamına kadar ilerleyen 25 bölümlük serinin bir parçası. Bölümler sırayla okunacak biçimde hazırlandı, ancak her biri tek başına da kullanılabilir.

Temeller

  1. Kurulum ve temel sözdizimi
  2. Döngüler ve akış kontrolü
  3. Diziler
  4. Fonksiyonlar
  5. String fonksiyonları
  6. Süper globaller
  7. Form işleme
  8. Dosya işlemleri

Dilin ayrıntıları

  1. Tip sistemi ve strict_types
  2. Hata ve istisna yönetimi
  3. Düzenli ifadeler
  4. Tarih ve saat işlemleri

Nesne yönelimli PHP

  1. Sınıf ve nesne
  2. Kalıtım, arayüz ve trait
  3. Statik üyeler, sabitler ve enum (bu yazı)
  4. Namespace, Composer ve autoload

Veri ve güvenlik

  1. PDO ile veritabanı işlemleri
  2. Oturum yönetimi ve kimlik doğrulama
  3. Güvenli çıktı ve XSS
  4. JSON ile veri değişimi

Dış dünya ve üretim

  1. cURL ile dış API çağırma
  2. Kendi REST API'ni yazmak
  3. PHPUnit ile birim test
  4. Hata ayıklama ve performans ölçümü
  5. Üretime alma ve güvenlik

Yorumlar

Bu form reCAPTCHA ile korunur; Google Gizlilik Politikası ve Hizmet Şartları geçerlidir.

İlk yorumu sen yap.

İlgili Yazılar