throw
In PHP, la parola chiave "throw" viene usata per generare un'eccezione e gestire errori e condizioni impreviste nel codice.
La parola chiave throw in PHP
La parola chiave throw interrompe l'esecuzione normale nel punto in cui viene chiamata e genera un'eccezione — un oggetto che segnala che qualcosa è andato storto. Il controllo salta immediatamente fuori dalla funzione corrente e risale lo stack delle chiamate finché non trova un blocco catch corrispondente. Se nessun catch corrisponde, PHP interrompe lo script con un errore fatale.
Questa pagina tratta la sintassi di throw, quando usarlo al posto della restituzione di un valore di errore, come lanciare eccezioni predefinite e personalizzate, il rilancio e il concatenamento, e la funzionalità di PHP 8 che permette di usare throw come espressione. Per un quadro più ampio, vedere Eccezioni in PHP e il flusso try/catch/finally.
Sintassi
throw new Exception("Error message here");throw deve ricevere un valore che sia un'istanza di Throwable — in pratica un Exception (o una delle sue sottoclassi) oppure un Error. La stringa passata al costruttore diventa il messaggio dell'eccezione, che si legge in seguito con getMessage().
Poiché un'eccezione lanciata svolge lo stack, qualsiasi codice dopo throw nello stesso blocco non viene mai eseguito:
throw new Exception("stop here");
echo "this line is unreachable"; // never executesPerché usare throw invece di restituire un errore?
Restituire un valore speciale (come false o -1) per segnalare un fallimento costringe ogni chiamante a ricordarsi di verificarlo, e il significato del valore è facile da perdere. throw rende il fallimento impossibile da ignorare: l'eccezione si propaga automaticamente finché qualcosa non la gestisce, e porta con sé un messaggio, un codice, uno stack trace e il file e la riga in cui è avvenuta.
Usare throw per condizioni eccezionali da cui la funzione corrente non può ragionevolmente riprendersi — argomenti non validi, una connessione al database fallita, un file obbligatorio mancante — e lasciare che un chiamante a un livello superiore decida cosa fare.
Un esempio di base
Qui una funzione divide() lancia un'eccezione quando le viene chiesto di dividere per zero, e il chiamante la intercetta:
<?php
function divide(int $numerator, int $denominator): float
{
if ($denominator === 0) {
throw new InvalidArgumentException("Cannot divide by zero.");
}
return $numerator / $denominator;
}
try {
echo divide(10, 2), PHP_EOL; // 5
echo divide(10, 0), PHP_EOL; // throws before printing
} catch (InvalidArgumentException $e) {
echo "Caught: " . $e->getMessage() . PHP_EOL;
}Output:
5
Caught: Cannot divide by zero.La prima chiamata ha successo e stampa 5. La seconda chiamata lancia l'eccezione, quindi il suo echo non viene mai eseguito e il controllo salta direttamente al blocco catch.
Lanciare un'eccezione personalizzata
Estendere Exception permette di dare a ogni tipo di errore il proprio nome, così i chiamanti possono intercettare esattamente i fallimenti che gli interessano e ignorare il resto:
<?php
class InsufficientFundsException extends Exception {}
function withdraw(float $balance, float $amount): float
{
if ($amount > $balance) {
throw new InsufficientFundsException(
"Cannot withdraw $amount; balance is only $balance."
);
}
return $balance - $amount;
}
try {
echo withdraw(100, 250), PHP_EOL;
} catch (InsufficientFundsException $e) {
echo "Declined: " . $e->getMessage() . PHP_EOL;
}Output:
Declined: Cannot withdraw 250; balance is only 100.Vedere Classi di eccezione personalizzate per maggiori informazioni sull'estensione di Exception.
Messaggio, codice e eccezione precedente
Il costruttore di Exception accetta tre argomenti — message, code e un'eccezione previous. Il terzo permette di racchiudere un errore di basso livello in uno più significativo senza perdere la causa originale (questo si chiama concatenamento di eccezioni):
<?php
try {
try {
throw new RuntimeException("Disk read failed", 13);
} catch (RuntimeException $low) {
// Re-throw a higher-level exception, keeping the original as the cause.
throw new Exception("Could not load config", 0, $low);
}
} catch (Exception $e) {
echo $e->getMessage() . PHP_EOL; // Could not load config
echo "Caused by: " . $e->getPrevious()->getMessage() . PHP_EOL; // Disk read failed
echo "Original code: " . $e->getPrevious()->getCode() . PHP_EOL; // 13
}Output:
Could not load config
Caused by: Disk read failed
Original code: 13Rilanciare in un blocco catch
Non è necessario gestire completamente un'eccezione dove la si intercetta. Un blocco catch può eseguire del lavoro (registrarla, aggiungere contesto) e poi lanciare di nuovo con throw per lasciare che un gestore esterno completi il lavoro:
<?php
function loadUser(int $id): array
{
try {
throw new RuntimeException("Database is down");
} catch (RuntimeException $e) {
error_log("loadUser($id) failed: " . $e->getMessage());
throw $e; // pass it on
}
}
try {
loadUser(7);
} catch (RuntimeException $e) {
echo "Handled at top level: " . $e->getMessage() . PHP_EOL;
}Output:
Handled at top level: Database is downthrow come espressione (PHP 8+)
Da PHP 8.0, throw è un'espressione, non solo un'istruzione, quindi può essere usato in contesti che si aspettano un valore — come gli operatori ?: e ?? o una funzione freccia:
<?php
function getConfig(array $config, string $key): string
{
// Throw inline when the key is missing.
return $config[$key] ?? throw new InvalidArgumentException("Missing key: $key");
}
echo getConfig(['env' => 'prod'], 'env'), PHP_EOL; // prod
try {
getConfig(['env' => 'prod'], 'region');
} catch (InvalidArgumentException $e) {
echo $e->getMessage() . PHP_EOL;
}Output:
prod
Missing key: regionErrori comuni
- Lanciare senza un
try/catchda nessuna parte nello stack. Un'eccezione non intercettata diventa un errore fatale e interrompe lo script. Intercettarla sempre da qualche parte, oppure registrare un fallback conset_exception_handler(). - Lanciare una stringa o un array.
throwrichiede un oggetto che implementaThrowable;throw "oops";è un errore di sintassi. - Silenziare le eccezioni. Un blocco
catchvuoto nasconde i bug. Registrare almeno il messaggio, oppure rilanciare l'eccezione. - Usare le eccezioni per il normale flusso di controllo. Lanciare su ogni ramo previsto (ad esempio, "utente non trovato" durante una ricerca ordinaria) è lento e confuso — riservarle ai casi genuinamente eccezionali.
Riepilogo
throwgenera unThrowablee svolge immediatamente lo stack fino alcatchcorrispondente più vicino.- Preferirlo ai valori di ritorno magici in modo che i fallimenti non possano essere ignorati silenziosamente.
- Estendere
Exceptionper creare tipi di errore con nome; passare un'eccezionepreviousper concatenare le cause. - Intercettare, aggiungere contesto e lanciare di nuovo con
throwper lasciare che un gestore esterno decida. - In PHP 8+,
throwfunziona come espressione all'interno di??,?:e funzioni freccia.
Continuare con il blocco try, il blocco catch e finally per vedere il ciclo completo di gestione delle eccezioni.