Las excepciones

Tabla de contenidos

PHP tiene un manejo de excepciones similar al que ofrecen otros lenguajes de programación. Una excepción puede ser lanzada ("throw") y capturada ("catch") en PHP. El código deberá estar rodeado de un bloque try para facilitar la captura de una excepción potencial. Cada try debe tener al menos un bloque catch o finally correspondiente.

Si una excepción es lanzada y el ámbito actual de la función no tiene un bloque catch, la excepción "subirá" la pila de llamadas de la función llamadora hasta encontrar un bloque catch correspondiente. Todos los bloques finally encontrados serán ejecutados. Si la pila de llamadas se desenrolla hasta el ámbito global sin encontrar un bloque catch correspondiente, el programa será terminado con un error fatal a menos que se haya definido un manejador global de excepciones.

El objeto lanzado debe ser una instanceof Throwable. Intentar lanzar un objeto que no lo es resultará en un error fatal emitido por PHP.

A partir de PHP 8.0.0, la palabra clave throw es una expresión y puede ser utilizada en cualquier contexto de expresiones. En versiones anteriores era una declaración que debía estar en su propia línea.

catch

Un bloque catch define cómo reaccionar ante una excepción que ha sido lanzada. Un bloque catch define uno o más tipos de excepciones o errores que puede manejar, y opcionalmente una variable en la que asignar la excepción. (Esta variable era requerida en versiones anteriores a PHP 8.0.0) El primer bloque catch que una excepción o error lanzado encuentre y que corresponda al tipo del objeto lanzado manejará el objeto.

Varios bloques catch pueden ser utilizados para atrapar diferentes clases de excepciones. La ejecución normal (cuando ninguna excepción es lanzada en el bloque try) continúa después del último bloque catch definido en la secuencia. Las excepciones pueden ser lanzadas (throw) o relanzadas en un bloque catch. De lo contrario, la ejecución continuará después del bloque catch que haya sido activado.

Cuando una excepción es lanzada, el código siguiente al tratamiento no será ejecutado y PHP intentará encontrar el primer bloque catch correspondiente. Si una excepción no es capturada, un error fatal de PHP será enviado con un mensaje "Uncaught Exception ..." indicando que la excepción no pudo ser capturada a menos que un manejador de excepciones sea definido con la función set_exception_handler().

A partir de PHP 7.1, un bloque catch puede especificar múltiples excepciones a través del carácter pipe (|). Esto es útil cuando diferentes excepciones de diferentes jerarquías de clases son tratadas de la misma manera.

A partir de PHP 8.0.0, el nombre de variable para la excepción capturada es opcional. Si no se especifica, el bloque catch será siempre ejecutado pero no tendrá acceso al objeto lanzado.

finally

Un bloque finally también puede ser especificado después de bloques catch. El código dentro del bloque finally será siempre ejecutado después de los bloques try y catch, independientemente de si una excepción ha sido lanzada, antes de continuar con la ejecución normal.

Una interacción notable es entre un bloque finally y una declaración return. Si una declaración return es encontrada dentro de los bloques try o catch, el bloque finally será igualmente ejecutado. Además, la declaración return es evaluada cuando es encontrada, pero el resultado será retornado después de que el bloque finally sea ejecutado. Adicionalmente, si el bloque finally contiene también una declaración return el valor del bloque finally es retornado.

Otra interacción notable es entre una excepción lanzada en un bloque try, y una excepción lanzada en un bloque finally. Si una excepción es lanzada en ambos bloques, entonces, la excepción lanzada en el bloque finally será la que se propagará, y la excepción lanzada en el bloque try será utilizada como excepción previa.

Manejador global de excepciones

Si una excepción es permitida para subir hasta el ámbito global, puede ser capturada por un manejador de excepciones global si ha sido definido. La función set_exception_handler() puede definir una función que será llamada en lugar de un bloque catch si ningún otro bloque es invocado. El efecto es esencialmente idéntico a envolver el programa entero en un bloque try-catch con esta función como catch.

Notas

Nota:

Las funciones internas de PHP utilizan principalmente el Error reporting, solo las extensiones orientadas a objetos utilizan excepciones. Sin embargo, los errores pueden ser fácilmente convertidos en excepciones con ErrorException. Sin embargo, esta técnica solo funciona para errores no fatales.

