Jack.Wrestler 1
Jack.Wrestler
mavzermete 1
mavzermete
noisiv 1
noisiv
Manwe Work 1
Manwe Work
Agora Metin2 1
Agora Metin2
lHezarfeNl 1
lHezarfeNl
Reklam vermek için turkmmo@gmail.com

Altın Konu PHP Enum Rehberi: Backed Enum, from(), tryFrom() ve cases() Kullanımı

image.jpeg


PHP 8.1 ile hayatımıza giren enum yapısı, sabit ve belirli bir değerler kümesini (case'ler) tanımlamamıza olanak tanıyan bir türdür: enum Status { case Active; case Banned; }. Bu yapıdaki bir case'e Status::Active şeklinde erişilir ve eğer bir parametrenin türü Status olarak belirlenmişse, bu parametre tanımlı case'ler dışında hiçbir değeri kabul etmez. Değerli (backed) enum'lar ise enum Status: string şeklinde tanımlanır; bu sayede her case'e atanmış bir değer bulunur ve bu değer Status::from() metoduyla tekrar case haline getirilebilir.

PHP:
<?php
enum Status: string
{
    case Active = 'active';
    case Pending = 'pending';
    case Banned = 'banned';
}

function canLogIn(Status $status): bool
{
    return $status === Status::Active;
}

$s = Status::Pending;
echo $s->name, ' = ', $s->value, "\n";
var_dump(canLogIn($s), canLogIn(Status::Active));

// From a value stored in a database or a form
$fromDb = Status::from('banned');
echo $fromDb->name, "\n";

Enum'lar gelmeden önce benzer ihtiyaçlar için genellikle dizge sabitleri kullanılıyordu; ancak bu yöntem 'actve' gibi basit yazım hatalarının fark edilmeden veritabanına kaydedilmesine yol açabiliyordu. Parametre türü Status olarak belirlendiğinde ise sadece tanımlanmış olan case'lerin gönderilmesi garanti altına alınmış olur.

Saf ve Değerli Enumlar Arasındaki Farklar

Saf (pure) enum'larda case'ler sadece birer isimden ibarettir. Değerli enum'larda ise ismin ardından int veya string şeklinde bir tür belirtilir ve her case, bu türe uygun benzersiz bir değere sahip olmalıdır. Tüm case'lerde ->name özelliği bulunurken, ->value özelliği sadece değerli enum'lara özgüdür.

PHP:
<?php
enum Suit
{
    case Hearts;
    case Spades;
}

enum Priority: int
{
    case Low = 1;
    case Normal = 5;
    case Urgent = 10;
}

$card = Suit::Hearts;
echo $card->name, "\n";
var_dump($card === Suit::Hearts, $card instanceof Suit);

$p = Priority::Urgent;
echo $p->name, ' = ', $p->value, "\n";
var_dump(Priority::Low->value < Priority::Urgent->value);
var_dump($p);

Eğer veri uygulamanın dışına aktarılacaksa (veritabanı sütunu, URL, form girişi veya JSON gibi), değerli enum kullanmak doğru tercihtir. Ancak sadece kodun kendi iç mantığında (örneğin bir ayrıştırıcının durum yönetimi gibi) kullanılacak değerler için saf enum'lar yeterlidir.

from() ve tryFrom() ile Değer Dönüştürme

Değerli enum'larda, saklanan bir değeri tekrar ilgili case'e dönüştürmek için iki statik yöntem kullanılır. from() metodu, karşılığı olmayan bir değerle karşılaştığında ValueError hatası fırlatır. tryFrom() ise bu durumda null döndürür; bu özellik, null birleştirme operatörü (??) ile birlikte varsayılan bir değer atamak için oldukça uygundur.

PHP:
<?php
enum Size: string
{
    case Small = 's';
    case Medium = 'm';
    case Large = 'l';
}

var_dump(Size::from('m'));
var_dump(Size::tryFrom('xl'));

$size = Size::tryFrom('xl') ?? Size::Medium;
echo "Using {$size->name}\n";

try {
    Size::from('xl');
} catch (ValueError $e) {
    echo get_class($e), ': ', $e->getMessage(), "\n";
}

Kullanıcıdan gelen verilerde tryFrom() kullanmanız önerilir. Kendi yazdığınız kodun ürettiği değerlerde ise, tanımlanmamış bir değerin bir hata (bug) olduğu varsayımıyla from() kullanmak daha mantıklıdır. Argüman gönderimi genel tür kurallarına tabidir: örneğin int tabanlı bir enum'da Priority::from('5') çağrısı, '5' değeri sayıya dönüştürülebildiği için Priority::Normal sonucunu verir. Fakat Priority::from('5x') bir TypeError ile sonuçlanır. declare(strict_types=1) aktif olduğunda ise tüm dizge ifadeleri TypeError fırlatır. Bu durum tryFrom() için de geçerlidir; örneğin Priority::tryFrom('abc') ifadesi null dönmek yerine hata verir. Bu nedenle, int enum'lar için formdan gelen girdileri önce filter_var($raw, FILTER_VALIDATE_INT) ile kontrol etmeniz gerekir.

cases() Metodu ile Tüm Case'leri Listeleme

Enum::cases() metodu, tanımlandıkları sırayla tüm case'leri içeren bir dizi döndürür. Bu yöntem; açılır liste (dropdown) oluşturmak, bir listeyi doğrulamak veya array_column kullanarak tüm değerleri toplamak için kullanılır.

PHP:
<?php
enum Plan: string
{
    case Free = 'free';
    case Pro = 'pro';
    case Team = 'team';

    public function label(): string
    {
        return match ($this) {
            Plan::Free => 'Free',
            Plan::Pro => 'Pro, 9 EUR a month',
            Plan::Team => 'Team, 29 EUR a month',
        };
    }
}

$chosen = Plan::tryFrom($_POST['plan'] ?? '');

echo '<form method="post"><select name="plan">';
foreach (Plan::cases() as $plan) {
    $selected = $plan === $chosen ? ' selected' : '';
    echo "<option value=\"{$plan->value}\"$selected>{$plan->label()}</option>";
}
echo '</select> <button>Choose</button></form>';

if ($chosen !== null) {
    echo "<p>You picked <b>{$chosen->name}</b> (stored as '{$chosen->value}').</p>";
}

echo '<pre>', implode(', ', array_column(Plan::cases(), 'value')), '</pre>';

Kodu çalıştırdığınızda bir plan seçip onayladığınızda, betik $_POST['plan'] verisiyle yeniden çalışır ve tryFrom() yöntemi gelen dizgeyi uygun Plan case'ine dönüştürür. plan=gold gibi geçersiz bir değer gönderilirse sonuç null döner ve hiçbir seçenek seçilmemiş olur.

Enum Metotları, Sabitleri ve Arayüzler

Enum'lar; metotlara, statik metotlara ve sabitlere sahip olabilir, ayrıca arayüzleri (interface) uygulayabilirler. Bir metot içerisinde $this ifadesi, mevcut case'i temsil eder. Bu durum, match ($this) yapısını her case için özel veriler eklemenin en doğal yolu haline getirir. Ancak enum'lar özellik (property) tanımlayamazlar; case'e özel veriler ancak metotlar aracılığıyla sağlanabilir.

PHP:
<?php
interface HasColor
{
    public function color(): string;
}

enum OrderState: string implements HasColor
{
    case New = 'new';
    case Shipped = 'shipped';
    case Delivered = 'delivered';
    case Cancelled = 'cancelled';

    const DEFAULT = self::New;

    public function color(): string
    {
        return match ($this) {
            self::New => 'blue',
            self::Shipped => 'orange',
            self::Delivered => 'green',
            self::Cancelled => 'grey',
        };
    }

    public function isFinal(): bool
    {
        return in_array($this, [self::Delivered, self::Cancelled], true);
    }

    public static function open(): array
    {
        return array_filter(self::cases(), fn (self $s) => !$s->isFinal());
    }
}

$state = OrderState::DEFAULT;
echo "{$state->name} is {$state->color()}\n";
var_dump(OrderState::Delivered->isFinal());
echo implode(', ', array_map(fn ($s) => $s->name, OrderState::open())), "\n";
var_dump($state instanceof HasColor);

Enum'ların match İfadesiyle Kullanımı

match yapısı karşılaştırmaları === (strict) şeklinde yaptığı için enum case'leri ile tam uyumludur. Eğer bir case match bloğunda tanımlanmamışsa ve akış o case'e ulaşırsa, PHP sessiz kalmak yerine bir UnhandledMatchError hatası fırlatır.

PHP:
<?php
enum Weekday: int
{
    case Mon = 1;
    case Tue = 2;
    case Wed = 3;
    case Thu = 4;
    case Fri = 5;
    case Sat = 6;
    case Sun = 7;
}

function kind(Weekday $d): string
{
    return match ($d) {
        Weekday::Sat, Weekday::Sun => 'weekend',
        default => 'workday',
    };
}

foreach ([Weekday::Fri, Weekday::Sat, Weekday::from(1)] as $day) {
    echo $day->name, ': ', kind($day), "\n";
}

$tomorrow = Weekday::tryFrom(Weekday::Sun->value + 1) ?? Weekday::Mon;
echo 'After Sun comes ', $tomorrow->name, "\n";

JSON ve Dizge Olarak Enum Kullanımı

json_encode() fonksiyonu, değerli bir case'i doğrudan kendi değeriyle yazar. Saf enum'lar ise JSON formatına dönüştürülemez. Ayrıca, herhangi bir enum case'ini doğrudan echo ile yazdırmaya çalışmak hataya yol açar; çünkü enum'lar birer dizge değil, nesnedir. Bunun yerine ->value veya ->name özellikleri yazdırılmalıdır.

PHP:
<?php
enum Role: string
{
    case Admin = 'admin';
    case Editor = 'editor';
}

$user = ['name' => 'Ada', 'role' => Role::Admin];
echo json_encode($user), "\n";

$data = json_decode('{"name":"Linus","role":"editor"}', true);
$role = Role::from($data['role']);
var_dump($role);

echo 'Role: ' . $role->value . "\n";
echo 'Name: ' . $role->name . "\n";

echo Role::Admin; komutu, "Object of class Role could not be converted to string" şeklinde bir Error üretir ve enum'lar __toString() metodunu desteklemez. JSON'dan veri geri alındığında düz bir dizge geldiği için, bu veriyi from() veya tryFrom() ile tekrar case'e dönüştürmeniz gerekir.

Sık Yapılan Enum Hataları

Enum kullanımı sırasında genellikle şu üç hata ile karşılaşılır: bir case asla kendi değerine eşit değildir, case'ler dizi anahtarı (array key) olarak kullanılamaz ve enum'lar new anahtar kelimesiyle örneklendirilemez.

PHP:
<?php
enum Status: string
{
    case Active = 'active';
    case Banned = 'banned';
}

// 1. A case is an object, not its value
var_dump(Status::Active == 'active');          // false
var_dump(Status::Active->value === 'active');  // true

// 2. Use ->value as an array key
$counts = [];
foreach ([Status::Active, Status::Banned, Status::Active] as $s) {
    $counts[$s->value] = ($counts[$s->value] ?? 0) + 1;
}
print_r($counts);

try {
    $bad = [Status::Active => 1];
} catch (TypeError $e) {
    echo 'Array key: ', $e->getMessage(), "\n";
}

// 3. Cases are created by PHP, never with new
try {
    $s = new Status();
} catch (Error $e) {
    echo $e->getMessage(), "\n";
}

Eğer case'leri anahtar olarak kullanan bir eşleme yapısına ihtiyaç duyuyorsanız, nesneleri anahtar olarak destekleyen SplObjectStorage veya WeakMap yapılarını kullanabilirsiniz.
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst