Enums in PHP 8.1: casi d'uso pratici
Pure vs Backed enums, cases(), from(), tryFrom(), interface su enum
Prima di PHP 8.1, un «insieme chiuso di valori possibili», lo stato di un ordine, il seme di una carta, si scriveva con costanti di classe o stringhe libere, senza nessuna garanzia che qualcuno non passasse per sbaglio un valore mai previsto. Gli enum risolvono esattamente questo: un tipo con un numero finito e noto di valori validi.
Pensiamo alla differenza tra un modulo con un campo di testo libero «scrivi il colore» dove qualcuno può scrivere «celestino» o «blaugh» per errore e un modulo con un menu a tendina che elenca solo i colori realmente disponibili. Con l'enum, il valore sbagliato non può nemmeno essere scritto.
1. Pure enum vs Backed enum
Il manuale PHP distingue due varianti:
- Pure Enum ha casi senza un valore scalare associato ogni caso è semplicemente un oggetto singleton con un nome;
- Backed Enum ha invece ogni caso «sostenuto» da un valore scalare esplicito (int o string, mai un misto dei due).
// Pure enum: nessun valore scalare
enum Direzione
{
case Nord;
case Sud;
case Est;
case Ovest;
}
// Backed enum: ogni caso ha un valore string esplicito
enum StatoOrdine: string
{
case InAttesa = 'in_attesa';
case Spedito = 'spedito';
case Consegnato = 'consegnato';
}
Il manuale PHP è specifico: non esistono valori auto-generati (niente interi sequenziali automatici), ogni valore di un backed enum va scritto esplicitamente, e dev'essere unico.
2. cases(): l'elenco completo dei valori possibili
Ogni enum (pure o backed) espone il metodo statico cases(), che restituisce un array con tutti i casi definiti: utile per popolare un menu a tendina, validare un input, o iterare senza doverli elencare a mano.
foreach (StatoOrdine::cases() as $stato) {
echo $stato->name . ' => ' . $stato->value . "\n";
}
// InAttesa => in_attesa
// Spedito => spedito
// Consegnato => consegnato
Ogni caso ha sempre una proprietà name (il nome del caso, come scritto nel codice) e, se backed, anche value (il valore scalare associato).
3. from() e tryFrom(): dal valore grezzo al caso
Un Backed Enum implementa l'interfaccia interna BackedEnum, che aggiunge due metodi statici per andare dal valore grezzo al caso, l'operazione inversa rispetto a ->value.
- from() converte il valore in un caso; se non esiste, lancia un ValueError. Indicato quando il valore è già fidato e un dato mancante è un errore grave.
- tryFrom() stesso lavoro, ma restituisce null invece di lanciare un errore indicato quando il valore arriva da input non fidato (form, API esterna) e vuoi gestire tu il caso mancante.
$stato = StatoOrdine::from('spedito');
// StatoOrdine::Spedito
$stato = StatoOrdine::from('sbagliato');
// ValueError!
$stato = StatoOrdine::tryFrom('sbagliato');
// null, nessun errore
$stato = StatoOrdine::tryFrom($input) ?? StatoOrdine::InAttesa;
// fallback
Nota tecnica dal manuale: definire manualmente un metodo from() o tryFrom() su un Backed Enum genera un errore fatale perchè sono riservati.
4. Interface su enum: contratti anche per un insieme di valori
Gli enum possono implementare interfacce e contenere metodi propri, esattamente come una classe. Se un enum implementa un'interfaccia, tutti i suoi casi soddisfano automaticamente quel contratto: utile per passare un caso enum ovunque ci si aspetti quel tipo.
interface Colorabile
{
public function colore(): string;
}
enum Seme implements Colorabile
{
case Cuori;
case Quadri;
case Fiori;
case Picche;
public function colore(): string
{
return match ($this) {
self::Cuori, self::Quadri => 'Rosso',
self::Fiori, self::Picche => 'Nero',
};
}
}
function dipingi(Colorabile $c)
{
echo $c->colore();
}
dipingi(Seme::Fiori);
// 'Nero' -- funziona, Seme implementa Colorabile
Nella vita reale: è come dare a ogni valore possibile del menu a tendina anche un comportamento coerente: non solo «esiste il seme Fiori», ma «il seme Fiori sa dirti da solo di che colore è», seguendo la stessa regola valida per tutti gli altri semi.
In sintesi
| Elemento | Cosa fa | Esempio |
|---|---|---|
Pure enum |
Casi senza valore scalare, solo nome | enum Direzione { case Nord; } |
Backed enum |
Ogni caso ha un int o string esplicito | enum Stato: string { case A = 'a'; } |
cases() |
Elenca tutti i casi possibili | StatoOrdine::cases() |
from() / tryFrom() |
Dal valore grezzo al caso: errore o null | StatoOrdine::tryFrom($v) |
implements |
Contratto valido per tutti i casi dell'enum | enum Seme implements Colorabile |
Gli enum non sono solo «costanti con un nome migliore»: sono un tipo a tutti gli effetti, che il motore PHP fa rispettare al posto tuo: lo stesso principio delle type declaration viste in precedenza, applicato a un insieme chiuso di valori.
Per approfondire
- match: il switch che avresti sempre voluto avere: match ed enum sono la coppia ideale per gestire un insieme chiuso di casi.
Commenti (0)
Registrati per commentare, fare domande e proporre l'argomento della prossima pillola (oppure accedi se hai già un account).
Nessun commento. Sii il primo a scrivere qualcosa.