---
title: 'PHP 8.1: Enums, Fibers, Readonly & Intersection Types — PHP Upgrade Guide | DevSense'
description: 'PHP 8.1 dopo PHP 8.0: enums, proprietà readonly, fibers, tipi intersezione/never, first-class callables—oltre alle regole su $GLOBALS, eccezioni MySQLi e deprecazioni.'
faq:
    - { question: 'Cosa sono le Enum in PHP 8.1 e come si usano?', answer: "Le Enum sono un modo type-safe per definire un insieme chiuso di possibili valori. Le backed enum mappano i casi a valori string o int per la serializzazione, mentre le pure enum servono solo per l'identità e rappresentano stati astratti." }
    - { question: 'Quali sono le regole per le proprietà Readonly in PHP 8.1?', answer: 'Le proprietà readonly devono essere tipizzate, non possono avere valori predefiniti e possono essere inizializzate una sola volta (solitamente nel costruttore). I tentativi successivi di modifica lanceranno un Fatal Error.' }
    - { question: 'Come funziona la sintassi first-class callable in PHP 8.1?', answer: 'Consente di creare una Closure da qualsiasi funzione o metodo richiamabile utilizzando i tre punti `...` (es. `strlen(...)`), sostituendo la sintassi precedente e più verbosa `Closure::fromCallable()`.' }
    - { question: 'A cosa serve il tipo di ritorno never?', answer: "Il tipo `never` indica che una funzione non restituirà mai un valore. Ciò significa che deve chiamare `exit()`, lanciare un'eccezione o entrare in un ciclo infinito, consentendo una migliore analisi statica." }
published: '2026-05-31'
---
# PHP 8.1: Funzionalità principali

Percorso di aggiornamento da PHP 8.0.x: aggiunte al linguaggio, restrizioni a runtime e checklist di migrazione per l'ambiente di staging.

