Error and Exception Handling in PHP

try / catch / finally, custom exceptions and native errors from PHP 8

🎯 Live Demo

Result of dividirSeguro():

Try values in the form →

Try different values

📖 Code Explanation

Complete code

class NumeroInvalidoException extends Exception {}

function dividirSeguro(float $dividendo, float $divisor): float {
    if ($divisor < 0) {
        throw new NumeroInvalidoException(t('El divisor no puede ser negativo') . " (\$divisor).");
    }
    return $dividendo / $divisor; // PHP 8 lanza DivisionByZeroError si \$divisor es 0}

try {
    $resultado = dividirSeguro($dividendo, $divisor);
    echo t('Resultado:') . " \$resultado";
} catch (DivisionByZeroError $e) {
    echo t('No se puede dividir por cero:') . " " . \$e->getMessage();
} catch (NumeroInvalidoException $e) {
    echo t('Dato inválido:') . " " . \$e->getMessage();
} catch (Throwable $e) {
    echo t('Error inesperado:') . " " . \$e->getMessage();
} finally {
    echo t('Esto se ejecuta siempre.');
}

📝 Custom exception

What does it do? NumeroInvalidoException extends Exception creates your own error type, with a descriptive name, which you can catch independently from the rest.

📝 Order of the catch

Why does it matter? PHP evaluates the catch blocks in order and uses the first one that matches the thrown type.. That's why the more specific ones (DivisionByZeroError, NumeroInvalidoException) go before the generic Throwable.

💡 Concept: Error vs Exception

Since PHP 7, fatal errors (like DivisionByZeroError) are also catchable objects.. Throwable is the interface common to Error and Exception, so a catch (Throwable $e) catches anything that slips past the previous ones.

📝 The block finally

What does it do? It always runs, whether there was an exception or not. Ideal for releasing resources (closing a connection, a file, etc.).

🎯 Key Concepts

try / catch / finally

  • try: code that might fail
  • catch: what to do if it fails
  • finally: always runs

Custom exceptions

  • extends Exception
  • Give the error a name and context

Throwable

  • Common interface of Error and Exception
  • Final safety net in the catch

💪 Practical Exercise

Extend error handling

  1. Create an exception NumeroDemasiadoGrandeException that is thrown if the dividend exceeds 1.000.000.
  2. Add a method getMensajeUsuario() to NumeroInvalidoException that returns a friendly message different from the technical one.
  3. Register a global set_exception_handler() that shows a generic error page for any uncaught exception.

Solution 1

class NumeroDemasiadoGrandeException extends Exception {}

if ($dividendo > 1000000) {
    throw new NumeroDemasiadoGrandeException(t('Dividendo demasiado grande:') . " $dividendo");
}

Solution 2

class NumeroInvalidoException extends Exception {
    public function getMensajeUsuario(): string {
        return t('Revisa los números introducidos, hay algo incorrecto.');
    }
}

Solution 3

set_exception_handler(function (Throwable $e) {
    error_log($e->getMessage());
    echo t('Ha ocurrido un error inesperado. Inténtalo más tarde.');
});