Hai mai aperto un progetto e trovato stringhe magiche tipo 'pending', 'shipped', 'delivered' sparse in decine di if? E ogni volta che aggiungi uno stato devi cercare tutti i punti in cui quel valore viene confrontato, sperando di non dimenticarne nessuno? Questo è il problema che gli enum di PHP 8.1 risolvono una volta per tutte. Noi, di Meteora Web, li usiamo in ogni progetto Laravel o WooCommerce custom che tocchiamo. Perché un errore di stato in un ordine o in un abbonamento costa molto più di qualche riga di codice ben scritta.
Come funziona un enum puro in PHP 8.1? Nessun valore nascosto
Un enum puro è un tipo che definisce un insieme chiuso di casi possibili. Non ha valori associati (né interi, né stringhe). Si usa quando l'identità del caso è sufficiente, senza bisogno di rappresentazioni esterne.
<?php
declare(strict_types=1);
enum StatoOrdine
{
case InAttesa;
case InElaborazione;
case Spedito;
case Consegnato;
case Annullato;
}
Ogni caso è un singleton. Li confronti con ===, non con stringhe. Se provi a passare un valore che non esiste — errore a compile-time, non a runtime. Questo taglia la testa al toro degli errori di battitura nei nomi degli stati.
Usare l'enum puro nel tuo codice: esempio concreto
Immagina una funzione che aggiorna lo stato di un ordine. Con le stringhe dovresti scrivere if ($stato === 'spedito'). Con un enum puro scrivi:
Sponsored Protocol
function aggiornaStato(StatoOrdine $nuovoStato): void
{
// logica di validazione del passaggio di stato
if ($nuovoStato === StatoOrdine::Spedito) {
// invia notifica
}
$this->stato = $nuovoStato;
}
Il type hinting ti protegge: nessuno potrà passare una stringa a caso. Se domani aggiungi InReso, l'IDE ti mostra subito dove usarlo. Fine della caccia ai valori magici.
match su un enum dimenticando un caso. PHP 8.1 lancia un UnhandledMatchError a runtime. Meglio usare match (true) con un default esplicito o un switch con default.
Backed enum in PHP 8.1: quando serve un intero o una stringa per il database
La realtà delle PMI italiane è fatta di database esistenti, API di terze parti, valori numerici negli archivi. L'enum puro è bello, ma se devi salvare lo stato in una colonna VARCHAR o INT sul DB, ti serve un backed enum. Associ ogni caso a un valore scalare (stringa o intero).
enum StatoOrdineBacked: string
{
case InAttesa = 'pending';
case InElaborazione = 'processing';
case Spedito = 'shipped';
case Consegnato = 'delivered';
case Annullato = 'cancelled';
}
// Con interi
enum RuoloUtente: int
{
case Admin = 1;
case Editore = 2;
case Abbonato = 3;
}
Dal database al codice con from() e tryFrom()
Quando leggi dal DB, usi StatoOrdineBacked::from($valore) — se il valore non esiste, lancia ValueError. Oppure tryFrom() che restituisce null. Scelta obbligatoria: se i dati sono puliti, from è più rigoroso; se possono arrivare valori sporchi (es. import da vecchio sistema), tryFrom con gestione dell'errore.
Sponsored Protocol
$statoDb = 'shipped'; // dal database
$statoEnum = StatoOrdineBacked::from($statoDb);
echo $statoEnum->name; // 'Spedito'
echo $statoEnum->value; // 'shipped'
Questo elimina la necessità di mappature manuali (array di corrispondenza) e riduce righe di codice nei repository. Noi abbiamo visto progetti con centinaia di chiamate a funzioni di mappatura — con gli enum spariscono tutte.
Nota su performance: backed enum sono leggermente più lenti dei puri per via della conversione, ma la differenza è irrilevante in applicazioni reali. La sicurezza del tipo paga cento volte.Quali metodi si possono aggiungere agli enum di PHP 8.1? Comportamento incapsulato
Gli enum in PHP non sono semplici elenchi — possono avere metodi. Questo permette di incapsulare logiche che altrimenti finirebbero in helper o funzioni statiche sparse.
Sponsored Protocol
enum StatoOrdineBacked: string
{
case InAttesa = 'pending';
case InElaborazione = 'processing';
case Spedito = 'shipped';
case Consegnato = 'delivered';
case Annullato = 'cancelled';
public function etichetta(): string
{
return match($this) {
self::InAttesa => 'In attesa di pagamento',
self::InElaborazione => 'In elaborazione',
self::Spedito => 'Spedito',
self::Consegnato => 'Consegnato',
self::Annullato => 'Annullato',
};
}
public function puoEssereAnnullato(): bool
{
return match($this) {
self::Consegnato, self::Annullato => false,
default => true,
};
}
public function prossimiStati(): array
{
return match($this) {
self::InAttesa => [self::InElaborazione, self::Annullato],
self::InElaborazione => [self::Spedito, self::Annullato],
self::Spedito => [self::Consegnato, self::Annullato],
default => [],
};
}
}
Ora ogni stato conosce il suo comportamento. Nessun if sparso nel controller o nel servizio. Chiami $ordine->stato->etichetta() e ottieni la label tradotta. Chiami $ordine->stato->puoEssereAnnullato() e decidi se mostrare il bottone. Tutto in un posto solo.
Sponsored Protocol
Metodi statici su enum per factory o query
Puoi anche aggiungere metodi statici per creare enum da input complessi o restituire array di casi:
enum RuoloUtente: int
{
case Admin = 1;
case Editore = 2;
case Abbonato = 3;
public static function valoriPerForm(): array
{
return array_combine(
array_map(fn($case) => $case->value, self::cases()),
array_map(fn($case) => $case->name, self::cases())
);
}
}
// Uso: RuoloUtente::valoriPerForm() => [1 => 'Admin', 2 => 'Editore', 3 => 'Abbonato']
Come gestisce un enum la serializzazione rispetto a un array di costanti? Maggiore sicurezza e manutenibilità
Prima di PHP 8.1, molti usavano array di costanti di classe o classi astratte con costanti. Funziona, ma presenta problemi: le costanti non sono tipizzabili (puoi passare un intero qualunque), non c'è autocompletamento avanzato, e il codice è più verboso.
| Caratteristica | Array di costanti | Enum PHP 8.1 |
|---|---|---|
| Type safety | No (int/string) | Sì (tipo specifico) |
| Autocompletamento IDE | Limitato | Completo (cases e metodi) |
| Serializzazione JSON | Manuale | Automatica (per backed enum, restituisce value) |
| Metodi associati | No (servono helper statici) | Sì (interni all'enum) |
La serializzazione JSON di un backed enum restituisce direttamente il valore scalare. Per un puro, devi implementare JsonSerializable. Vuoi personalizzare? Sovrascrivi jsonSerialize():
Sponsored Protocol
enum StatoOrdine: string implements \JsonSerializable
{
case InAttesa = 'pending';
// ...
public function jsonSerialize(): string
{
return $this->value;
}
}
Cosa fare adesso: adotta gli enum nel prossimo progetto
- Identifica i domini chiusi nel tuo codice: stati ordine, ruoli utente, tipi di prodotto, canali di vendita. Ogni insieme finito di valori è candidato.
- Sostituisci gradualmente le costanti e le stringhe magiche con enum. Inizia da un modello (es. StatoOrdine) e verifica che i test passino.
- Sposta la logica nei metodi dell'enum — etichette, permessi, transizioni. Vedrai il codice diventare più dichiarativo e facile da modificare.
- Usa backed enum se devi interfacciarti con database esterni o API. Per logiche puramente interne, va bene il puro.
Noi di Meteora Web abbiamo convertito un sistema legacy di gestione ordini con oltre 30 stati sparsi in un unico enum con metodi. Il codice è passato da 2000 righe di if-else a 300 righe chiare. Il costo di manutenzione è crollato. Ora tocca a te.
Per approfondire l'ecosistema PHP moderno, dai un'occhiata alla nostra guida completa su PHP 8 avanzato.