W3docs

try

Scopri come PHP try/catch/finally gestisce le eccezioni. Comprende blocchi catch multipli, tipi union, rilancio e insidie comuni.

La parola chiave try in PHP

La parola chiave try delimita un blocco di codice che potrebbe lanciare un'eccezione — un oggetto che segnala che qualcosa è andato storto e l'esecuzione normale non può continuare. Quando un'eccezione viene lanciata all'interno di un blocco try, PHP smette di eseguire il resto di quel blocco e cerca un blocco catch corrispondente per gestirla. Un blocco finally opzionale viene eseguito successivamente indipendentemente da ciò che è accaduto.

Questa pagina tratta la sintassi di try/catch/finally, come vengono abbinati più blocchi catch, come catturare più tipi di eccezione contemporaneamente, il rilancio delle eccezioni e le insidie più comuni. Se sei nuovo al concetto di lancio di errori, leggi prima Eccezioni in PHP.

Sintassi

try {
  // Code that may throw an exception
} catch (ExceptionType $e) {
  // Code that runs if an exception of ExceptionType (or a subclass) is thrown
} finally {
  // Optional: always runs, whether or not an exception was thrown
}

Un blocco try deve essere seguito da almeno un blocco catch, oppure da un blocco finally, oppure da entrambi — un try da solo è un errore di sintassi. La variabile in catch (ExceptionType $e) contiene l'oggetto eccezione lanciato, che si interroga con metodi come $e->getMessage().

Un primo esempio

La funzione seguente lancia un'eccezione quando le viene chiesto di dividere per zero. Il blocco try la chiama, il blocco catch segnala il problema e finally viene sempre eseguito:

<?php

function divide($dividend, $divisor)
{
  if ($divisor == 0) {
    throw new Exception("Cannot divide by zero.");
  }
  return $dividend / $divisor;
}

try {
  $result = divide(10, 0);
  echo $result;            // skipped — divide() threw before returning
} catch (Exception $e) {
  echo "Error: " . $e->getMessage() . PHP_EOL;
} finally {
  echo "Done." . PHP_EOL;
}

Output:

Error: Cannot divide by zero.
Done.

Si noti che echo $result; non viene mai eseguito: non appena divide(10, 0) lancia l'eccezione, il controllo passa direttamente al blocco catch. Il blocco finally viene eseguito dopo. Se invece si chiama divide(10, 2), non viene lanciata alcuna eccezione, il blocco try stampa 5 e finally stampa comunque Done..

Blocchi catch multipli

Un singolo try può essere seguito da diversi blocchi catch. PHP li controlla dall'alto verso il basso ed esegue il primo il cui tipo corrisponde all'eccezione lanciata (la corrispondenza include le sottoclassi). Elenca i tipi più specifici prima di quelli più generali — un catch (Exception $e) in testa ingloberebbe tutto ciò che segue.

<?php

try {
  $value = "5";
  if (!is_int($value)) {
    throw new TypeError("Expected an integer.");
  }
} catch (TypeError $e) {
  echo "Type problem: " . $e->getMessage() . PHP_EOL;
} catch (Exception $e) {
  echo "Other problem: " . $e->getMessage() . PHP_EOL;
}

Output:

Type problem: Expected an integer.

Catturare più tipi in un unico blocco

Quando due tipi di eccezione devono essere gestiti allo stesso modo, combinali con una barra verticale (|) — funzionalità aggiunta in PHP 7.1 — invece di duplicare il blocco:

<?php

try {
  throw new RuntimeException("Network timed out.");
} catch (RuntimeException | LogicException $e) {
  echo "Handled: " . $e->getMessage() . PHP_EOL;
}

Output:

Handled: Network timed out.

Come si comporta finally

Il blocco finally viene eseguito anche se il blocco try o catch esegue un return. Questo lo rende il posto giusto per le operazioni di pulizia — chiusura di file, rilascio di lock, rollback di una transazione — che devono avvenire in ogni caso:

<?php

function readConfig()
{
  try {
    return "config loaded";
  } finally {
    echo "Cleanup ran." . PHP_EOL;
  }
}

echo readConfig() . PHP_EOL;

Output:

Cleanup ran.
config loaded

Il blocco finally viene eseguito prima che la funzione restituisca effettivamente il suo valore. Evita di usare return dentro finally: un return lì sovrascrive il valore restituito da try/catch e scarta silenziosamente qualsiasi eccezione che stava per essere lanciata.

Rilancio di un'eccezione

Un blocco catch può svolgere un lavoro parziale — registrare l'errore, aggiungere contesto — e poi rilanciare l'eccezione in modo che un chiamante di livello superiore possa decidere cosa fare. Usa throw $e; per rilanciare lo stesso oggetto:

<?php

try {
  try {
    throw new Exception("Disk full.");
  } catch (Exception $e) {
    echo "Logging: " . $e->getMessage() . PHP_EOL;
    throw $e; // hand it to the outer handler
  }
} catch (Exception $e) {
  echo "Outer handler: " . $e->getMessage() . PHP_EOL;
}

Output:

Logging: Disk full.
Outer handler: Disk full.

Insidie comuni

  • try ha bisogno di un compagno. Un blocco try da solo non viene analizzato correttamente — abbinalo ad almeno un catch o un finally.
  • Gli errori non sono tutti eccezioni. Molti avvisi di runtime (una variabile non definita, un fopen() fallito) non vengono lanciati come eccezioni, quindi catch non li intercetterà. Usa set_error_handler() oppure controlla i valori di ritorno per quei casi. Gli oggetti Error fatali (come TypeError) possono essere catturati perché implementano Throwable.
  • Ordina i blocchi catch dal più specifico al più generico. Un catch (Exception $e) generico posto per primo nasconde tutti i blocchi successivi.
  • Non inglobare in silenzio. Un blocco catch vuoto nasconde il fallimento; come minimo registra $e->getMessage() in modo che il problema sia visibile.
  • finally può mascherare le eccezioni. Restituire da finally scarta sia il valore di ritorno di try sia qualsiasi eccezione in corso.

Quando usare try?

Ricorri a try/catch quando un fallimento è eccezionale e il codice chiamante può ragionevolmente reagire ad esso: una connessione al database che si interrompe, un API che restituisce un errore, input utente non valido che vuoi rifiutare in modo pulito. Per il normale flusso di controllo (una chiave è presente in un array? una string è vuota?), usa i normali condizionali — le eccezioni sono per il percorso anomalo, non per la ramificazione quotidiana.

Correlati

Esercizio

Pratica
Quale tag si usa in PHP per iniziare un blocco di codice?
Quale tag si usa in PHP per iniziare un blocco di codice?
Was this page helpful?