PHP: Wie verwendet man Enums?

Statuswerte, Prioritäten, Ampelfarben: Immer wieder braucht man eine feste Liste erlaubter Werte. Seit PHP 8.1 gibt es dafür ein eigenes Sprachmittel und man muss sich nicht mehr mit Klassenkonstanten behelfen.

Die einfache Form

Ein Enum ohne Werte nennt sich “pure enum”:

<?php
enum Status {
  case Offen;
  case InArbeit;
  case Erledigt;
}

$s = Status::Offen;
echo $s->name;   // Offen
?>

Jeder Fall ist ein Objekt der Klasse Status. Über cases() kommst du an alle Fälle:

Status::cases() : Offen, InArbeit, Erledigt

Das ist praktisch, um zum Beispiel ein Auswahlfeld zu füllen, ohne die Liste ein zweites Mal zu pflegen.

Die Form mit Werten

Sobald der Wert dein Programm verlässt — Richtung Datenbank, JSON oder URL — brauchst du ein “backed enum”, also eines mit hinterlegten Werten:

<?php
enum Prioritaet: int {
  case Niedrig = 1;
  case Mittel  = 2;
  case Hoch    = 3;
}

echo Prioritaet::Hoch->value;   // 3
?>

Als Typ sind int und string erlaubt. Zurück vom Wert zum Fall kommst du mit from() oder tryFrom():

Prioritaet::from(2)       : Mittel
Prioritaet::tryFrom(99)   : NULL
Prioritaet::from(99)      : ValueError
                            99 is not a valid backing value for enum Prioritaet

Der Unterschied ist wichtig. from() wirft eine Ausnahme, tryFrom() liefert null. Für Werte aus der Datenbank, denen du vertraust, nimmst du from(). Für Benutzereingaben tryFrom():

<?php
  $prio = Prioritaet::tryFrom((int) $_GET['prio']) ?? Prioritaet::Mittel;
?>

Enums können Methoden haben

Das ist der Punkt, an dem Enums deutlich mehr sind als eine Liste von Konstanten:

<?php
enum Ampel: string {
  case Rot   = 'rot';
  case Gelb  = 'gelb';
  case Gruen = 'gruen';

  public function darfFahren(): bool {
    return match($this) {
      Ampel::Gruen => true,
      Ampel::Rot, Ampel::Gelb => false,
    };
  }
}
?>
Rot    value=rot    darfFahren=false
Gelb   value=gelb   darfFahren=false
Gruen  value=gruen  darfFahren=true

Die Logik liegt damit dort, wo die Werte definiert sind, und nicht verstreut in if-Ketten über die halbe Anwendung. Beachte außerdem, dass match hier ohne default auskommt — kommt später ein vierter Fall dazu, meldet PHP das, statt still den falschen Zweig zu nehmen.

Was man gegenüber Klassenkonstanten gewinnt

Vor PHP 8.1 hat man das so gelöst:

<?php
class Status {
  const OFFEN = 'offen';
  const ERLEDIGT = 'erledigt';
}

function verarbeiten(string $status): string {
  return "verarbeite $status";
}
?>

Das Problem steckt in der Signatur. Der Parameter ist am Ende ein string, also kommt jeder beliebige String durch:

verarbeite gibtsnicht    <- kein Fehler, der Tippfehler faellt nicht auf

Mit einem Enum als Typ prüft die Sprache:

<?php
function verarbeiten(Ampel $a): string {
  return "verarbeite {$a->value}";
}
?>
String statt Enum -> TypeError

Genau das ist der Gewinn: Ein ganzer Fehlerfall verschwindet, weil er sich nicht mehr formulieren lässt.

Enums sind Singletons

Jeder Fall existiert genau einmal:

Status::Offen === Status::Offen   =>   true

Deshalb kannst du mit === vergleichen und match($this) funktioniert ohne weiteres Zutun. Du kannst ein Enum übrigens auch nicht mit new erzeugen und nicht klonen.

Die Falle bei JSON

Ein Detail, über das man leicht stolpert. Ein backed enum lässt sich ohne Weiteres als JSON ausgeben, ein pure enum nicht:

json_encode(Ampel::Rot)      : "rot"
json_encode(Status::Offen)   : false
json_last_error_msg()        : Non-backed enums have no default serialization

Wichtig ist der zweite Wert: json_encode() wirft keine Ausnahme, sondern liefert false. Wer den Rückgabewert nicht prüft, schickt eine leere Antwort und sucht den Fehler an der falschen Stelle. Mit dem Flag JSON_THROW_ON_ERROR bekommst du stattdessen eine JsonException.

Die praktische Regel daraus: Sobald ein Wert dein Programm verlässt, nimm ein backed enum.

Zusammenfassung

  • Pure enum für rein interne Zustände, backed enum sobald der Wert nach außen geht.
  • from() wirft, tryFrom() liefert null — für Benutzereingaben tryFrom().
  • cases() liefert alle Fälle, praktisch für Auswahlfelder.
  • Methoden im Enum halten die Logik bei den Werten.
  • Der eigentliche Gewinn ist die Typprüfung, die es bei Konstanten nicht gibt.

Hinweis zu Netcup (Werbung)

Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).