Ejemplo #1 Convertir el error reporting en excepciones

<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new
ErrorException($message, 0, $severity, $filename, $lineno);
}

set_error_handler('exceptions_error_handler');
?>

Sugerencia

La biblioteca estándar PHP (SPL) proporciona un buen número de excepciones adicionales.

Ejemplos

Ejemplo #2 Lanzar una excepción

<?php
function inverse($x) {
if (!
$x) {
throw new
Exception('División por cero.');
}
return
1/$x;
}

try {
echo
inverse(5) . "\n";
echo
inverse(0) . "\n";
} catch (
Exception $e) {
echo
'Excepción recibida: ', $e->getMessage(), "\n";
}

// Continuar la ejecución
echo "¡Hola mundo!\n";
?>

El resultado del ejemplo sería:

0.2
Excepción recibida: División por cero.
¡Hola mundo!

Ejemplo #3 Manejo de la excepción con un bloque finally

<?php
function inverse($x) {
if (!
$x) {
throw new
Exception('División por cero.');
}
return
1/$x;
}

try {
echo
inverse(5) . "\n";
} catch (
Exception $e) {
echo
'Excepción recibida: ', $e->getMessage(), "\n";
} finally {
echo
"Primer fin.\n";
}

try {
echo
inverse(0) . "\n";
} catch (
Exception $e) {
echo
'Excepción recibida: ', $e->getMessage(), "\n";
} finally {
echo
"Segundo fin.\n";
}

// Continuar la ejecución
echo "¡Hola mundo!\n";
?>

El resultado del ejemplo sería:

0.2
Primer fin.
Excepción recibida: División por cero.
Segundo fin.
¡Hola mundo!

Ejemplo #4 Interacción entre el bloque finally y return

<?php

function test() {
try {
throw new
Exception('foo');
} catch (
Exception $e) {
return
'catch';
} finally {
return
'finally';
}
}

echo
test();
?>

El resultado del ejemplo sería:

finally

Ejemplo #5 Herencia de una excepción

<?php

class MyException extends Exception { }

class
Test {
public function
testing() {
try {
try {
throw new
MyException('foo!');
} catch (
MyException $e) {
// se relanza
throw $e;
}
} catch (
Exception $e) {
var_dump($e->getMessage());
}
}
}

$foo = new Test;
$foo->testing();

?>

El resultado del ejemplo sería:

string(4) "foo!"

Ejemplo #6 Manejo de excepciones de captura múltiple

<?php

class MyException extends Exception { }

class
MyOtherException extends Exception { }

class
Test {
public function
testing() {
try {
throw new
MyException();
} catch (
MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}

$foo = new Test;
$foo->testing();

?>

El resultado del ejemplo sería:

string(11) "MyException"

Ejemplo #7 Omitir la variable capturada

Solo permitido en PHP 8.0.0 y versiones posteriores.

<?php

function test() {
throw new
SpecificException('Oopsie');
}

try {
test();
} catch (
SpecificException) {
print
"Se lanzó una SpecificException, pero no nos importa con los detalles.";
}
?>

El resultado del ejemplo sería:

Se lanzó una SpecificException, pero no nos importa con los detalles.

Ejemplo #8 Throw como expresión

Solo permitido en PHP 8.0.0 y versiones posteriores.

<?php

class SpecificException extends Exception {}

function
test() {
do_something_risky() or throw new Exception('No funcionó');
}

function
do_something_risky() {
return
false; // Simular un fallo
}

try {
test();
} catch (
Exception $e) {
print
$e->getMessage();
}
?>

El resultado del ejemplo sería:

No funcionó

Ejemplo #9 Excepción en try y en finally

<?php

try {
try {
throw new
Exception(message: 'Third', previous: new Exception('Fourth'));
} finally {
throw new
Exception(message: 'First', previous: new Exception('Second'));
}
} catch (
Exception $e) {
var_dump(
$e->getMessage(),
$e->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
);
}

El resultado del ejemplo sería:

string(5) "First"
string(6) "Second"
string(5) "Third"
string(6) "Fourth"