## Indice
* [Enum (Enumerazioni)](#enums)
* [Proprietà Readonly](#readonly-properties)
* [Sintassi First-class callable](#first-class-callable)
* [Fibers](#fibers)
* [Tipi di intersezione](#intersection-types)
* [Tipo di ritorno never](#never-type)
* [new nei valori di inizializzazione](#new-in-initializers)
* [Aggiustamenti sintattici e del linguaggio (0o, unpacking di array, argomenti con nome dopo unpacking)](#syntax-tweaks)
* [Modifiche non retrocompatibili (note di migrazione)](#backward-incompatible)
* [Deprecazioni (cosa correggere presto)](#deprecations)
* [Estensioni: modifiche importanti](#extensions-changes)
* [Errori comuni](#common-mistakes)
* [Quiz di autovalutazione](#self-test-quiz)

---

Se PHP 8.0 è stato un ripristino fondamentale del motore di runtime, PHP 8.1 si concentra sul perfezionamento della semantica del linguaggio e sulla fornitura di costrutti sintattici moderni. Introducendo Enum native, proprietà di sola lettura (readonly) e tipi di intersezione direttamente nel sistema dei tipi, PHP 8.1 consente agli sviluppatori di scrivere codice dichiarativo e sicuro rispetto ai tipi con una quantità notevolmente inferiore di codice boilerplate. Tuttavia, questa versione restringe anche alcune flessibilità a runtime—limitando le operazioni di scrittura su `$GLOBALS`, forzando MySQLi a lanciare eccezioni per impostazione predefinita e contrassegnando come deprecati decine di comportamenti legacy. Ecco una guida testata sul campo per comprendere queste funzionalità e gestire con successo l'aggiornamento.

<a id="enums"></a>
## Enum (Enumerazioni)

Le Enum offrono un modo nativo e sicuro rispetto ai tipi per rappresentare un insieme chiuso di valori.

* **Backed enum**: memorizzano un valore scalare (`string|int`) che può essere facilmente serializzato o trasmesso.
* **Pure enum**: sono basate solo sull'identità, ideali per macchine a stati di dominio e controlli esaustivi.

> [!NOTE]
> **Lo sapevi?**
> Le Enum sono implementate internamente come classi! È possibile implementare interfacce, definire metodi, scrivere metodi statici e utilizzare trait (con il limite che i trait non possono contenere proprietà) sulle Enum proprio come per le normali classi.

```php
// app/Enums/Status.php
enum Status: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}

// app/Services/PostService.php
function canEdit(Status $status): bool
{
    return $status !== Status::Archived;
}

$status = Status::from('draft');  // Status::Draft
$value  = $status->value;         // 'draft'
```

### Indicazioni pratiche

* Utilizza le enum al posto di flag gestiti come stringhe generiche (`'draft'|'published'`) nelle firme dei metodi e nei DTO.
* Preferisci le backed enum per la persistenza e le API; preferisci le pure enum per gli stati dei workflow interni.
* Combinale con `match` per una gestione esaustiva di tutti i casi:

```php
// app/Services/PostService.php
function label(Status $status): string
{
    return match ($status) {
        Status::Draft => 'Draft',
        Status::Published => 'Published',
        Status::Archived => 'Archived',
    };
}
```

---

<a id="readonly-properties"></a>
## Proprietà Readonly

Le proprietà in sola lettura (readonly) possono essere assegnate **una sola volta**, solitamente nel costruttore. Questa è una grande vittoria per DTO immutabili, value object e modelli di dominio più sicuri.

> [!NOTE]
> **Lo sapevi?**
> Una proprietà readonly non può avere un valore predefinito! Se provi a scrivere `public readonly string $status = 'draft';`, PHP genererà un errore fatale in fase di compilazione.

```php
// app/DTO/UserProfile.php
final class UserProfile
{
    public function __construct(
        public readonly int $id,
        public readonly string $email,
    ) {}
}
```

### Impatto sull'architettura

* Incoraggia l'uso di oggetti basati sul principio \"costruisci completamente, poi non mutare\".
* Riduce la necessità di definire proprietà private affiancate da getter per molte strutture di tipo DTO.
* Si integra perfettamente con la promozione delle proprietà nel costruttore per tipi immutabili concisi.

---

<a id="first-class-callable"></a>
## Sintassi First-class callable

PHP 8.1 aggiunge un modo pratico per creare closure da elementi richiamabili (callable):

```php
// app/Helpers/StringHelper.php
function normalize(string $s): string
{
    return strtolower(trim($s));
}

// app/Services/DataService.php
$fn = normalize(...);               // Closure
$out = array_map($fn, [' A ', 'B ']); // ['a', 'b']
```

Questo costrutto equivale a `Closure::fromCallable('normalize')`, ma è più breve e si legge meglio all'interno delle pipeline.

---

<a id="fibers"></a>
## Fibers

Le Fiber sono primitive di basso livello per la **concorrenza cooperativa**. Non rendono magicamente asincrono il tuo codice, ma consentono a framework e librerie di creare runtime asincroni, scheduler e modelli di concorrenza strutturata.

```php
// app/Services/FiberService.php
$fiber = new Fiber(function (): void {
    $value = Fiber::suspend('in pausa');
    echo "ripreso con: {$value}\n";
});

echo $fiber->start() . "\n"; // in pausa
$fiber->resume('payload');   // ripreso con: payload
```

### Quando interessarsene

* Se utilizzi un framework asincrono o un event loop (ReactPHP, Amp): le Fiber semplificheranno notevolmente l'integrazione.
* Per la maggior parte delle classiche applicazioni basate su richiesta/risposta: le Fiber rappresentano per lo più \"infrastruttura destinata alle librerie\".

---

<a id="intersection-types"></a>
## Tipi di intersezione

I tipi di intersezione esprimono il concetto che un valore \"deve soddisfare tutte le interfacce/classi indicate\".

```php
// app/Services/ExporterService.php
function export(iterable&Countable $items): array
{
    if (count($items) === 0) {
        return [];
    }

    return iterator_to_array($items, preserve_keys: true);
}
```

Nota: i tipi di intersezione non possono essere combinati con i tipi di unione in PHP 8.1 (i tipi DNF sono stati introdotti successivamente in PHP 8.2).

---

<a id="never-type"></a>
## Tipo de ritorno never

Il tipo di ritorno `never` indica che una funzione **non ritorna**: lancia sempre un'eccezione o termina l'esecuzione.

```php
// app/Helpers/ErrorHelper.php
function fail(string $message): never
{
    throw new RuntimeException($message);
}
```

Questo migliora l'analisi statica e rende esplicito il flusso di controllo.

---

<a id="new-in-initializers"></a>
## new nei valori di inizializzazione

PHP 8.1 consente l'uso di espressioni `new` in più punti: valori predefiniti dei parametri, variabili statiche, inizializzatori di costanti e argomenti degli attributi.

```php
// app/Services/Clock.php
final class Clock
{
    public function now(): DateTimeImmutable
    {
        return new DateTimeImmutable('now');
    }
}

// app/Services/HandlerService.php
function handler(Clock $clock = new Clock()): DateTimeImmutable
{
    return $clock->now();
}
```

Questo elimina molto codice boilerplate per le factory nei casi più semplici. (Fai attenzione al ciclo di vita dei servizi se utilizzi un container DI.)

---

<a id="syntax-tweaks"></a>
## Aggiustamenti sintattici e del linguaggio

### Prefisso per i letterali interi ottali: `0o` / `0O`

```php
// app/Config/FileConfig.php
$mask = 0o755;
```

### Unpacking di array con chiavi stringa

```php
// app/Services/ArrayService.php
$a = [1, 'a' => 'b'];
$b = [...$a, 'c' => 'd']; // [1, 'a' => 'b', 'c' => 'd']
```

### Argomenti con nome dopo l'unpacking degli argomenti

```php
// app/Services/ArgumentService.php
foo(...$args, named: $arg);
```

### `full_path` per il caricamento di file

Le voci in `$_FILES` includono ora una chiave `full_path` (pensata per i caricamenti tramite `webkitdirectory`).

---

<a id="backward-incompatible"></a>
## Modifiche non retrocompatibili (note di migrazione)

Questa sezione si concentra sui problemi che comunemente causano malfunzionamenti nelle applicazioni durante il processo di aggiornamento.

### Core PHP / linguaggio

* **Restrizioni di scrittura su $GLOBALS**: la sovrascrittura dell'intero array `$GLOBALS` non è più supportata (es. `array_pop($GLOBALS)` genera un errore). L'accesso a singoli elementi come `$GLOBALS['x']` rimane funzionante.
* **Variabili statiche nei metodi ereditati**: i metodi ereditati (e non sovrascritti) ora condividono le variabili statiche con il metodo padre.
* **Parametri opzionali prima di quelli richiesti**: i parametri opzionali dichiarati prima di quelli richiesti vengono trattati come richiesti; in PHP 8.1 questo può causare un `ArgumentCountError` al momento della chiamata (anche con argomenti con nome).
* **Compatibilità dei tipi di ritorno con le classi interne**: la sovrascrittura di metodi interni senza tipi di ritorno compatibili genera avvisi di deprecazione; utilizza l'attributo `#[ReturnTypeWillChange]` dove necessario per il supporto multiversione.
* **Nuove parole chiave**:
  * `readonly` è ora una parola chiave (ancora consentita come nome di funzione).
  * `never` è ora una parola riservata (non utilizzabile per nomi di classi/interfacce/trait o nei namespace).

### Migrazione da risorse a oggetti

Diverse estensioni hanno migrato le tradizionali risorse in oggetti (aggiorna i controlli basati su `is_resource()` verificando l'istanza dell'oggetto o il valore false):

* **FileInfo**: utilizza oggetti `finfo` anziché risorse.
* **FTP**: utilizza oggetti `FTP\Connection`.
* **IMAP**: utilizza oggetti `IMAP\Connection`.
* **LDAP**: utilizza oggetti `LDAP\Connection`, `LDAP\Result`, `LDAP\ResultEntry`.
* **PgSQL**: utilizza oggetti `PgSql\Connection`, `PgSql\Result`, `PgSql\Lob`.
* **PSpell**: utilizza oggetti `PSpell\Dictionary` e `PSpell\Config`.

### Modifiche al comportamento di PDO / MySQLi

* **MySQLi**: la modalità di notifica degli errori predefinita è passata da \"silent\" a \"exceptions\". Per ripristinare il vecchio comportamento: `mysqli_report(MYSQLI_REPORT_OFF);`
* **PDO**: il comportamento di `PDO::ATTR_STRINGIFY_FETCHES` è cambiato per i booleani; i driver SQLite/MySQL ora restituiscono interi/float come tipi nativi PHP in modo più coerente.

### Modifiche al comportamento della libreria standard

* `version_compare()` non accetta più abbreviazioni di operatori non documentate.
* Le funzioni di escape HTML ora utilizzano come impostazione predefinita `ENT_QUOTES | ENT_SUBSTITUTE` (le virgolette vengono convertite in entità; i caratteri UTF-8 non validi vengono sostituiti).

---

<a id="deprecations"></a>
## Deprecazioni (cosa correggere presto)

Le deprecazioni in PHP 8.1 diventeranno errori nelle versioni successive. Correggerle tempestivamente faciliterà il passaggio a PHP 8.2+.

### Deprecazioni del core

* Il passaggio di valori `null` a parametri non nullable di funzioni integrate è deprecato.
* Le conversioni implicite da float a int che comportano perdita di precisione sono deprecate (es. per chiavi di array, dichiarazioni di tipo o operatori di tipo intero).
* La chiamata di un elemento statico su un trait è deprecata.
* L'autovivificazione a partire da un valore `false` è deprecata (`$x = false; $x[] = 1;`).

### Deprecazioni importanti nelle estensioni

* **Date**: le funzioni `strftime()` / `gmstrftime()` e `strptime()` sono deprecate (utilizza `date()` / `IntlDateFormatter`).
* **Filter**: le opzioni `FILTER_SANITIZE_STRING` e `FILTER_SANITIZE_STRIPPED` sono deprecate.
* **Hash**: le funzioni `mhash*()` sono deprecate (utilizza `hash_*()`).
* **PDO**: l'opzione `PDO::FETCH_SERIALIZE` è deprecata.

---

<a id="common-mistakes"></a>
## ⚠️ Errori comuni

Ecco alcuni dei problemi più diffusi riscontrati nell'uso delle novità di PHP 8.1:

### 1. Modifica di proprietà Readonly internamente o nelle sottoclassi
Una proprietà `readonly` non può essere modificata, nemmeno dall'interno della classe o da una sua sottoclasse, una volta inizializzata.

```php
// app/DTO/UserProfile.php
// ERRATO: la modifica di una proprietà readonly dopo l'inizializzazione genera un Error
class UserProfile {
    public function __construct(public readonly string $email) {}

    public function updateEmail(string $newEmail): void {
        $this->email = $newEmail; // Fatal Error: Cannot modify readonly property
    }
}
```

### 2. Autovivificazione da False
In PHP 8.1, il tentativo di creare automaticamente un array a partire da una variabile booleana impostata su `false` è deprecato.

```php
// app/Services/ArrayService.php
// ERRATO: genera un avviso di deprecazione in PHP 8.1 (e un errore fatale in PHP 8.2)
$data = false;
$data[] = 'value';

// CORRETTO: inizializza prima la variabile come array vuoto
$data = [];
$data[] = 'value';
```

### 3. Riassegnazione dell'array $GLOBALS
È possibile modificare le singole chiavi all'interno di `$GLOBALS`, ma non è consentito riassegnare o distruggere la variabile `$GLOBALS` stessa.

```php
// app/Services/LegacyService.php
// ERRATO: Errore fatale in PHP 8.1
$GLOBALS = []; 

// ERRATO: il rilegame tramite riferimento non è consentito
$GLOBALS = &$someOtherArray;
```

---

<a id="self-test-quiz"></a>
## Quiz di autovalutazione

Metti alla prova la tua comprensione delle novità di PHP 8.1.

### Domanda 1: Quale delle seguenti affermazioni è vera in merito alle proprietà Readonly in PHP 8.1?
* A) Possono essere riassegnate purché il nuovo valore sia dello stesso tipo.
* B) Possono essere dichiarate solo su proprietà tipizzate (non su proprietà senza tipo).
* C) È possibile assegnare loro un valore predefinito nella definizione della classe.

<details>
<summary>Clicca per vedere la risposta</summary>

**Risposta: B**  
In PHP 8.1, `readonly` può essere applicato solo alle proprietà tipizzate. Dichiarare una proprietà readonly senza tipo genera un errore di sintassi. Inoltre, non possono avere valori predefiniti e non possono mai essere riassegnate dopo l'inizializzazione.
</details>

### Domanda 2: Cosa succede in PHP 8.1 se tenti di compilare automaticamente un array a partire da una variabile che attualmente è impostata su `false` (es. `$x = false; $x[] = 1;`)?
* A) L'operazione funziona correttamente, convertendo la variabile in un array.
* B) Viene generato un avviso di deprecazione.
* C) Viene lanciato immediatamente un errore fatale (Fatal Error).

<details>
<summary>Clicca per vedere la risposta</summary>

**Risposta: B**  
In PHP 8.1, l'autovivificazione a partire da `false` è deprecata e genera un warning. In PHP 8.2, questo avviso viene convertito in un errore fatale (Fatal Error).
</details>

---

## Riassunto

Se PHP 8.0 è stato un ripristino tecnico, PHP 8.1 ne rifinisce la semantica e introduce costrutti moderni. Grazie a Enum, proprietà Readonly e tipi di intersezione integrati direttamente nel sistema dei tipi, potrai scrivere codice più pulito, più sicuro rispetto ai tipi e con una quantità notevolmente inferiore di codice boilerplate.