back-end php Intermediate 4 min

Enums in PHP 8.1: casi d'uso pratici

Pure vs Backed enums, cases(), from(), tryFrom(), interface su enum

· 745 parole

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.

Fonte: PHP Manual: «Backed enumerations»: un Backed Enum può essere sostenuto solo da int o string, mai un'unione dei due; nessun valore scalare è generato automaticamente.

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.

Fonte: PHP Manual: «Enumerations»: un enum può implementare un'interfaccia, e ogni caso soddisfa automaticamente quel contratto di tipo.

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

Accedi o registrati per votare, salvare o commentare la pillola

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.

Pillole correlate