Le versioni moderne di PHP hanno introdotto strumenti che modificano profondamente il modo di progettare e scrivere codice. Tra questi, readonly properties, enums e match expression rappresentano tre pilastri fondamentali per un codice più espressivo, sicuro e manutenibile. Questa guida esplora ciascuna feature in dettaglio, mostrando esempi reali, casi d'uso avanzati e come combinarle per creare applicazioni robuste.
Readonly Properties: Immutabilità Dichiarativa
Introdotte in PHP 8.1, le proprietà readonly consentono di dichiarare che una proprietà di una classe può essere assegnata una sola volta, tipicamente nel costruttore. Dopo l'inizializzazione, qualsiasi tentativo di modifica solleva un'eccezione Error. Questo meccanismo rafforza l'immutabilità a livello di singola proprietà, senza dover scrivere getter manuali o logiche di blocco.
Sintassi e Comportamento
class User {
public readonly string $name;
public readonly int $age;
public function __construct(string $name, int $age) {
$this->name = $name;
$this->age = $age;
}
}
$user = new User('Alice', 30);
// $user->name = 'Bob'; // Error: Cannot modify readonly property User::$name
Le readonly property possono essere dichiarate con tipo (inclusi union e intersection types) e possono essere type-promoted nel costruttore, rendendo il codice ancora più conciso:
class Product {
public function __construct(
public readonly string $sku,
public readonly float $price,
) {}
}
Non è consentito dichiarare una proprietà readonly senza tipo, né utilizzare static o abstract su di essa. Inoltre, non è possibile definire un valore di default che non sia una costante; la proprietà deve essere inizializzata nel costruttore o tramite una chiamata al costruttore stesso.
Vantaggi e Casi d'Uso
Le readonly property sono ideali per Value Object, DTO (Data Transfer Object), configurazioni immutabili e qualsiasi entità che non debba cambiare dopo la creazione. Eliminano la necessità di getter (se non si vuole incapsulamento aggiuntivo) e prevengono errori dovuti a modifiche accidentali. Insieme agli enums e alla match expression, permettono di scrivere codice dichiarativo e prevedibile.
Enums: Un Tipo di Dati Discreto e Sicuro
Gli enum sono stati introdotti in PHP 8.1 come un vero e proprio tipo di dato. Permettono di definire un insieme finito di valori possibili, migliorando la leggibilità e la sicurezza del codice rispetto all'uso di costanti o interi arbitrari.
Enum Puri e Backed Enum
Un enum puro non ha un valore associato:
enum Status {
case Pending;
case Approved;
case Rejected;
}
Un backed enum associa a ogni caso un valore scalare (int o string):
enum OrderStatus: string {
case New = 'new';
case Processing = 'processing';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
}
Gli enums possono implementare interfacce, avere metodi propri (anche statici) e supportare il pattern matching attraverso la match expression o il nuovo enum con match.
Metodi e Interfacce negli Enums
enum Color: int {
case Red = 1;
case Green = 2;
case Blue = 3;
public function hexCode(): string {
return match($this) {
Color::Red => '#FF0000',
Color::Green => '#00FF00',
Color::Blue => '#0000FF',
};
}
}
Questa integrazione rende gli enums molto più potenti delle semplici costanti: ogni caso è un oggetto con comportamento.
Match Expression: Switch Migliorato e Espressivo
Introdotta in PHP 8.0, la match expression è un costrutto che unisce le capacità di switch con la sicurezza di un confronto stretto (===) e la possibilità di restituire un valore. A differenza dello switch, la match expression è un'espressione che può essere assegnata.
Sintassi Fondamentale
$result = match($value) {
1 => 'Uno',
2, 3 => 'Due o tre',
default => 'Altro',
};
Ogni braccio è separato da una virgola. Non c'è break; il primo braccio corrispondente viene eseguito e la sua espressione restituita. Se nessun braccio corrisponde e non esiste un default, viene lanciata un'eccezione UnhandledMatchError.
Vantaggi rispetto a Switch
- Confronto stretto (===) invece di ==, prevenendo errori di tipo.
- Restituisce un valore, permettendo codice più conciso e funzionale.
- Supporta pattern multipli (es.
2, 3 => ...). - Nessun fall-through: ogni braccio è autonomo.
La match expression è particolarmente potente quando combinata con enums e readonly properties, creando flussi di controllo chiari e immutabili.
Combinare le Tre Feature: Esempi Pratici
DTO Immutabile con Enum e Match
enum OrderAction: string {
case Create = 'create';
case Update = 'update';
case Delete = 'delete';
}
readonly class OrderDTO {
public function __construct(
public string $orderId,
public OrderAction $action,
public array $data = [],
) {}
}
function processOrder(OrderDTO $dto): string {
return match($dto->action) {
OrderAction::Create => createOrder($dto->data),
OrderAction::Update => updateOrder($dto->orderId, $dto->data),
OrderAction::Delete => deleteOrder($dto->orderId),
};
}
In questo esempio, OrderDTO è immutabile grazie alle readonly property; l'azione viene rappresentata da un enum, e la logica di routing è gestita dalla match expression. Il risultato è codice a prova di errore e facile da estendere.
State Machine Semplice
enum PaymentStatus: string {
case Pending = 'pending';
case Completed = 'completed';
case Failed = 'failed';
case Refunded = 'refunded';
public function canTransitionTo(self $newStatus): bool {
return match($this) {
self::Pending => in_array($newStatus, [self::Completed, self::Failed], true),
self::Completed => $newStatus === self::Refunded,
self::Failed, self::Refunded => false,
};
}
}
Le transizioni di stato sono espresse con match, e l'immutabilità delle proprietà readonly garantisce che lo stato non venga alterato se non attraverso un processo controllato.
Best Practice e Avvertenze
Le readonly property non possono essere utilizzate con classi che richiedono lazy loading o riassegnazioni frequenti. Sono invece eccellenti per DTO, configurazioni e value object.
Gli enums, pur essendo tipi forti, non supportano l'ereditarietà (non si può estendere un enum). Usare interfacce per condividere comportamenti comuni.
La match expression va preferita allo switch per operazioni che coinvolgono valori discreti (enums, stringhe prevedibili). Tuttavia, per condizioni booleane complesse o range, un blocco if/elseif rimane più leggibile.
Infine, l'abbinamento di readonly, enums e match porta a un codice dichiarativo e funzionale, riducendo gli effetti collaterali e migliorando la testabilità.
Conclusione e Prossimi Passi
Readonly properties, enums e match expression non sono semplici aggiunte sintattiche: rappresentano un cambio di paradigma verso uno stile di programmazione più sicuro ed espressivo. Integrandole nel proprio flusso di lavoro, si ottiene codice più facile da ragionare, mantenere e testare.
Per approfondire, consulta la documentazione ufficiale PHP sulle readonly properties e la guida agli enum. Per un confronto con le novità di PHP 8.3 e 8.4, leggi il nostro articolo dedicato: Novità PHP 8.3 e 8.4: Typed Class Constants, Property Hooks e json_validate.
Sponsored Protocol